From cf88b086c04feaa01424477c5f0d4829f3d2576f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 12:23:57 +0100 Subject: [PATCH 0001/1472] Add initial project files --- README.rst | 0 lib/__init__.py | 0 lib/colors.py | 54 ++++++++ lib/core.py | 236 ++++++++++++++++++++++++++++++++++ lib/renderers/__init__.py | 8 ++ lib/renderers/terminal.py | 48 +++++++ powerline-terminal-example.py | 18 +++ 7 files changed, 364 insertions(+) create mode 100644 README.rst create mode 100644 lib/__init__.py create mode 100644 lib/colors.py create mode 100644 lib/core.py create mode 100644 lib/renderers/__init__.py create mode 100644 lib/renderers/terminal.py create mode 100755 powerline-terminal-example.py diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..e69de29b diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/colors.py b/lib/colors.py new file mode 100644 index 00000000..fb128762 --- /dev/null +++ b/lib/colors.py @@ -0,0 +1,54 @@ +def cterm_to_hex(cterm_color): + '''Translate a cterm color index into the corresponding hex/RGB color. + ''' + color_dict = { + 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, + 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, + 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, + 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, + 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, + 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, + 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, + 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, + 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, + 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, + 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, + 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, + 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, + 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, + 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, + 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, + 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, + 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, + 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, + 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, + 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, + 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, + 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, + 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, + 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, + 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, + 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, + 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, + 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, + 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, + 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, + 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, + 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, + 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, + 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, + 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, + 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, + 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, + 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, + 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, + } + if not cterm_color: + return None + + try: + return color_dict[cterm_color] + except KeyError: + import sys + sys.stderr.write('Invalid cterm color index: {0}\n'.format(cterm_color)) + return None diff --git a/lib/core.py b/lib/core.py new file mode 100644 index 00000000..a942311e --- /dev/null +++ b/lib/core.py @@ -0,0 +1,236 @@ +class Segment: + '''Powerline segment renderer. + + Powerline segments are initially structured as a tree of segments and sub + segments. This is to give the segments a sense of grouping and "scope", to + avoid having to define all the properties (fg, bg, etc.) for every single + segment. By grouping you can e.g. provide a common background color for + several segments. + + Usage example: + + from lib.core import Segment + from lib.renderers import TerminalSegmentRenderer + + powerline = Segment([ + Segment('First segment'), + Segment([ + Segment('Grouped segment 1'), + Segment('Grouped segment 2'), + ]), + ]) + + print(powerline.render(TerminalSegmentRenderer)) + ''' + separators = { + 'l': { + 'hard': '⮀', + 'soft': '⮁', + }, + 'r': { + 'hard': '⮂', + 'soft': '⮃', + }, + } + + ATTR_BOLD = 1 + ATTR_ITALIC = 2 + ATTR_UNDERLINE = 4 + + def __init__(self, content='', fg=None, bg=None, attr=None, side=None, padding=None, separate=None): + '''Create a new segment. + + No arguments are required when creating new segments, as + empty/colorless segments can be used e.g. as left/right separators. + ''' + self.content = content + self.parent = None + self.prev = None + self.next = None + + try: + if len(fg) == 2: + self._fg = fg + except TypeError: + # Only the terminal color is defined, so we need to get the hex color + from lib.colors import cterm_to_hex + self._fg = [fg, cterm_to_hex(fg)] + + try: + if len(bg) == 2: + self._bg = bg + except TypeError: + # Only the terminal color is defined, so we need to get the hex color + from lib.colors import cterm_to_hex + self._bg = [bg, cterm_to_hex(bg)] + + self._attr = attr + self._side = side + self._padding = padding + self._separate = separate + + @property + def fg(self): + '''Segment foreground color property. + + Recursively searches for the property or the parent segments' property + until one is found. If this property is not defined anywhere in the + tree, the default foreground color is used. + ''' + if self.parent and self._fg[0] is None: + return self.parent.fg + return self._fg if self._fg[0] is not None else [None, None] + + @property + def bg(self): + '''Segment background color property. + + Recursively searches for the property or the parent segments' property + until one is found. If this property is not defined anywhere in the + tree, the default background color is used. + ''' + if self.parent and self._bg[0] is None: + return self.parent.bg + return self._bg if self._bg[0] is not None else [None, None] + + @property + def attr(self): + '''Segment attribute property. + + Recursively searches for the property or the parent segments' property + until one is found. If this property is not defined anywhere in the + tree, no attributes are applied. + ''' + if self.parent and self._attr is None: + return self.parent.attr + return self._attr if self._attr is not None else 0 + + @property + def side(self): + '''Segment side property. + + Recursively searches for the property or the parent segments' property + until one is found. If this property is not defined anywhere in the + tree, the left side is used for all segments. + ''' + if self.parent and self._side is None: + return self.parent.side + return self._side if self._side is not None else 'l' + + @property + def padding(self): + '''Segment padding property. + + Return which string is used to pad the segment before and after the + separator symbol. + + Recursively searches for the property or the parent segments' property + until one is found. If this property is not defined anywhere in the + tree, a single space is used for padding. + ''' + if self.parent and self._padding is None: + return self.parent.padding + return self._padding if self._padding is not None else ' ' + + @property + def separate(self): + '''Segment separation property. + + Returns whether a separator symbol should be drawn before/after the + segment. + + Recursively searches for the property or the parent segments' property + until one is found. If this property is not defined anywhere in the + tree, then separators will be drawn around all segments. + ''' + if self.parent and self._separate is None: + return self.parent.separate + return self._separate if self._separate is not None else True + + @property + def separator(self): + '''Segment separator property. + + Returns the separator symbol to be used, depending on which side this + segment is on. + ''' + return self.separators[self.side] + + def render(self, renderer): + '''Render the segment and all child segments. + + This method flattens the segment and all its child segments into + a one-dimensional array. It then loops through this array and compares + the foreground/background colors and separator/padding properties and + returns the rendered statusline as a string. + ''' + def flatten(segment): + '''Flattens the segment tree into a one-dimensional array. + ''' + ret = [] + for child_segment in segment.content: + child_segment.parent = segment + if isinstance(child_segment.content, str): + # If the contents of the child segment is a string then + # this is a tree node + ret.append(child_segment) + else: + # This is a segment group that should be flattened + ret += flatten(child_segment) + return ret + + segments = flatten(self) + output = '' + + # Loop through the segment array and create the segments, colors and + # separators + # + # TODO Make this prettier + for idx, segment in enumerate(segments): + # Ensure that we always have a previous/next segment, if we're at + # the beginning/end of the array an empty segment is used for the + # prev/next segment + segment.prev = segments[idx - 1] if idx > 0 else Segment() + segment.next = segments[idx + 1] if idx < len(segments) - 1 else Segment() + + if segment.side == 'l': + output += renderer.fg(segment.fg[0]) + output += renderer.bg(segment.bg[0]) + output += segment.padding + output += renderer.attr(segment.attr) + output += segment.content + output += renderer.attr(None) + if segment.content: + if segment.next.bg == segment.bg: + if segment.next.content and segment.separate: + output += segment.padding + output += segment.separator['soft'] + # Don't draw a hard separator if the next segment is on + # the opposite side, it screws up the coloring + elif segment.next.side == segment.side: + output += segment.padding + output += renderer.fg(segment.bg[0]) + output += renderer.bg(segment.next.bg[0]) + output += segment.separator['hard'] + else: + pad_pre = False + if segment.content: + if segment.prev.bg == segment.bg: + if segment.prev.content and segment.separate: + pad_pre = True + output += segment.separator['soft'] + else: + pad_pre = True + output += renderer.fg(segment.bg[0]) + output += renderer.bg(segment.prev.bg[0]) + output += segment.separator['hard'] + output += renderer.fg(segment.fg[0]) + output += renderer.bg(segment.bg[0]) + if pad_pre: + output += segment.padding + output += renderer.attr(segment.attr) + output += segment.content + output += renderer.attr(None) + output += segment.padding + + return output diff --git a/lib/renderers/__init__.py b/lib/renderers/__init__.py new file mode 100644 index 00000000..ee7568ee --- /dev/null +++ b/lib/renderers/__init__.py @@ -0,0 +1,8 @@ +class SegmentRenderer: + def fg(col): + raise NotImplementedError + + def bg(col): + raise NotImplementedError + +from lib.renderers.terminal import TerminalSegmentRenderer diff --git a/lib/renderers/terminal.py b/lib/renderers/terminal.py new file mode 100644 index 00000000..10eeb3d9 --- /dev/null +++ b/lib/renderers/terminal.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +from lib.core import Segment +from lib.renderers import SegmentRenderer + + +class TerminalSegmentRenderer(SegmentRenderer): + '''Powerline terminal segment renderer. + ''' + def fg(col): + '''Return ANSI escape code for foreground colors. + + If no color is provided, the color is reset to the terminal default. + ''' + if col: + return '[38;5;{0}m'.format(col) + else: + return '' + + def bg(col): + '''Return ANSI escape code for background colors. + + If no color is provided, the color is reset to the terminal default. + ''' + if col: + return '[48;5;{0}m'.format(col) + else: + return '' + + def attr(attrs): + '''Return ANSI escape code for attributes. + + Accepts a flag with attributes defined in Segment. + + If no attributes are provided, the attributes are reset to the terminal + defaults. + ''' + if not attrs: + return '' + + ansi_attrs = [] + if attrs & Segment.ATTR_BOLD: + ansi_attrs.append('1') + + if ansi_attrs: + return '[{0}m'.format(';'.join(ansi_attrs)) + + return '' diff --git a/powerline-terminal-example.py b/powerline-terminal-example.py new file mode 100755 index 00000000..2230e516 --- /dev/null +++ b/powerline-terminal-example.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +'''Powerline terminal prompt example. +''' + +from lib.core import Segment +from lib.renderers import TerminalSegmentRenderer + +powerline = Segment([ + Segment('⭤ SSH', 220, 166, attr=Segment.ATTR_BOLD), + Segment('username', 153, 31), + Segment([ + Segment('~'), + Segment('projects'), + Segment('powerline', 231, attr=Segment.ATTR_BOLD), + ], 248, 239), +]) + +print(powerline.render(TerminalSegmentRenderer)) From 99ded1d0c6558cb49a7bdbc24358282798bbf60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 12:44:46 +0100 Subject: [PATCH 0002/1472] Add vim statusline example This is currently rendered with the terminal renderer, and is just a simple proof-of-concept of how vim statuslines can be defined with the Python API. --- powerline-vim-example.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 powerline-vim-example.py diff --git a/powerline-vim-example.py b/powerline-vim-example.py new file mode 100755 index 00000000..03e45f81 --- /dev/null +++ b/powerline-vim-example.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +'''Powerline vim statusline example. +''' + +from lib.core import Segment +from lib.renderers import TerminalSegmentRenderer + +powerline = Segment([ + Segment('NORMAL', 22, 148, attr=Segment.ATTR_BOLD), + Segment('⭠ develop', 247, 240), + Segment([ + Segment(' ~/projects/powerline/lib/'), + Segment('core.py ', 231, attr=Segment.ATTR_BOLD), + ], 250, 240, separate=False, padding=''), + Segment(), + Segment([ + Segment('unix'), + Segment('utf-8'), + Segment('python'), + Segment(' 83%', 247, 240), + Segment([ + Segment(' ⭡ ', 239), + Segment('23', attr=Segment.ATTR_BOLD), + Segment(':1 ', 244), + ], 235, 252, separate=False, padding=''), + ], 245, side='r'), +], bg=236) + +print(powerline.render(TerminalSegmentRenderer)) From ffbdcc0f75267f65d0547ecd6341ca0c4d8bffbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 12:51:15 +0100 Subject: [PATCH 0003/1472] Fix soft separator issue with vim divider segments --- lib/core.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/core.py b/lib/core.py index a942311e..c3020539 100644 --- a/lib/core.py +++ b/lib/core.py @@ -204,7 +204,10 @@ class Segment: if segment.next.bg == segment.bg: if segment.next.content and segment.separate: output += segment.padding - output += segment.separator['soft'] + if segment.next.side == segment.side: + # Only draw the soft separator if this segment is on the same side + # No need to draw the soft separator if there's e.g. a vim divider in the next segment + output += segment.separator['soft'] # Don't draw a hard separator if the next segment is on # the opposite side, it screws up the coloring elif segment.next.side == segment.side: @@ -218,7 +221,10 @@ class Segment: if segment.prev.bg == segment.bg: if segment.prev.content and segment.separate: pad_pre = True - output += segment.separator['soft'] + if segment.prev.side == segment.side: + # Only draw the soft separator if this segment is on the same side + # No need to draw the soft separator if there's e.g. a vim divider in the previous segment + output += segment.separator['soft'] else: pad_pre = True output += renderer.fg(segment.bg[0]) From 59a760c3c617ce65c2734d1aa12f9f702075b62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 13:16:22 +0100 Subject: [PATCH 0004/1472] Join the fg/bg/attr methods of the renderers This change joins the fg/bg/attr methods into a single hl method in the renderers. This provides the same functionality, and it simplifies the terminal rendering by being able to join all the attributes into one escape sequence. It's also a necessary change for the vim renderer, as this renderer needs to log all the highlighting and create separate highlighting classes for every single color and attribute combination. The only way to do this is to have a single highlighting method. --- lib/core.py | 22 +++++--------- lib/renderers/__init__.py | 5 +--- lib/renderers/terminal.py | 56 ++++++++++++++--------------------- powerline-terminal-example.py | 2 +- powerline-vim-example.py | 2 +- 5 files changed, 34 insertions(+), 53 deletions(-) diff --git a/lib/core.py b/lib/core.py index c3020539..fe659b42 100644 --- a/lib/core.py +++ b/lib/core.py @@ -79,7 +79,7 @@ class Segment: ''' if self.parent and self._fg[0] is None: return self.parent.fg - return self._fg if self._fg[0] is not None else [None, None] + return self._fg if self._fg[0] is not None else False @property def bg(self): @@ -91,7 +91,7 @@ class Segment: ''' if self.parent and self._bg[0] is None: return self.parent.bg - return self._bg if self._bg[0] is not None else [None, None] + return self._bg if self._bg[0] is not None else False @property def attr(self): @@ -194,12 +194,10 @@ class Segment: segment.next = segments[idx + 1] if idx < len(segments) - 1 else Segment() if segment.side == 'l': - output += renderer.fg(segment.fg[0]) - output += renderer.bg(segment.bg[0]) + output += renderer.hl(segment.fg, segment.bg, segment.attr) output += segment.padding - output += renderer.attr(segment.attr) output += segment.content - output += renderer.attr(None) + output += renderer.hl(attr=False) if segment.content: if segment.next.bg == segment.bg: if segment.next.content and segment.separate: @@ -212,8 +210,7 @@ class Segment: # the opposite side, it screws up the coloring elif segment.next.side == segment.side: output += segment.padding - output += renderer.fg(segment.bg[0]) - output += renderer.bg(segment.next.bg[0]) + output += renderer.hl(segment.bg, segment.next.bg) output += segment.separator['hard'] else: pad_pre = False @@ -227,16 +224,13 @@ class Segment: output += segment.separator['soft'] else: pad_pre = True - output += renderer.fg(segment.bg[0]) - output += renderer.bg(segment.prev.bg[0]) + output += renderer.hl(segment.bg, segment.prev.bg) output += segment.separator['hard'] - output += renderer.fg(segment.fg[0]) - output += renderer.bg(segment.bg[0]) + output += renderer.hl(segment.fg, segment.bg, segment.attr) if pad_pre: output += segment.padding - output += renderer.attr(segment.attr) output += segment.content - output += renderer.attr(None) + output += renderer.hl(attr=False) output += segment.padding return output diff --git a/lib/renderers/__init__.py b/lib/renderers/__init__.py index ee7568ee..bda7cf34 100644 --- a/lib/renderers/__init__.py +++ b/lib/renderers/__init__.py @@ -1,8 +1,5 @@ class SegmentRenderer: - def fg(col): - raise NotImplementedError - - def bg(col): + def hl(self, fg=None, bg=None, attr=None): raise NotImplementedError from lib.renderers.terminal import TerminalSegmentRenderer diff --git a/lib/renderers/terminal.py b/lib/renderers/terminal.py index 10eeb3d9..3d9df3f7 100644 --- a/lib/renderers/terminal.py +++ b/lib/renderers/terminal.py @@ -7,42 +7,32 @@ from lib.renderers import SegmentRenderer class TerminalSegmentRenderer(SegmentRenderer): '''Powerline terminal segment renderer. ''' - def fg(col): - '''Return ANSI escape code for foreground colors. + def hl(self, fg=None, bg=None, attr=None): + '''Highlight a segment. - If no color is provided, the color is reset to the terminal default. + If an argument is None, the argument is ignored. If an argument is + False, the argument is reset to the terminal defaults. If an argument + is a valid color or attribute, it's added to the ANSI escape code. ''' - if col: - return '[38;5;{0}m'.format(col) - else: - return '' + ansi = [] - def bg(col): - '''Return ANSI escape code for background colors. + if fg is not None: + if fg is False: + ansi += [39] + else: + ansi += [38, 5, fg[0]] - If no color is provided, the color is reset to the terminal default. - ''' - if col: - return '[48;5;{0}m'.format(col) - else: - return '' + if bg is not None: + if bg is False: + ansi += [49] + else: + ansi += [48, 5, bg[0]] - def attr(attrs): - '''Return ANSI escape code for attributes. + if attr is not None: + if attr is False: + ansi += [22] + else: + if attr & Segment.ATTR_BOLD: + ansi += [1] - Accepts a flag with attributes defined in Segment. - - If no attributes are provided, the attributes are reset to the terminal - defaults. - ''' - if not attrs: - return '' - - ansi_attrs = [] - if attrs & Segment.ATTR_BOLD: - ansi_attrs.append('1') - - if ansi_attrs: - return '[{0}m'.format(';'.join(ansi_attrs)) - - return '' + return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) diff --git a/powerline-terminal-example.py b/powerline-terminal-example.py index 2230e516..0677d768 100755 --- a/powerline-terminal-example.py +++ b/powerline-terminal-example.py @@ -15,4 +15,4 @@ powerline = Segment([ ], 248, 239), ]) -print(powerline.render(TerminalSegmentRenderer)) +print(powerline.render(TerminalSegmentRenderer())) diff --git a/powerline-vim-example.py b/powerline-vim-example.py index 03e45f81..e7879230 100755 --- a/powerline-vim-example.py +++ b/powerline-vim-example.py @@ -26,4 +26,4 @@ powerline = Segment([ ], 245, side='r'), ], bg=236) -print(powerline.render(TerminalSegmentRenderer)) +print(powerline.render(TerminalSegmentRenderer())) From 20892b14d8f01ac2bbb512a2b68d59428d2652d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 14:05:16 +0100 Subject: [PATCH 0005/1472] Fix default segment attribute value --- lib/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core.py b/lib/core.py index fe659b42..7391651b 100644 --- a/lib/core.py +++ b/lib/core.py @@ -103,7 +103,7 @@ class Segment: ''' if self.parent and self._attr is None: return self.parent.attr - return self._attr if self._attr is not None else 0 + return self._attr if self._attr is not None else False @property def side(self): From b461ab1358c2516cd94dad53081755fbec512dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 14:05:48 +0100 Subject: [PATCH 0006/1472] Create basic vim segment renderer This commit also includes a basic proof-of-concept demo for creating vim statuslines. When creating vim statuslines you basically need to first create the statusline the same way as the terminal demo, use the vim renderer and then afterwards loop through the collected highlight groups in the vim renderer and write those as vim highlight statements. Vim obviously needs a ton of wrapper code to make everything work properly (separate modes, callbacks, all that stuff) so the current demo only shows some basic statusline highlighting. --- lib/renderers/__init__.py | 1 + lib/renderers/vim.py | 59 +++++++++++++++++++++++++++++++++++++++ powerline-vim-example.py | 21 +++++++++++--- 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 lib/renderers/vim.py diff --git a/lib/renderers/__init__.py b/lib/renderers/__init__.py index bda7cf34..f5539bca 100644 --- a/lib/renderers/__init__.py +++ b/lib/renderers/__init__.py @@ -3,3 +3,4 @@ class SegmentRenderer: raise NotImplementedError from lib.renderers.terminal import TerminalSegmentRenderer +from lib.renderers.vim import VimSegmentRenderer diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py new file mode 100644 index 00000000..15f1ef9c --- /dev/null +++ b/lib/renderers/vim.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +from lib.core import Segment +from lib.renderers import SegmentRenderer + + +class VimSegmentRenderer(SegmentRenderer): + '''Powerline vim segment renderer. + ''' + def __init__(self): + self.hl_groups = {} + + def hl(self, fg=None, bg=None, attr=None): + '''Highlight a segment. + + If an argument is None, the argument is ignored. If an argument is + False, the argument is reset to the terminal defaults. If an argument + is a valid color or attribute, it's added to the vim highlight group. + ''' + hl_group = { + 'ctermfg': 'NONE', + 'guifg': 'NONE', + 'ctermbg': 'NONE', + 'guibg': 'NONE', + 'attr': ['NONE'], + } + + # We don't need to explicitly reset attributes in vim, so skip those calls + if not attr and not bg and not fg: + return '' + + if fg is not None and fg is not False: + hl_group['ctermfg'] = fg[0] + hl_group['guifg'] = fg[1] + + if bg is not None and bg is not False: + hl_group['ctermbg'] = bg[0] + hl_group['guibg'] = bg[1] + + if attr is not None and attr is not False and attr != 0: + hl_group['attr'] = [] + if attr & Segment.ATTR_BOLD: + hl_group['attr'].append('bold') + if attr & Segment.ATTR_ITALIC: + hl_group['attr'].append('italic') + if attr & Segment.ATTR_UNDERLINE: + hl_group['attr'].append('underline') + + hl_group_name = 'Pl_{ctermfg}_{guifg}_{ctermbg}_{guibg}_{attr}'.format( + ctermfg=hl_group['ctermfg'], + guifg=hl_group['guifg'], + ctermbg=hl_group['ctermbg'], + guibg=hl_group['guibg'], + attr=''.join(attr[0] for attr in hl_group['attr']), + ) + + self.hl_groups[hl_group_name] = hl_group + + return '%#{0}#'.format(hl_group_name) diff --git a/powerline-vim-example.py b/powerline-vim-example.py index e7879230..39cb2938 100755 --- a/powerline-vim-example.py +++ b/powerline-vim-example.py @@ -3,7 +3,7 @@ ''' from lib.core import Segment -from lib.renderers import TerminalSegmentRenderer +from lib.renderers import VimSegmentRenderer powerline = Segment([ Segment('NORMAL', 22, 148, attr=Segment.ATTR_BOLD), @@ -12,12 +12,12 @@ powerline = Segment([ Segment(' ~/projects/powerline/lib/'), Segment('core.py ', 231, attr=Segment.ATTR_BOLD), ], 250, 240, separate=False, padding=''), - Segment(), + Segment('%<%='), Segment([ Segment('unix'), Segment('utf-8'), Segment('python'), - Segment(' 83%', 247, 240), + Segment(' 83%%', 247, 240), Segment([ Segment(' ⭡ ', 239), Segment('23', attr=Segment.ATTR_BOLD), @@ -26,4 +26,17 @@ powerline = Segment([ ], 245, side='r'), ], bg=236) -print(powerline.render(TerminalSegmentRenderer())) +renderer = VimSegmentRenderer() +stl = powerline.render(renderer) + +for group, hl in renderer.hl_groups.items(): + print('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( + group=group, + 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']), + )) + +print('let &stl = "{0}"'.format(stl)) From f47dc4bffda104e822400c28e50a2cc785774a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 8 Nov 2012 14:59:59 +0100 Subject: [PATCH 0007/1472] Change wording: separator -> divider --- lib/core.py | 58 ++++++++++++++++++++-------------------- powerline-vim-example.py | 4 +-- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/lib/core.py b/lib/core.py index 7391651b..e79f7623 100644 --- a/lib/core.py +++ b/lib/core.py @@ -22,7 +22,7 @@ class Segment: print(powerline.render(TerminalSegmentRenderer)) ''' - separators = { + dividers = { 'l': { 'hard': '⮀', 'soft': '⮁', @@ -37,11 +37,11 @@ class Segment: ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - def __init__(self, content='', fg=None, bg=None, attr=None, side=None, padding=None, separate=None): + def __init__(self, content='', fg=None, bg=None, attr=None, side=None, padding=None, divide=None): '''Create a new segment. No arguments are required when creating new segments, as - empty/colorless segments can be used e.g. as left/right separators. + empty/colorless segments can be used e.g. as left/right dividers. ''' self.content = content self.parent = None @@ -67,7 +67,7 @@ class Segment: self._attr = attr self._side = side self._padding = padding - self._separate = separate + self._divide = divide @property def fg(self): @@ -122,7 +122,7 @@ class Segment: '''Segment padding property. Return which string is used to pad the segment before and after the - separator symbol. + divider symbol. Recursively searches for the property or the parent segments' property until one is found. If this property is not defined anywhere in the @@ -133,35 +133,35 @@ class Segment: return self._padding if self._padding is not None else ' ' @property - def separate(self): - '''Segment separation property. + def divide(self): + '''Segment divider property. - Returns whether a separator symbol should be drawn before/after the + Returns whether a divider symbol should be drawn before/after the segment. Recursively searches for the property or the parent segments' property until one is found. If this property is not defined anywhere in the - tree, then separators will be drawn around all segments. + tree, then dividers will be drawn around all segments. ''' - if self.parent and self._separate is None: - return self.parent.separate - return self._separate if self._separate is not None else True + if self.parent and self._divide is None: + return self.parent.divide + return self._divide if self._divide is not None else True @property - def separator(self): - '''Segment separator property. + def divider(self): + '''Segment divider property. - Returns the separator symbol to be used, depending on which side this + Returns the divider symbol to be used, depending on which side this segment is on. ''' - return self.separators[self.side] + return self.dividers[self.side] def render(self, renderer): '''Render the segment and all child segments. This method flattens the segment and all its child segments into a one-dimensional array. It then loops through this array and compares - the foreground/background colors and separator/padding properties and + the foreground/background colors and divider/padding properties and returns the rendered statusline as a string. ''' def flatten(segment): @@ -183,7 +183,7 @@ class Segment: output = '' # Loop through the segment array and create the segments, colors and - # separators + # dividers # # TODO Make this prettier for idx, segment in enumerate(segments): @@ -200,32 +200,32 @@ class Segment: output += renderer.hl(attr=False) if segment.content: if segment.next.bg == segment.bg: - if segment.next.content and segment.separate: + if segment.next.content and segment.divide: output += segment.padding if segment.next.side == segment.side: - # Only draw the soft separator if this segment is on the same side - # No need to draw the soft separator if there's e.g. a vim divider in the next segment - output += segment.separator['soft'] - # Don't draw a hard separator if the next segment is on + # Only draw the soft divider if this segment is on the same side + # No need to draw the soft divider if there's e.g. a vim separation point in the next segment + output += segment.divider['soft'] + # Don't draw a hard divider if the next segment is on # the opposite side, it screws up the coloring elif segment.next.side == segment.side: output += segment.padding output += renderer.hl(segment.bg, segment.next.bg) - output += segment.separator['hard'] + output += segment.divider['hard'] else: pad_pre = False if segment.content: if segment.prev.bg == segment.bg: - if segment.prev.content and segment.separate: + if segment.prev.content and segment.divide: pad_pre = True if segment.prev.side == segment.side: - # Only draw the soft separator if this segment is on the same side - # No need to draw the soft separator if there's e.g. a vim divider in the previous segment - output += segment.separator['soft'] + # Only draw the soft divider if this segment is on the same side + # No need to draw the soft divider if there's e.g. a vim separation point in the previous segment + output += segment.divider['soft'] else: pad_pre = True output += renderer.hl(segment.bg, segment.prev.bg) - output += segment.separator['hard'] + output += segment.divider['hard'] output += renderer.hl(segment.fg, segment.bg, segment.attr) if pad_pre: output += segment.padding diff --git a/powerline-vim-example.py b/powerline-vim-example.py index 39cb2938..5103bf57 100755 --- a/powerline-vim-example.py +++ b/powerline-vim-example.py @@ -11,7 +11,7 @@ powerline = Segment([ Segment([ Segment(' ~/projects/powerline/lib/'), Segment('core.py ', 231, attr=Segment.ATTR_BOLD), - ], 250, 240, separate=False, padding=''), + ], 250, 240, divide=False, padding=''), Segment('%<%='), Segment([ Segment('unix'), @@ -22,7 +22,7 @@ powerline = Segment([ Segment(' ⭡ ', 239), Segment('23', attr=Segment.ATTR_BOLD), Segment(':1 ', 244), - ], 235, 252, separate=False, padding=''), + ], 235, 252, divide=False, padding=''), ], 245, side='r'), ], bg=236) From 21a48e997a1c7c69f0aa195db9e6955e2232c504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 12 Nov 2012 13:11:19 +0100 Subject: [PATCH 0008/1472] Move vimscript hl statement generation to vim renderer --- lib/renderers/vim.py | 17 +++++++++++++++++ powerline-vim-example.py | 11 +---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py index 15f1ef9c..cefe9dd7 100644 --- a/lib/renderers/vim.py +++ b/lib/renderers/vim.py @@ -57,3 +57,20 @@ class VimSegmentRenderer(SegmentRenderer): self.hl_groups[hl_group_name] = hl_group return '%#{0}#'.format(hl_group_name) + + def get_hl_statements(self): + '''Return vimscript highlight statements. + + Returns a string with all the required highlight statements for vim. + Requires the statusline to be rendered using the Segment.render() + method first as this method generates all the necessary highlighting + information. + ''' + return '\n'.join(['hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( + group=group, + 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']), + ) for group, hl in self.hl_groups.items()]) diff --git a/powerline-vim-example.py b/powerline-vim-example.py index 5103bf57..0cc302d5 100755 --- a/powerline-vim-example.py +++ b/powerline-vim-example.py @@ -29,14 +29,5 @@ powerline = Segment([ renderer = VimSegmentRenderer() stl = powerline.render(renderer) -for group, hl in renderer.hl_groups.items(): - print('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( - group=group, - 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']), - )) - +print(renderer.get_hl_statements()) print('let &stl = "{0}"'.format(stl)) From d3bace24ece72f717f9e210a1bf94653b6b41fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 14 Nov 2012 11:53:39 +0100 Subject: [PATCH 0009/1472] Improve the Segment class and rendering The Segment class has been given two new properties: priority and filler. The render method has been given a width argument. If the width argument is specified when rendering a segment and the rendered segments are too wide, segments are dropped in order of priority, with the lowest priority (highest number) being dropped first and any segment with priority < 0 is kept, regardless of the specified width. If the width argument is specified along with one or more filler segments, the filler segments will pad the remaining space with space characters until the desired width has been reached. The handling of segment attributes has also been improved by lazily looking up the correct attributes in the segment tree when it's being rendered. Finally, the rendering code itself has been improved a bit with less code duplication and much more readable code. --- lib/core.py | 321 +++++++++++++++++++-------------------- powerline-vim-example.py | 24 +-- 2 files changed, 172 insertions(+), 173 deletions(-) diff --git a/lib/core.py b/lib/core.py index e79f7623..386b4f5e 100644 --- a/lib/core.py +++ b/lib/core.py @@ -37,142 +37,106 @@ class Segment: ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - def __init__(self, content='', fg=None, bg=None, attr=None, side=None, padding=None, divide=None): - '''Create a new segment. - - No arguments are required when creating new segments, as - empty/colorless segments can be used e.g. as left/right dividers. + def __init__(self, contents=None, fg=None, bg=None, attr=None, side=None, padding=None, draw_divider=None, priority=None, filler=None): + '''Create a new Powerline segment. ''' - self.content = content self.parent = None - self.prev = None - self.next = None + + self.contents = contents or '' + self.fg = fg + self.bg = bg + self.attr = attr + self.side = side + self.padding = padding + self.draw_divider = draw_divider + self.priority = priority + self.filler = filler + + if self.filler: + # Filler segments should never have any dividers + self.draw_divider = False + + # Set the parent property for child segments + for segment in self.contents: + try: + segment.parent = self + except AttributeError: + # Not a Segment node + continue + + def init_attributes(self): + '''Initialize the default attributes for this segment. + + This method is intended to be run when all segments in the segment tree + have the correct parent segment set (i.e. after the root segment has + been instantiated). + ''' + def lookup_attr(attr, default, obj=self): + '''Looks up attributes in the segment tree. + + If the attribute isn't found anywhere, the default argument is used + for this segment. + ''' + # Check if the current object has the attribute defined + obj_attr = getattr(obj, attr) + if obj_attr is None: + try: + # Check if the object's parent has the attribute defined + return lookup_attr(attr, default, obj.parent) + except AttributeError: + # Root node reached + return default + return obj_attr + + # Set default attributes + self.fg = lookup_attr('fg', False) + self.bg = lookup_attr('bg', False) + self.attr = lookup_attr('attr', False) + self.side = lookup_attr('side', 'l') + self.padding = lookup_attr('padding', ' ') + self.draw_divider = lookup_attr('draw_divider', True) + self.priority = lookup_attr('priority', -1) + self.filler = lookup_attr('filler', False) try: - if len(fg) == 2: - self._fg = fg + if len(self.fg) == 2: + self.fg = self.fg except TypeError: # Only the terminal color is defined, so we need to get the hex color from lib.colors import cterm_to_hex - self._fg = [fg, cterm_to_hex(fg)] + self.fg = [self.fg, cterm_to_hex(self.fg)] try: - if len(bg) == 2: - self._bg = bg + if len(self.bg) == 2: + self.bg = self.bg except TypeError: # Only the terminal color is defined, so we need to get the hex color from lib.colors import cterm_to_hex - self._bg = [bg, cterm_to_hex(bg)] + self.bg = [self.bg, cterm_to_hex(self.bg)] - self._attr = attr - self._side = side - self._padding = padding - self._divide = divide - - @property - def fg(self): - '''Segment foreground color property. - - Recursively searches for the property or the parent segments' property - until one is found. If this property is not defined anywhere in the - tree, the default foreground color is used. - ''' - if self.parent and self._fg[0] is None: - return self.parent.fg - return self._fg if self._fg[0] is not None else False - - @property - def bg(self): - '''Segment background color property. - - Recursively searches for the property or the parent segments' property - until one is found. If this property is not defined anywhere in the - tree, the default background color is used. - ''' - if self.parent and self._bg[0] is None: - return self.parent.bg - return self._bg if self._bg[0] is not None else False - - @property - def attr(self): - '''Segment attribute property. - - Recursively searches for the property or the parent segments' property - until one is found. If this property is not defined anywhere in the - tree, no attributes are applied. - ''' - if self.parent and self._attr is None: - return self.parent.attr - return self._attr if self._attr is not None else False - - @property - def side(self): - '''Segment side property. - - Recursively searches for the property or the parent segments' property - until one is found. If this property is not defined anywhere in the - tree, the left side is used for all segments. - ''' - if self.parent and self._side is None: - return self.parent.side - return self._side if self._side is not None else 'l' - - @property - def padding(self): - '''Segment padding property. - - Return which string is used to pad the segment before and after the - divider symbol. - - Recursively searches for the property or the parent segments' property - until one is found. If this property is not defined anywhere in the - tree, a single space is used for padding. - ''' - if self.parent and self._padding is None: - return self.parent.padding - return self._padding if self._padding is not None else ' ' - - @property - def divide(self): - '''Segment divider property. - - Returns whether a divider symbol should be drawn before/after the - segment. - - Recursively searches for the property or the parent segments' property - until one is found. If this property is not defined anywhere in the - tree, then dividers will be drawn around all segments. - ''' - if self.parent and self._divide is None: - return self.parent.divide - return self._divide if self._divide is not None else True - - @property - def divider(self): - '''Segment divider property. - - Returns the divider symbol to be used, depending on which side this - segment is on. - ''' - return self.dividers[self.side] - - def render(self, renderer): + def render(self, renderer, width=None): '''Render the segment and all child segments. This method flattens the segment and all its child segments into a one-dimensional array. It then loops through this array and compares the foreground/background colors and divider/padding 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 flatten(segment): - '''Flattens the segment tree into a one-dimensional array. + '''Flatten the segment tree into a one-dimensional array. ''' ret = [] - for child_segment in segment.content: - child_segment.parent = segment - if isinstance(child_segment.content, str): + for child_segment in segment.contents: + if isinstance(child_segment.contents, str): # If the contents of the child segment is a string then # this is a tree node + child_segment.init_attributes() ret.append(child_segment) else: # This is a segment group that should be flattened @@ -180,57 +144,92 @@ class Segment: return ret segments = flatten(self) - output = '' - # Loop through the segment array and create the segments, colors and - # dividers - # - # TODO Make this prettier - for idx, segment in enumerate(segments): - # Ensure that we always have a previous/next segment, if we're at - # the beginning/end of the array an empty segment is used for the - # prev/next segment - segment.prev = segments[idx - 1] if idx > 0 else Segment() - segment.next = segments[idx + 1] if idx < len(segments) - 1 else Segment() + def render_segments(segments, render_raw=True, render_highlighted=True): + '''Render a one-dimensional segment array. - if segment.side == 'l': - output += renderer.hl(segment.fg, segment.bg, segment.attr) - output += segment.padding - output += segment.content - output += renderer.hl(attr=False) - if segment.content: - if segment.next.bg == segment.bg: - if segment.next.content and segment.divide: - output += segment.padding - if segment.next.side == segment.side: - # Only draw the soft divider if this segment is on the same side - # No need to draw the soft divider if there's e.g. a vim separation point in the next segment - output += segment.divider['soft'] - # Don't draw a hard divider if the next segment is on - # the opposite side, it screws up the coloring - elif segment.next.side == segment.side: - output += segment.padding - output += renderer.hl(segment.bg, segment.next.bg) - output += segment.divider['hard'] - else: - pad_pre = False - if segment.content: - if segment.prev.bg == segment.bg: - if segment.prev.content and segment.divide: - pad_pre = True - if segment.prev.side == segment.side: - # Only draw the soft divider if this segment is on the same side - # No need to draw the soft divider if there's e.g. a vim separation point in the previous segment - output += segment.divider['soft'] + By default this function renders both raw (un-highlighted segments + used for calculating final width) and highlighted segments. + ''' + rendered_raw = '' + rendered_highlighted = '' + + for idx, segment in enumerate(segments): + prev = segments[idx - 1] if idx > 0 else Segment() + next = segments[idx + 1] if idx < len(segments) - 1 else Segment() + + compare_segment = next if segment.side == 'l' else prev + divider_type = 'soft' if compare_segment.bg == segment.bg else 'hard' + divider = self.dividers[segment.side][divider_type] + + if segment.filler: + # Filler segments shouldn't be padded + segment_format = '{contents}' + elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare_segment.side): + # Draw divider if specified, and if the next segment is on + # the opposite side only draw the divider if it's a hard + # divider + if segment.side == 'l': + segment_format = '{segment_hl}{padding}{contents}{padding}{divider_hl}{divider}' else: - pad_pre = True - output += renderer.hl(segment.bg, segment.prev.bg) - output += segment.divider['hard'] - output += renderer.hl(segment.fg, segment.bg, segment.attr) - if pad_pre: - output += segment.padding - output += segment.content - output += renderer.hl(attr=False) - output += segment.padding + segment_format = '{divider_hl}{divider}{segment_hl}{padding}{contents}{padding}' + elif segment.contents: + # Soft divided segments + segment_format = '{segment_hl}{padding}{contents}{padding}' + else: + # Unknown segment type, skip it + continue - return output + if render_raw is True and segment.filler is False: + # Filler segments must be empty when used e.g. in vim (the + # %=%< segment which disappears), so they will be skipped + # when calculating the width using the raw rendering + rendered_raw += segment_format.format( + padding=segment.padding, + divider=divider, + contents=segment.contents, + divider_hl='', + segment_hl='', + ) + + if render_highlighted is True: + rendered_highlighted += segment_format.format( + padding=segment.padding, + divider=divider, + contents=segment.contents, + divider_hl='' if divider_type == 'soft' else renderer.hl(segment.bg, compare_segment.bg), + segment_hl=renderer.hl(segment.fg, segment.bg, segment.attr), + ) + + return { + 'highlighted': rendered_highlighted, + 'raw': rendered_raw, + } + + rendered = render_segments(segments) + + if not width: + # No width specified, so we don't need to crop or pad anything + return rendered['highlighted'] + + import math + + # Create an ordered list of segments that can be dropped + segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] + + while len(rendered['raw']) > width and len(segments_priority): + segments.remove(segments_priority[0]) + segments_priority.pop(0) + + rendered = render_segments(segments, render_highlighted=False) + + # Distribute the remaining space on the filler segments + segments_fillers = [segment for segment in segments if segment.filler is True] + segments_fillers_contents = ' ' * math.floor((width - len(rendered['raw'])) / len(segments_fillers)) + for segment in segments_fillers: + segment.contents = segments_fillers_contents + + # Do a final render now that we have handled the cropping and padding + rendered = render_segments(segments, render_raw=False) + + return rendered['highlighted'] diff --git a/powerline-vim-example.py b/powerline-vim-example.py index 0cc302d5..ddcb1855 100755 --- a/powerline-vim-example.py +++ b/powerline-vim-example.py @@ -7,22 +7,22 @@ from lib.renderers import VimSegmentRenderer powerline = Segment([ Segment('NORMAL', 22, 148, attr=Segment.ATTR_BOLD), - Segment('⭠ develop', 247, 240), + Segment('⭠ develop', 247, 240, priority=10), Segment([ - Segment(' ~/projects/powerline/lib/'), + Segment(' ~/projects/powerline/lib/', draw_divider=False), Segment('core.py ', 231, attr=Segment.ATTR_BOLD), - ], 250, 240, divide=False, padding=''), - Segment('%<%='), + ], 250, 240, padding=''), + Segment('%=%<', filler=True), Segment([ - Segment('unix'), - Segment('utf-8'), - Segment('python'), - Segment(' 83%%', 247, 240), + Segment('unix', priority=50), + Segment('utf-8', priority=50), + Segment('python', priority=50), + Segment('83%', 247, 240, priority=30), Segment([ - Segment(' ⭡ ', 239), - Segment('23', attr=Segment.ATTR_BOLD), - Segment(':1 ', 244), - ], 235, 252, divide=False, padding=''), + Segment('⭡', 239), + Segment('23', attr=Segment.ATTR_BOLD, padding='', draw_divider=False), + Segment(':1', 244, priority=30, padding='', draw_divider=False), + ], 235, 252), ], 245, side='r'), ], bg=236) From b5d72afb7b0745dc39cb4308fe5d716201eef29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 14 Nov 2012 20:24:09 +0100 Subject: [PATCH 0010/1472] Create dynamic vim statusline example This example only does a single type of highlighting (normal mode) but demonstrates nicely how rendering a statusline with Python could work. This also utilizes the new width functionality to crop away less important segment if the window is too small, and do the padding with Python instead of using vim's split segment. Some optimization will probably be necessary, as calling Python for each statusline redraw is quite expensive. A solution could be evaluating the segments in Python once and then calculating the width and crop away/pad the segments, and then sending the statusline with unevaluated segments to vim for rendering without calling Python. Only some events (like CursorHold, InsertEnter/InsertLeave, etc.) would trigger a re-render with Python, a recalculation of the width and cropping/padding. --- powerline-vim-example.vim | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 powerline-vim-example.vim diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim new file mode 100644 index 00000000..997e184d --- /dev/null +++ b/powerline-vim-example.vim @@ -0,0 +1,55 @@ +" Powerline vim example +" Run with :source % + +let s:did_highlighting = 0 +let s:segments = [ + \ { 'contents': 'mode()', 'fg': 22, 'bg': 148, 'attr': 1 }, + \ { 'contents': '"⭠ develop"', 'fg': 247, 'bg': 240, 'priority': 10 }, + \ { 'contents': '" ".expand("%:p:h")."/"', 'fg': 250, 'bg': 240, 'padding': '', 'draw_divider': 0, 'priority': 5 }, + \ { 'contents': 'expand("%:p:t")." "', 'fg': 231, 'bg': 240, 'padding': '', 'attr': 1 }, + \ { 'filler': 1 }, + \ { 'contents': '&ff', 'fg': 245, 'bg': 236, 'side': 'r', 'priority': 50 }, + \ { 'contents': '&fenc', 'fg': 245, 'bg': 236, 'side': 'r', 'priority': 50 }, + \ { 'contents': '&ft', 'fg': 245, 'bg': 236, 'side': 'r', 'priority': 50 }, + \ { 'contents': '(line(".") * 100 / line("$") * 100) / 100 . "%%"', 'fg': 247, 'bg': 240, 'side': 'r', 'priority': 30 }, + \ { 'contents': '"⭡"', 'fg': 239, 'bg': 252, 'side': 'r' }, + \ { 'contents': 'line(".")', 'fg': 239, 'bg': 252, 'side': 'r', 'attr': 1, 'padding': '', 'draw_divider': 0 }, + \ { 'contents': '":". col(".")', 'fg': 244, 'bg': 252, 'side': 'r', 'padding': '', 'draw_divider': 0, 'priority': 30 }, +\ ] + +function! DynStl() + python < Date: Wed, 14 Nov 2012 20:44:23 +0100 Subject: [PATCH 0011/1472] Update vim statusline example Now the vim statusline example does everything in Python, so the python example isn't required anymore. --- powerline-vim-example.py | 33 -------------------------- powerline-vim-example.vim | 50 +++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 59 deletions(-) delete mode 100755 powerline-vim-example.py diff --git a/powerline-vim-example.py b/powerline-vim-example.py deleted file mode 100755 index ddcb1855..00000000 --- a/powerline-vim-example.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -'''Powerline vim statusline example. -''' - -from lib.core import Segment -from lib.renderers import VimSegmentRenderer - -powerline = Segment([ - Segment('NORMAL', 22, 148, attr=Segment.ATTR_BOLD), - Segment('⭠ develop', 247, 240, priority=10), - Segment([ - Segment(' ~/projects/powerline/lib/', draw_divider=False), - Segment('core.py ', 231, attr=Segment.ATTR_BOLD), - ], 250, 240, padding=''), - Segment('%=%<', filler=True), - Segment([ - Segment('unix', priority=50), - Segment('utf-8', priority=50), - Segment('python', priority=50), - Segment('83%', 247, 240, priority=30), - Segment([ - Segment('⭡', 239), - Segment('23', attr=Segment.ATTR_BOLD, padding='', draw_divider=False), - Segment(':1', 244, priority=30, padding='', draw_divider=False), - ], 235, 252), - ], 245, side='r'), -], bg=236) - -renderer = VimSegmentRenderer() -stl = powerline.render(renderer) - -print(renderer.get_hl_statements()) -print('let &stl = "{0}"'.format(stl)) diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim index 997e184d..faf04f87 100644 --- a/powerline-vim-example.vim +++ b/powerline-vim-example.vim @@ -2,20 +2,6 @@ " Run with :source % let s:did_highlighting = 0 -let s:segments = [ - \ { 'contents': 'mode()', 'fg': 22, 'bg': 148, 'attr': 1 }, - \ { 'contents': '"⭠ develop"', 'fg': 247, 'bg': 240, 'priority': 10 }, - \ { 'contents': '" ".expand("%:p:h")."/"', 'fg': 250, 'bg': 240, 'padding': '', 'draw_divider': 0, 'priority': 5 }, - \ { 'contents': 'expand("%:p:t")." "', 'fg': 231, 'bg': 240, 'padding': '', 'attr': 1 }, - \ { 'filler': 1 }, - \ { 'contents': '&ff', 'fg': 245, 'bg': 236, 'side': 'r', 'priority': 50 }, - \ { 'contents': '&fenc', 'fg': 245, 'bg': 236, 'side': 'r', 'priority': 50 }, - \ { 'contents': '&ft', 'fg': 245, 'bg': 236, 'side': 'r', 'priority': 50 }, - \ { 'contents': '(line(".") * 100 / line("$") * 100) / 100 . "%%"', 'fg': 247, 'bg': 240, 'side': 'r', 'priority': 30 }, - \ { 'contents': '"⭡"', 'fg': 239, 'bg': 252, 'side': 'r' }, - \ { 'contents': 'line(".")', 'fg': 239, 'bg': 252, 'side': 'r', 'attr': 1, 'padding': '', 'draw_divider': 0 }, - \ { 'contents': '":". col(".")', 'fg': 244, 'bg': 252, 'side': 'r', 'padding': '', 'draw_divider': 0, 'priority': 30 }, -\ ] function! DynStl() python < Date: Wed, 14 Nov 2012 20:48:07 +0100 Subject: [PATCH 0012/1472] Simplify filler rendering --- lib/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/core.py b/lib/core.py index 386b4f5e..73ddb28a 100644 --- a/lib/core.py +++ b/lib/core.py @@ -212,8 +212,6 @@ class Segment: # No width specified, so we don't need to crop or pad anything return rendered['highlighted'] - import math - # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] @@ -225,9 +223,10 @@ class Segment: # Distribute the remaining space on the filler segments segments_fillers = [segment for segment in segments if segment.filler is True] - segments_fillers_contents = ' ' * math.floor((width - len(rendered['raw'])) / len(segments_fillers)) - for segment in segments_fillers: - segment.contents = segments_fillers_contents + if segments_fillers: + segments_fillers_contents = ' ' * int((width - len(rendered['raw'])) / len(segments_fillers)) + for segment in segments_fillers: + segment.contents = segments_fillers_contents # Do a final render now that we have handled the cropping and padding rendered = render_segments(segments, render_raw=False) From 89375b5c6a52f5e96a502043c6492fe0e3574d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 14 Nov 2012 20:49:04 +0100 Subject: [PATCH 0013/1472] Fix issues with terminal renderer --- lib/renderers/terminal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/renderers/terminal.py b/lib/renderers/terminal.py index 3d9df3f7..b80c37e1 100644 --- a/lib/renderers/terminal.py +++ b/lib/renderers/terminal.py @@ -17,13 +17,13 @@ class TerminalSegmentRenderer(SegmentRenderer): ansi = [] if fg is not None: - if fg is False: + if fg[0] is False: ansi += [39] else: ansi += [38, 5, fg[0]] if bg is not None: - if bg is False: + if bg[0] is False: ansi += [49] else: ansi += [48, 5, bg[0]] From cff4fcdd88f9f4dff2797f015892a0d13c66717b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 14 Nov 2012 22:37:50 +0100 Subject: [PATCH 0014/1472] Fix segment padding The padding argument has been removed and is now handled automatically. Segments without dividers are not padded anymore. This was necessary to ensure that segments are rendered correctly when cropping segments (overriding the padding argument screwed up the padding for segments without dividers and without padding). --- lib/core.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/core.py b/lib/core.py index 73ddb28a..44be33e2 100644 --- a/lib/core.py +++ b/lib/core.py @@ -37,7 +37,7 @@ class Segment: ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - def __init__(self, contents=None, fg=None, bg=None, attr=None, side=None, padding=None, draw_divider=None, priority=None, filler=None): + def __init__(self, contents=None, fg=None, bg=None, attr=None, side=None, draw_divider=None, priority=None, filler=None): '''Create a new Powerline segment. ''' self.parent = None @@ -47,7 +47,6 @@ class Segment: self.bg = bg self.attr = attr self.side = side - self.padding = padding self.draw_divider = draw_divider self.priority = priority self.filler = filler @@ -93,7 +92,6 @@ class Segment: self.bg = lookup_attr('bg', False) self.attr = lookup_attr('attr', False) self.side = lookup_attr('side', 'l') - self.padding = lookup_attr('padding', ' ') self.draw_divider = lookup_attr('draw_divider', True) self.priority = lookup_attr('priority', -1) self.filler = lookup_attr('filler', False) @@ -119,8 +117,8 @@ class Segment: This method flattens the segment and all its child segments into a one-dimensional array. It then loops through this array and compares - the foreground/background colors and divider/padding properties and - returns the rendered statusline as a string. + 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 @@ -170,12 +168,12 @@ class Segment: # the opposite side only draw the divider if it's a hard # divider if segment.side == 'l': - segment_format = '{segment_hl}{padding}{contents}{padding}{divider_hl}{divider}' + segment_format = '{segment_hl}{outer_padding}{contents} {divider_hl}{divider} ' else: - segment_format = '{divider_hl}{divider}{segment_hl}{padding}{contents}{padding}' + segment_format = ' {divider_hl}{divider}{segment_hl} {contents}{outer_padding}' elif segment.contents: - # Soft divided segments - segment_format = '{segment_hl}{padding}{contents}{padding}' + # Segments without divider + segment_format = '{segment_hl}{contents}{outer_padding}' else: # Unknown segment type, skip it continue @@ -185,20 +183,20 @@ class Segment: # %=%< segment which disappears), so they will be skipped # when calculating the width using the raw rendering rendered_raw += segment_format.format( - padding=segment.padding, divider=divider, contents=segment.contents, divider_hl='', segment_hl='', + outer_padding=' ' if idx == 0 or idx == len(segments) - 1 else '', ) if render_highlighted is True: rendered_highlighted += segment_format.format( - padding=segment.padding, divider=divider, contents=segment.contents, divider_hl='' if divider_type == 'soft' else renderer.hl(segment.bg, compare_segment.bg), segment_hl=renderer.hl(segment.fg, segment.bg, segment.attr), + outer_padding=' ' if idx == 0 or idx == len(segments) - 1 else '', ) return { From e99b1f17532605e7674500fa8745d104bd1aeb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 14 Nov 2012 22:40:26 +0100 Subject: [PATCH 0015/1472] Fix vim statusline example --- powerline-vim-example.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim index faf04f87..6d84818a 100644 --- a/powerline-vim-example.vim +++ b/powerline-vim-example.vim @@ -22,9 +22,9 @@ powerline = Segment([ Segment(vim.eval('mode()'), 22, 148, attr=Segment.ATTR_BOLD), Segment('⭠ develop', 247, 240, priority=10), Segment([ - Segment(vim.eval('" ".expand("%:p:h")."/"'), draw_divider=False, priority=5), - Segment(vim.eval('expand("%:p:t")." "'), 231, attr=Segment.ATTR_BOLD), - ], 250, 240, padding=''), + Segment(vim.eval('expand("%:p:h")."/"'), draw_divider=False, priority=5), + Segment(vim.eval('expand("%:p:t")'), 231, attr=Segment.ATTR_BOLD), + ], 250, 240), Segment(filler=True), Segment([ Segment(vim.eval('&ff'), priority=50), @@ -32,9 +32,9 @@ powerline = Segment([ Segment(vim.eval('&ft'), priority=50), Segment(str(line_percent) + '%%', 247, 240, priority=30), Segment([ - Segment('⭡', 239), - Segment(str(line_current), attr=Segment.ATTR_BOLD, padding='', draw_divider=False), - Segment(vim.eval('":".col(".")." "'), 244, priority=30, padding='', draw_divider=False), + Segment('⭡ ', 239), + Segment(str(line_current), attr=Segment.ATTR_BOLD, draw_divider=False), + Segment(vim.eval('":".col(".")'), 244, priority=30, draw_divider=False), ], 235, 252), ], 245, side='r'), ], fg=236, bg=236) From ba73083641cf0be1c8bbdce97e7c67cce07e3541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 15 Nov 2012 13:31:42 +0100 Subject: [PATCH 0016/1472] Remove get_hl_statements from vim renderer --- lib/renderers/vim.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py index cefe9dd7..15f1ef9c 100644 --- a/lib/renderers/vim.py +++ b/lib/renderers/vim.py @@ -57,20 +57,3 @@ class VimSegmentRenderer(SegmentRenderer): self.hl_groups[hl_group_name] = hl_group return '%#{0}#'.format(hl_group_name) - - def get_hl_statements(self): - '''Return vimscript highlight statements. - - Returns a string with all the required highlight statements for vim. - Requires the statusline to be rendered using the Segment.render() - method first as this method generates all the necessary highlighting - information. - ''' - return '\n'.join(['hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( - group=group, - 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']), - ) for group, hl in self.hl_groups.items()]) From 804447d04dab77380820c72c0b535cb9160ef702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 15 Nov 2012 13:32:07 +0100 Subject: [PATCH 0017/1472] Update vim statusline example --- powerline-vim-example.vim | 72 +++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim index 6d84818a..4a092262 100644 --- a/powerline-vim-example.vim +++ b/powerline-vim-example.vim @@ -1,11 +1,14 @@ " Powerline vim example " Run with :source % -let s:did_highlighting = 0 +set stl=%!DynStl() function! DynStl() - python < Date: Thu, 15 Nov 2012 13:35:08 +0100 Subject: [PATCH 0018/1472] Add unicode stuff for Python 2 Powerline will probably only be Python 2 in the foreseeable future because of poor Python 3 support in applications like vim, so this small change ensures correct unicode behavior in Python 2. --- lib/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core.py b/lib/core.py index 44be33e2..77445da4 100644 --- a/lib/core.py +++ b/lib/core.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- + + class Segment: '''Powerline segment renderer. @@ -213,7 +216,7 @@ class Segment: # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] - while len(rendered['raw']) > width and len(segments_priority): + while len(rendered['raw'].decode('utf-8')) > width and len(segments_priority): segments.remove(segments_priority[0]) segments_priority.pop(0) @@ -222,7 +225,7 @@ class Segment: # Distribute the remaining space on the filler segments segments_fillers = [segment for segment in segments if segment.filler is True] if segments_fillers: - segments_fillers_contents = ' ' * int((width - len(rendered['raw'])) / len(segments_fillers)) + segments_fillers_contents = ' ' * int((width - len(rendered['raw'].decode('utf-8'))) / len(segments_fillers)) for segment in segments_fillers: segment.contents = segments_fillers_contents From 2ea555b2dd270456d77b46ac1f168c2b64f39a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 16 Nov 2012 12:08:55 +0100 Subject: [PATCH 0019/1472] Rewrite core code to only allow a single-dimensional segment array The segment tree seemed like a good idea at the time, but for the core code it's probably best to do a single-dimensional array and instead have some duplicated data (common segment attributes). By removing the tree structured segments the code is much simpler to understand and use, and probably quite a bit faster. If a tree structure is wanted for ease of configuration, it could easily be supported in the JSON config files for vim statuslines, and then letting the vim statusline code flatten the configuration into a single-dimensional array for rendering. --- lib/core.py | 185 ++++++++++------------------------ powerline-terminal-example.py | 13 ++- powerline-vim-example.vim | 32 +++--- 3 files changed, 74 insertions(+), 156 deletions(-) diff --git a/lib/core.py b/lib/core.py index 77445da4..a4f353da 100644 --- a/lib/core.py +++ b/lib/core.py @@ -1,30 +1,7 @@ # -*- coding: utf-8 -*- -class Segment: - '''Powerline segment renderer. - - Powerline segments are initially structured as a tree of segments and sub - segments. This is to give the segments a sense of grouping and "scope", to - avoid having to define all the properties (fg, bg, etc.) for every single - segment. By grouping you can e.g. provide a common background color for - several segments. - - Usage example: - - from lib.core import Segment - from lib.renderers import TerminalSegmentRenderer - - powerline = Segment([ - Segment('First segment'), - Segment([ - Segment('Grouped segment 1'), - Segment('Grouped segment 2'), - ]), - ]) - - print(powerline.render(TerminalSegmentRenderer)) - ''' +class Powerline: dividers = { 'l': { 'hard': '⮀', @@ -36,91 +13,16 @@ class Segment: }, } - ATTR_BOLD = 1 - ATTR_ITALIC = 2 - ATTR_UNDERLINE = 4 - - def __init__(self, contents=None, fg=None, bg=None, attr=None, side=None, draw_divider=None, priority=None, filler=None): - '''Create a new Powerline segment. + def __init__(self, segments): + '''Create a new Powerline. ''' - self.parent = None - - self.contents = contents or '' - self.fg = fg - self.bg = bg - self.attr = attr - self.side = side - self.draw_divider = draw_divider - self.priority = priority - self.filler = filler - - if self.filler: - # Filler segments should never have any dividers - self.draw_divider = False - - # Set the parent property for child segments - for segment in self.contents: - try: - segment.parent = self - except AttributeError: - # Not a Segment node - continue - - def init_attributes(self): - '''Initialize the default attributes for this segment. - - This method is intended to be run when all segments in the segment tree - have the correct parent segment set (i.e. after the root segment has - been instantiated). - ''' - def lookup_attr(attr, default, obj=self): - '''Looks up attributes in the segment tree. - - If the attribute isn't found anywhere, the default argument is used - for this segment. - ''' - # Check if the current object has the attribute defined - obj_attr = getattr(obj, attr) - if obj_attr is None: - try: - # Check if the object's parent has the attribute defined - return lookup_attr(attr, default, obj.parent) - except AttributeError: - # Root node reached - return default - return obj_attr - - # Set default attributes - self.fg = lookup_attr('fg', False) - self.bg = lookup_attr('bg', False) - self.attr = lookup_attr('attr', False) - self.side = lookup_attr('side', 'l') - self.draw_divider = lookup_attr('draw_divider', True) - self.priority = lookup_attr('priority', -1) - self.filler = lookup_attr('filler', False) - - try: - if len(self.fg) == 2: - self.fg = self.fg - except TypeError: - # Only the terminal color is defined, so we need to get the hex color - from lib.colors import cterm_to_hex - self.fg = [self.fg, cterm_to_hex(self.fg)] - - try: - if len(self.bg) == 2: - self.bg = self.bg - except TypeError: - # Only the terminal color is defined, so we need to get the hex color - from lib.colors import cterm_to_hex - self.bg = [self.bg, cterm_to_hex(self.bg)] + self.segments = segments def render(self, renderer, width=None): - '''Render the segment and all child segments. + '''Render all the segments with the specified renderer. - This method flattens the segment and all its child segments into - a one-dimensional array. It then loops through this array and compares - the foreground/background colors and divider properties and returns the + This method loops through the segment array and compares the + foreground/background colors and divider properties and returns the rendered statusline as a string. When a width is provided, low-priority segments are dropped one at @@ -129,28 +31,13 @@ class Segment: provided they will fill the remaining space until the desired width is reached. ''' - def flatten(segment): - '''Flatten the segment tree into a one-dimensional array. - ''' - ret = [] - for child_segment in segment.contents: - if isinstance(child_segment.contents, str): - # If the contents of the child segment is a string then - # this is a tree node - child_segment.init_attributes() - ret.append(child_segment) - else: - # This is a segment group that should be flattened - ret += flatten(child_segment) - return ret - - segments = flatten(self) - def render_segments(segments, render_raw=True, render_highlighted=True): - '''Render a one-dimensional segment array. + '''Render a segment array. By default this function renders both raw (un-highlighted segments - used for calculating final width) and highlighted segments. + used for calculating final width) and highlighted segments. The raw + rendering is used for calculating the total width for dropping + low-priority segments. ''' rendered_raw = '' rendered_highlighted = '' @@ -207,29 +94,67 @@ class Segment: 'raw': rendered_raw, } - rendered = render_segments(segments) + rendered = render_segments(self.segments) if not width: # No width specified, so we don't need to crop or pad anything return rendered['highlighted'] # Create an ordered list of segments that can be dropped - segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] + segments_priority = [segment for segment in sorted(self.segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] while len(rendered['raw'].decode('utf-8')) > width and len(segments_priority): - segments.remove(segments_priority[0]) + self.segments.remove(segments_priority[0]) segments_priority.pop(0) - rendered = render_segments(segments, render_highlighted=False) + rendered = render_segments(self.segments, render_highlighted=False) # Distribute the remaining space on the filler segments - segments_fillers = [segment for segment in segments if segment.filler is True] + segments_fillers = [segment for segment in self.segments if segment.filler is True] if segments_fillers: segments_fillers_contents = ' ' * int((width - len(rendered['raw'].decode('utf-8'))) / len(segments_fillers)) for segment in segments_fillers: segment.contents = segments_fillers_contents # Do a final render now that we have handled the cropping and padding - rendered = render_segments(segments, render_raw=False) + rendered = render_segments(self.segments, render_raw=False) return rendered['highlighted'] + + +class Segment: + ATTR_BOLD = 1 + ATTR_ITALIC = 2 + ATTR_UNDERLINE = 4 + + def __init__(self, contents=None, fg=False, bg=False, attr=False, side='l', draw_divider=True, priority=-1, filler=False): + '''Create a new Powerline segment. + ''' + self.contents = str(contents or '') + self.fg = fg + self.bg = bg + self.attr = attr + self.side = side + self.draw_divider = draw_divider + self.priority = priority + self.filler = filler + + if self.filler: + # Filler segments should never have any dividers + self.draw_divider = False + + try: + if len(self.fg) != 2: + raise TypeError + except TypeError: + # Only the terminal color is defined, so we need to get the hex color + from lib.colors import cterm_to_hex + self.fg = [self.fg, cterm_to_hex(self.fg)] + + try: + if len(self.bg) != 2: + raise TypeError + except TypeError: + # Only the terminal color is defined, so we need to get the hex color + from lib.colors import cterm_to_hex + self.bg = [self.bg, cterm_to_hex(self.bg)] diff --git a/powerline-terminal-example.py b/powerline-terminal-example.py index 0677d768..8ae52cfc 100755 --- a/powerline-terminal-example.py +++ b/powerline-terminal-example.py @@ -2,17 +2,16 @@ '''Powerline terminal prompt example. ''' -from lib.core import Segment +from lib.core import Powerline, Segment from lib.renderers import TerminalSegmentRenderer -powerline = Segment([ +powerline = Powerline([ Segment('⭤ SSH', 220, 166, attr=Segment.ATTR_BOLD), Segment('username', 153, 31), - Segment([ - Segment('~'), - Segment('projects'), - Segment('powerline', 231, attr=Segment.ATTR_BOLD), - ], 248, 239), + Segment('~', 248, 239), + Segment('projects', 248, 239), + Segment('powerline', 231, 239, attr=Segment.ATTR_BOLD), + Segment(filler=True), ]) print(powerline.render(TerminalSegmentRenderer())) diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim index 4a092262..7f2da064 100644 --- a/powerline-vim-example.vim +++ b/powerline-vim-example.vim @@ -12,7 +12,7 @@ import re import sys sys.path.append('.') -from lib.core import Segment +from lib.core import Powerline, Segment from lib.renderers import VimSegmentRenderer winwidth = int(vim.eval('winwidth(0)')) @@ -47,26 +47,20 @@ filepath = os.path.split(vim.eval('expand("%:~:.")')) if filepath[0]: filepath[0] += os.sep -powerline = Segment([ +powerline = Powerline([ Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), Segment('⭠ ' + branch, 250, 240, priority=10), - Segment([ - Segment(filepath[0], draw_divider=False, priority=5), - Segment(filepath[1], 231, attr=Segment.ATTR_BOLD), - ], 250, 240), - Segment(filler=True), - Segment([ - Segment(vim.eval('&ff'), priority=50), - Segment(vim.eval('&fenc'), priority=50), - Segment(vim.eval('&ft'), priority=50), - Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, priority=30), - Segment([ - Segment('⭡ ', 239), - Segment(str(line_current).rjust(3), attr=Segment.ATTR_BOLD, draw_divider=False), - Segment(':' + str(col_current).ljust(2), 244, priority=30, draw_divider=False), - ], 235, 252), - ], 247, side='r'), -], fg=236, bg=236) + Segment(filepath[0], 250, 240, draw_divider=False, priority=5), + Segment(filepath[1], 231, 240, attr=Segment.ATTR_BOLD), + Segment(filler=True, fg=236, bg=236), + Segment(vim.eval('&ff'), 247, 236, side='r', priority=50), + Segment(vim.eval('&fenc'), 247, 236, side='r', priority=50), + Segment(vim.eval('&ft'), 247, 236, side='r', priority=50), + Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), + Segment('⭡ ', 239, 252, side='r'), + Segment(str(line_current).rjust(3), 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), + Segment(':' + str(col_current).ljust(2), 244, 252, side='r', priority=30, draw_divider=False), +]) renderer = VimSegmentRenderer() stl = powerline.render(renderer, winwidth) From cbf8d16ed88bdfb5f9699076a6f2a689984ad4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 16 Nov 2012 12:49:54 +0100 Subject: [PATCH 0020/1472] Ignore empty segments that aren't filler segments --- lib/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core.py b/lib/core.py index a4f353da..26950054 100644 --- a/lib/core.py +++ b/lib/core.py @@ -15,8 +15,11 @@ class Powerline: def __init__(self, segments): '''Create a new Powerline. + + Segments that have empty contents and aren't filler segments are + dropped from the segment array. ''' - self.segments = segments + self.segments = [segment for segment in segments if segment.contents or segment.filler] def render(self, renderer, width=None): '''Render all the segments with the specified renderer. From 1d1021992fab93a444e98935154f3aaf77d2b87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 16 Nov 2012 17:05:55 +0100 Subject: [PATCH 0021/1472] Update vim and terminal examples The examples have been moved into their own directory. The vim example now uses pyeval() and vim.bindeval() and loads the Python file through an import (so it gets compiled) for speed improvements. --- examples/__init__.py | 0 .../terminal/powerline.py | 4 + examples/vim/__init__.py | 0 examples/vim/powerline.py | 113 ++++++++++++++++++ examples/vim/powerline.vim | 21 ++++ powerline-vim-example.vim | 89 -------------- 6 files changed, 138 insertions(+), 89 deletions(-) create mode 100644 examples/__init__.py rename powerline-terminal-example.py => examples/terminal/powerline.py (83%) create mode 100644 examples/vim/__init__.py create mode 100644 examples/vim/powerline.py create mode 100644 examples/vim/powerline.vim delete mode 100644 powerline-vim-example.vim diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline-terminal-example.py b/examples/terminal/powerline.py similarity index 83% rename from powerline-terminal-example.py rename to examples/terminal/powerline.py index 8ae52cfc..66cd497b 100755 --- a/powerline-terminal-example.py +++ b/examples/terminal/powerline.py @@ -2,6 +2,10 @@ '''Powerline terminal prompt example. ''' +import os +import sys +sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) + from lib.core import Powerline, Segment from lib.renderers import TerminalSegmentRenderer diff --git a/examples/vim/__init__.py b/examples/vim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py new file mode 100644 index 00000000..11066d86 --- /dev/null +++ b/examples/vim/powerline.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- + +import vim +import os +import re + +from lib.core import Powerline, Segment +from lib.renderers import VimSegmentRenderer + +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', +} + + +def statusline(): + winwidth = int(vim.bindeval('winwidth(0)')) + + # Prepare segment contents + mode = modes[vim.bindeval('mode()')] + + branch = vim.bindeval('fugitive#head(5)') + if branch: + branch = '⭠ ' + branch + + line_current = int(vim.bindeval('line(".")')) + line_end = int(vim.bindeval('line("$")')) + line_percent = int(float(line_current) / float(line_end) * 100) + + # 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.bindeval('col(".")') + + filepath = os.path.split(vim.bindeval('expand("%:~:.")')) + filename_color = 231 + if filepath[0]: + filepath[0] += os.sep + + if not filepath[1]: + filepath = ('', '[No Name]') + filename_color = 250 + + readonly = vim.bindeval('&ro ? "⭤ " : ""') + modified = vim.bindeval('&mod ? " +" : ""') + + currenttag = vim.bindeval('tagbar#currenttag("%s", "")') + + # The Syntastic segment is center aligned (filler segment on each side) to show off how the filler segments work + # Not necessarily how it's going to look in the final theme + vim.command('let g:syntastic_stl_format = "⮃ %E{ ERRORS (%e) ⭡ %fe }%W{ WARNINGS (%w) ⭡ %fw } ⮁"') + syntastic = vim.bindeval('SyntasticStatuslineFlag()') + + powerline = Powerline([ + Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), + Segment(vim.bindeval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), + Segment(branch, 250, 240, priority=10), + Segment(readonly, 196, 240, draw_divider=False), + Segment(filepath[0], 250, 240, draw_divider=False, priority=5), + Segment(filepath[1], filename_color, 240, attr=Segment.ATTR_BOLD, draw_divider=not len(modified)), + Segment(modified, 220, 240, attr=Segment.ATTR_BOLD), + Segment(currenttag, 246, 236, draw_divider=False, priority=100), + Segment(filler=True, fg=236, bg=236), + Segment(syntastic, 214, 236, attr=Segment.ATTR_BOLD, draw_divider=False, priority=100), + Segment(filler=True, fg=236, bg=236), + Segment(vim.bindeval('&ff'), 247, 236, side='r', priority=50), + Segment(vim.bindeval('&fenc'), 247, 236, side='r', priority=50), + Segment(vim.bindeval('&ft'), 247, 236, side='r', priority=50), + Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), + Segment('⭡ ', 239, 252, side='r'), + Segment(str(line_current).rjust(3), 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), + Segment(':' + str(col_current).ljust(2), 244, 252, side='r', priority=30, draw_divider=False), + ]) + + renderer = VimSegmentRenderer() + stl = powerline.render(renderer, winwidth) + + # Escape percent chars in the statusline, but only if they aren't part of any stl escape sequence + stl = re.sub('(\w+)\%(?![-{()<=#*%])', '\\1%%', stl) + + # Create highlighting groups + for group, hl in renderer.hl_groups.items(): + if int(vim.bindeval('hlexists("{0}")'.format(group))): + # Only create hl group if it doesn't already exist + continue + + vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( + group=group, + 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']), + )) + + return stl diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim new file mode 100644 index 00000000..469c3b32 --- /dev/null +++ b/examples/vim/powerline.vim @@ -0,0 +1,21 @@ +" Powerline vim example +" Run with :source % + +python import sys, vim, os +python sys.path.append(vim.eval('expand(":h:h:h")')) +python from examples.vim.powerline import statusline + +if exists('*pyeval') + let s:pyeval=function('pyeval') +else + python import json + function s:pyeval(e) + python vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) + endfunction +endif + +function! DynStl() + return s:pyeval('statusline()') +endfunction + +set stl=%!DynStl() diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim deleted file mode 100644 index 7f2da064..00000000 --- a/powerline-vim-example.vim +++ /dev/null @@ -1,89 +0,0 @@ -" Powerline vim example -" Run with :source % - -set stl=%!DynStl() - -function! DynStl() -python < Date: Fri, 16 Nov 2012 17:16:30 +0100 Subject: [PATCH 0022/1472] Fix filename issues in vim example --- examples/vim/powerline.py | 14 +++++++------- examples/vim/powerline.vim | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 11066d86..d267a5ef 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -49,13 +49,13 @@ def statusline(): col_current = vim.bindeval('col(".")') - filepath = os.path.split(vim.bindeval('expand("%:~:.")')) + filepath, filename = os.path.split(vim.bindeval('expand("%:~:.")')) filename_color = 231 - if filepath[0]: - filepath[0] += os.sep + if filepath: + filepath += os.sep - if not filepath[1]: - filepath = ('', '[No Name]') + if not filename: + filename = '[No Name]' filename_color = 250 readonly = vim.bindeval('&ro ? "⭤ " : ""') @@ -73,8 +73,8 @@ def statusline(): Segment(vim.bindeval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), Segment(branch, 250, 240, priority=10), Segment(readonly, 196, 240, draw_divider=False), - Segment(filepath[0], 250, 240, draw_divider=False, priority=5), - Segment(filepath[1], filename_color, 240, attr=Segment.ATTR_BOLD, draw_divider=not len(modified)), + Segment(filepath, 250, 240, draw_divider=False, priority=5), + Segment(filename, filename_color, 240, attr=Segment.ATTR_BOLD, draw_divider=not len(modified)), Segment(modified, 220, 240, attr=Segment.ATTR_BOLD), Segment(currenttag, 246, 236, draw_divider=False, priority=100), Segment(filler=True, fg=236, bg=236), diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 469c3b32..d3b86b12 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -15,7 +15,7 @@ else endif function! DynStl() - return s:pyeval('statusline()') + return s:pyeval('statusline()') endfunction set stl=%!DynStl() From 708306c6bae453dcdd2cd68e4facd2d404a00381 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Nov 2012 13:58:28 +0359 Subject: [PATCH 0023/1472] Avoid invoking vim parser as much as possible in case vim is recent enough --- examples/vim/powerline.py | 102 ++++++++++++++++++++++++++++++------- examples/vim/powerline.vim | 2 +- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index d267a5ef..76044b17 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -28,28 +28,78 @@ modes = { '!': 'SHELL', } +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 + + def get_vim_func(f): + return VimFunc(f) + + def set_global_var(var, val): + 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'), + 'sstlflag' : get_vim_func('SyntasticStatuslineFlag'), + 'hlexists' : get_vim_func('hlexists', rettype=int), +} def statusline(): - winwidth = int(vim.bindeval('winwidth(0)')) + winwidth = vim_funcs['winwidth'](0) # Prepare segment contents - mode = modes[vim.bindeval('mode()')] + mode = modes[vim_funcs['mode']()] - branch = vim.bindeval('fugitive#head(5)') + try: + branch = vim_funcs['fghead'](5) + except vim.error: + vim_funcs['fghead'] = None + branch = '' + except TypeError: + branch = '' if branch: branch = '⭠ ' + branch - line_current = int(vim.bindeval('line(".")')) - line_end = int(vim.bindeval('line("$")')) - line_percent = int(float(line_current) / float(line_end) * 100) + line_current = vim_funcs['line']('.') + line_end = vim_funcs['line']('$') + line_percent = line_current * 100 // line_end # 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.bindeval('col(".")') + col_current = vim_funcs['col']('.') - filepath, filename = os.path.split(vim.bindeval('expand("%:~:.")')) + filepath, filename = os.path.split(vim_funcs['expand']('%:~:.')) filename_color = 231 if filepath: filepath += os.sep @@ -58,19 +108,31 @@ def statusline(): filename = '[No Name]' filename_color = 250 - readonly = vim.bindeval('&ro ? "⭤ " : ""') - modified = vim.bindeval('&mod ? " +" : ""') + readonly = vim.eval('&ro ? "⭤ " : ""') + modified = vim.eval('&mod ? " +" : ""') - currenttag = vim.bindeval('tagbar#currenttag("%s", "")') + try: + currenttag = vim_funcs['tbcurtag']('%s', '') + except vim.error: + vim_funcs['tbcurtag'] = None + currenttag = '' + except TypeError: + currenttag = '' # The Syntastic segment is center aligned (filler segment on each side) to show off how the filler segments work # Not necessarily how it's going to look in the final theme - vim.command('let g:syntastic_stl_format = "⮃ %E{ ERRORS (%e) ⭡ %fe }%W{ WARNINGS (%w) ⭡ %fw } ⮁"') - syntastic = vim.bindeval('SyntasticStatuslineFlag()') + set_global_var('syntastic_stl_format', '⮃ %E{ ERRORS (%e) ⭡ %fe }%W{ WARNINGS (%w) ⭡ %fw } ⮁') + try: + syntastic = vim_funcs['sstlflag']() + except vim.error: + vim_funcs['sstlflag'] = None + syntastic = '' + except TypeError: + syntastic = '' powerline = Powerline([ Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), - Segment(vim.bindeval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), + Segment(vim.eval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), Segment(branch, 250, 240, priority=10), Segment(readonly, 196, 240, draw_divider=False), Segment(filepath, 250, 240, draw_divider=False, priority=5), @@ -80,9 +142,9 @@ def statusline(): Segment(filler=True, fg=236, bg=236), Segment(syntastic, 214, 236, attr=Segment.ATTR_BOLD, draw_divider=False, priority=100), Segment(filler=True, fg=236, bg=236), - Segment(vim.bindeval('&ff'), 247, 236, side='r', priority=50), - Segment(vim.bindeval('&fenc'), 247, 236, side='r', priority=50), - Segment(vim.bindeval('&ft'), 247, 236, side='r', priority=50), + Segment(vim.eval('&ff'), 247, 236, side='r', priority=50), + Segment(vim.eval('&fenc'), 247, 236, side='r', priority=50), + Segment(vim.eval('&ft'), 247, 236, side='r', priority=50), Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), Segment('⭡ ', 239, 252, side='r'), Segment(str(line_current).rjust(3), 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), @@ -97,7 +159,7 @@ def statusline(): # Create highlighting groups for group, hl in renderer.hl_groups.items(): - if int(vim.bindeval('hlexists("{0}")'.format(group))): + if vim_funcs['hlexists'](group): # Only create hl group if it doesn't already exist continue @@ -111,3 +173,7 @@ def statusline(): )) return stl + +statusline() + +# vim: ft=python ts=4 sts=4 sw=4 noet diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index d3b86b12..84cdefd5 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -9,7 +9,7 @@ if exists('*pyeval') let s:pyeval=function('pyeval') else python import json - function s:pyeval(e) + function! s:pyeval(e) python vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) endfunction endif From 0f6d24b1e87bcd054cdebcfc3e9f08b8d64bae9b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Nov 2012 14:08:38 +0359 Subject: [PATCH 0024/1472] Made it work on older vims --- examples/vim/powerline.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 76044b17..7f4d937e 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -55,8 +55,7 @@ else: return self.rettype(r) return r - def get_vim_func(f): - return VimFunc(f) + get_vim_func = VimFunc def set_global_var(var, val): vim.command('let g:{0}={1}'.format(var, json.dumps(val))) From 4cb0559be45fe03b74c50b4ff24a1d1096f93157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 17 Nov 2012 12:21:24 +0100 Subject: [PATCH 0025/1472] Fix whitespace issues so pyflakes doesn't complain --- examples/vim/powerline.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 7f4d937e..9da9973e 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -33,7 +33,7 @@ if hasattr(vim, 'bindeval'): def get_vim_func(f, rettype=None): try: - return vim.bindeval('function("'+f+'")') + return vim.bindeval('function("' + f + '")') except vim.error: return None @@ -43,35 +43,38 @@ if hasattr(vim, 'bindeval'): vim_globals[var] = val else: import json + class VimFunc(object): - __slots__ = ('f','rettype') + __slots__ = ('f', 'rettype') + def __init__(self, f, rettype=None): - self.f = f + self.f = f self.rettype = rettype def __call__(self, *args): - r = vim.eval(self.f+'('+json.dumps(args)[1:-1]+')') + 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): + 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'), - 'sstlflag' : get_vim_func('SyntasticStatuslineFlag'), - 'hlexists' : get_vim_func('hlexists', rettype=int), + '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'), + 'sstlflag': get_vim_func('SyntasticStatuslineFlag'), + 'hlexists': get_vim_func('hlexists', rettype=int), } + def statusline(): winwidth = vim_funcs['winwidth'](0) @@ -89,7 +92,7 @@ def statusline(): branch = '⭠ ' + branch line_current = vim_funcs['line']('.') - line_end = vim_funcs['line']('$') + line_end = vim_funcs['line']('$') line_percent = line_current * 100 // line_end # Fun gradient colored percent segment From 90763f1fec0cad8b08e05aed85ed616ae379fe30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 17 Nov 2012 12:35:22 +0100 Subject: [PATCH 0026/1472] Improve unicode handling of rendered segments --- lib/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core.py b/lib/core.py index 26950054..1024114d 100644 --- a/lib/core.py +++ b/lib/core.py @@ -93,8 +93,8 @@ class Powerline: ) return { - 'highlighted': rendered_highlighted, - 'raw': rendered_raw, + 'highlighted': rendered_highlighted.decode('utf-8'), + 'raw': rendered_raw.decode('utf-8'), } rendered = render_segments(self.segments) @@ -106,7 +106,7 @@ class Powerline: # 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 len(rendered['raw'].decode('utf-8')) > width and len(segments_priority): + while len(rendered['raw']) > width and len(segments_priority): self.segments.remove(segments_priority[0]) segments_priority.pop(0) @@ -115,7 +115,7 @@ class Powerline: # 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_contents = ' ' * int((width - len(rendered['raw'].decode('utf-8'))) / len(segments_fillers)) + segments_fillers_contents = ' ' * int((width - len(rendered['raw'])) / len(segments_fillers)) for segment in segments_fillers: segment.contents = segments_fillers_contents From 08695540dafc93cc6c91507f2acd991e1cf714dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 17 Nov 2012 12:35:38 +0100 Subject: [PATCH 0027/1472] Handle remainder whitespace for filler segments When more than one filler segment is specified it may sometimes be a remainder when calculating the amount of whitespace. divmod() is used to retrieve the remainder, which is added as whitespace to the first filler segment. --- lib/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core.py b/lib/core.py index 1024114d..427ad172 100644 --- a/lib/core.py +++ b/lib/core.py @@ -115,9 +115,12 @@ class Powerline: # 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_contents = ' ' * int((width - len(rendered['raw'])) / len(segments_fillers)) + segments_fillers_len, segments_fillers_remainder = divmod((width - len(rendered['raw'])), 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 # Do a final render now that we have handled the cropping and padding rendered = render_segments(self.segments, render_raw=False) From a532da48349c645464b97fb618b83799269a6b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 17 Nov 2012 16:42:28 +0100 Subject: [PATCH 0028/1472] Fix issues with the terminal prompt example --- examples/terminal/powerline.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/terminal/powerline.py b/examples/terminal/powerline.py index 66cd497b..0e6ab9fc 100755 --- a/examples/terminal/powerline.py +++ b/examples/terminal/powerline.py @@ -1,10 +1,11 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- '''Powerline terminal prompt example. ''' import os import sys -sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) from lib.core import Powerline, Segment from lib.renderers import TerminalSegmentRenderer From 6c5316a05807fb91453fe107733b9c40011b2a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Nov 2012 21:16:12 +0100 Subject: [PATCH 0029/1472] Make vim example work with split and inactive windows This commit introduces a bunch of changes. Most importantly, it ensures that split windows and inactive windows work as expected. This is accomplished with some clever workarounds for vim's statusline limitations. Vim statuslines evaluated using the %! statusline item are always evaluated in the buffer context and not in the statusline's owner window's context, and this made the previous implementation show the same statusline for all windows. The only way to ensure that the correct statusline is rendered to the correct window is to walk through all windows, and setting the statuslines with a reference to the current window number every time the current window is changed. This is accomplished by a set of BufEnter, BufLeave, etc. autocommands. The Syntastic segment has been temporarily removed due to errors when referencing the statusline function before Syntastic has been loaded. --- examples/vim/powerline.py | 156 ++++++++++++++++++++----------------- examples/vim/powerline.vim | 20 ++++- 2 files changed, 101 insertions(+), 75 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 9da9973e..f4951744 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -70,87 +70,103 @@ vim_funcs = { 'col': get_vim_func('col', rettype=int), 'expand': get_vim_func('expand'), 'tbcurtag': get_vim_func('tagbar#currenttag'), - 'sstlflag': get_vim_func('SyntasticStatuslineFlag'), 'hlexists': get_vim_func('hlexists', rettype=int), } +getwinvar = get_vim_func('getwinvar') +setwinvar = get_vim_func('setwinvar') -def statusline(): - winwidth = vim_funcs['winwidth'](0) - # Prepare segment contents +def statusline(winnr): + winwidth = vim_funcs['winwidth'](winnr) + + current = getwinvar(winnr, 'current') + windata = getwinvar(winnr, 'powerline') + + if current: + # 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 + + try: + branch = vim_funcs['fghead'](5) + except vim.error: + vim_funcs['fghead'] = None + branch = '' + except TypeError: + branch = '' + if branch: + branch = '⭠ ' + 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" : ""'), + '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) + '%', + 'line_percent_color': line_percent_color, + 'linecurrent': str(line_current).rjust(3), + 'colcurrent': ':' + str(col_current).ljust(2), + } + + setwinvar(winnr, 'powerline', windata) + mode = modes[vim_funcs['mode']()] - try: - branch = vim_funcs['fghead'](5) - except vim.error: - vim_funcs['fghead'] = None - branch = '' - except TypeError: - branch = '' - if branch: - branch = '⭠ ' + branch - - line_current = vim_funcs['line']('.') - line_end = vim_funcs['line']('$') - line_percent = line_current * 100 // line_end - - # 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 = '' - - # The Syntastic segment is center aligned (filler segment on each side) to show off how the filler segments work - # Not necessarily how it's going to look in the final theme - set_global_var('syntastic_stl_format', '⮃ %E{ ERRORS (%e) ⭡ %fe }%W{ WARNINGS (%w) ⭡ %fw } ⮁') - try: - syntastic = vim_funcs['sstlflag']() - except vim.error: - vim_funcs['sstlflag'] = None - syntastic = '' - except TypeError: - syntastic = '' + if not current: + mode = None powerline = Powerline([ Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), - Segment(vim.eval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), - Segment(branch, 250, 240, priority=10), - Segment(readonly, 196, 240, draw_divider=False), - Segment(filepath, 250, 240, draw_divider=False, priority=5), - Segment(filename, filename_color, 240, attr=Segment.ATTR_BOLD, draw_divider=not len(modified)), - Segment(modified, 220, 240, attr=Segment.ATTR_BOLD), - Segment(currenttag, 246, 236, draw_divider=False, priority=100), + Segment(windata['paste'], 231, 166, attr=Segment.ATTR_BOLD), + Segment(windata['branch'], 250, 240, priority=10), + Segment(windata['readonly'], 196, 240, draw_divider=False), + Segment(windata['filepath'], 250, 240, draw_divider=False, priority=5), + Segment(windata['filename'], windata['filename_color'], 240, attr=Segment.ATTR_BOLD, draw_divider=not len(windata['modified'])), + Segment(windata['modified'], 220, 240, attr=Segment.ATTR_BOLD), + Segment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), Segment(filler=True, fg=236, bg=236), - Segment(syntastic, 214, 236, attr=Segment.ATTR_BOLD, draw_divider=False, priority=100), - Segment(filler=True, fg=236, bg=236), - Segment(vim.eval('&ff'), 247, 236, side='r', priority=50), - Segment(vim.eval('&fenc'), 247, 236, side='r', priority=50), - Segment(vim.eval('&ft'), 247, 236, side='r', priority=50), - Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), + Segment(windata['fileformat'], 247, 236, side='r', priority=50), + Segment(windata['fileencoding'], 247, 236, side='r', priority=50), + Segment(windata['filetype'], 247, 236, side='r', priority=50), + Segment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30), Segment('⭡ ', 239, 252, side='r'), - Segment(str(line_current).rjust(3), 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), - Segment(':' + str(col_current).ljust(2), 244, 252, side='r', priority=30, draw_divider=False), + Segment(windata['linecurrent'], 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), + Segment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), ]) renderer = VimSegmentRenderer() @@ -176,6 +192,4 @@ def statusline(): return stl -statusline() - # vim: ft=python ts=4 sts=4 sw=4 noet diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 84cdefd5..4babdcb8 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -6,7 +6,7 @@ python sys.path.append(vim.eval('expand(":h:h:h")')) python from examples.vim.powerline import statusline if exists('*pyeval') - let s:pyeval=function('pyeval') + let s:pyeval = function('pyeval') else python import json function! s:pyeval(e) @@ -14,8 +14,20 @@ else endfunction endif -function! DynStl() - return s:pyeval('statusline()') +function! Powerline(winnr) + return s:pyeval('statusline('. a:winnr .')') endfunction -set stl=%!DynStl() +function! s:WinDoPowerline() + if ! exists('w:powerline') + let w:powerline = {} + endif + + let &l:stl = '%!Powerline('. winnr() .')' +endfunction + +augroup Powerline + autocmd! + autocmd BufEnter,BufWinEnter,WinEnter * let w:current = 1 | let currwin = winnr() | windo call s:WinDoPowerline() | exec currwin . 'wincmd w' + autocmd BufLeave,BufWinLeave,WinLeave * let w:current = 0 +augroup END From f4e3d01d07ae4396ed09f1ce04f7434511a3c42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Nov 2012 09:57:16 +0100 Subject: [PATCH 0030/1472] Improve rendering performance This commit almost doubles the segment rendering performance. This is accomplished by caching a lot of data like highlighting groups, moving some calculations out of loops, and by performing less function calls overall. When a width is specified the main speed improvement comes from avoiding rendering the raw segments over and over until the statusline is short enough. Instead, the raw rendering is stored as a segment property and the combined length of all these renderings is used when removing low-priority segments instead. This results in a maximum of two rendering passes. Some "less pythonic" solutions have been chosen some places for performance reasons, e.g. joining strings instead of appending and joining lists. Overall this commit appears to make the performance equal or better than the legacy vimscript implementation. Later optimizations (in particular finding another method than remove() for removing low-priority segments) may make this version of Powerline far superior both in terms of functionality and performance. --- examples/vim/powerline.py | 6 +-- lib/colors.py | 96 +++++++++++++++------------------ lib/core.py | 110 ++++++++++++++++++++------------------ lib/renderers/vim.py | 63 +++++++++++----------- 4 files changed, 134 insertions(+), 141 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index f4951744..435392ee 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -176,13 +176,13 @@ def statusline(winnr): stl = re.sub('(\w+)\%(?![-{()<=#*%])', '\\1%%', stl) # Create highlighting groups - for group, hl in renderer.hl_groups.items(): - if vim_funcs['hlexists'](group): + for idx, hl in renderer.hl_groups.items(): + if vim_funcs['hlexists'](hl['name']): # Only create hl group if it doesn't already exist continue vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( - group=group, + group=hl['name'], ctermfg=hl['ctermfg'], guifg='#{0:06x}'.format(hl['guifg']) if hl['guifg'] != 'NONE' else 'NONE', ctermbg=hl['ctermbg'], diff --git a/lib/colors.py b/lib/colors.py index fb128762..c0a34e45 100644 --- a/lib/colors.py +++ b/lib/colors.py @@ -1,54 +1,42 @@ -def cterm_to_hex(cterm_color): - '''Translate a cterm color index into the corresponding hex/RGB color. - ''' - color_dict = { - 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, - 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, - 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, - 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, - 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, - 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, - 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, - 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, - 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, - 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, - 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, - 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, - 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, - 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, - 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, - 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, - 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, - 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, - 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, - 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, - 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, - 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, - 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, - 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, - 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, - 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, - 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, - 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, - 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, - 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, - 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, - 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, - 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, - 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, - 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, - 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, - 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, - 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, - 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, - 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, - } - if not cterm_color: - return None - - try: - return color_dict[cterm_color] - except KeyError: - import sys - sys.stderr.write('Invalid cterm color index: {0}\n'.format(cterm_color)) - return None +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, + 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, + 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, + 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, + 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, + 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, + 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, + 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, + 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, + 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, + 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, + 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, + 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, + 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, + 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, + 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, + 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, + 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, + 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, + 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, + 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, + 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, + 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, + 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, + 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, + 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, + 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, + 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, + 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, + 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, + 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, + 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, + 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, + 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, + 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, + 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, + 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, + 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, + 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, +} diff --git a/lib/core.py b/lib/core.py index 427ad172..25b57a91 100644 --- a/lib/core.py +++ b/lib/core.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +from lib.colors import cterm_to_hex + class Powerline: dividers = { @@ -20,6 +22,7 @@ class Powerline: 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. @@ -34,7 +37,7 @@ class Powerline: provided they will fill the remaining space until the desired width is reached. ''' - def render_segments(segments, render_raw=True, render_highlighted=True): + def render_segments(segments, render_highlighted=True): '''Render a segment array. By default this function renders both raw (un-highlighted segments @@ -42,90 +45,94 @@ class Powerline: rendering is used for calculating the total width for dropping low-priority segments. ''' - rendered_raw = '' rendered_highlighted = '' + segments_len = len(segments) + empty_segment = Segment() for idx, segment in enumerate(segments): - prev = segments[idx - 1] if idx > 0 else Segment() - next = segments[idx + 1] if idx < len(segments) - 1 else Segment() + prev = segments[idx - 1] if idx > 0 else empty_segment + next = segments[idx + 1] if idx < segments_len - 1 else empty_segment - compare_segment = next if segment.side == 'l' else prev - divider_type = 'soft' if compare_segment.bg == segment.bg else 'hard' + 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 - segment_format = '{contents}' - elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare_segment.side): + rendered_highlighted += segment.contents + elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare.side): # Draw divider if specified, and if the next segment is on # the opposite side only draw the divider if it's a hard # divider if segment.side == 'l': - segment_format = '{segment_hl}{outer_padding}{contents} {divider_hl}{divider} ' + segment.rendered_raw += outer_padding + segment.contents + ' ' + divider + ' ' + rendered_highlighted += segment_hl + outer_padding + segment.contents + ' ' + divider_hl + divider + ' ' else: - segment_format = ' {divider_hl}{divider}{segment_hl} {contents}{outer_padding}' + 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 - segment_format = '{segment_hl}{contents}{outer_padding}' + 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 - if render_raw is True and segment.filler is False: - # Filler segments must be empty when used e.g. in vim (the - # %=%< segment which disappears), so they will be skipped - # when calculating the width using the raw rendering - rendered_raw += segment_format.format( - divider=divider, - contents=segment.contents, - divider_hl='', - segment_hl='', - outer_padding=' ' if idx == 0 or idx == len(segments) - 1 else '', - ) + return rendered_highlighted.decode('utf-8') - if render_highlighted is True: - rendered_highlighted += segment_format.format( - divider=divider, - contents=segment.contents, - divider_hl='' if divider_type == 'soft' else renderer.hl(segment.bg, compare_segment.bg), - segment_hl=renderer.hl(segment.fg, segment.bg, segment.attr), - outer_padding=' ' if idx == 0 or idx == len(segments) - 1 else '', - ) - - return { - 'highlighted': rendered_highlighted.decode('utf-8'), - 'raw': rendered_raw.decode('utf-8'), - } - - rendered = render_segments(self.segments) + 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'] + 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 len(rendered['raw']) > width and len(segments_priority): + while self._total_len() > width and len(segments_priority): + # FIXME The remove method is quite expensive and we should find another way of removing low-priority segments self.segments.remove(segments_priority[0]) segments_priority.pop(0) - rendered = render_segments(self.segments, render_highlighted=False) - # 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 - len(rendered['raw'])), len(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 - # Do a final render now that we have handled the cropping and padding - rendered = render_segments(self.segments, render_raw=False) + return render_segments(self.segments) - return rendered['highlighted'] + 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]).decode('utf-8')) class Segment: @@ -144,23 +151,20 @@ class Segment: self.draw_divider = draw_divider self.priority = priority self.filler = filler + self.rendered_raw = '' if self.filler: # Filler segments should never have any dividers self.draw_divider = False try: - if len(self.fg) != 2: - raise TypeError + self.fg = (fg[0], fg[1]) except TypeError: # Only the terminal color is defined, so we need to get the hex color - from lib.colors import cterm_to_hex - self.fg = [self.fg, cterm_to_hex(self.fg)] + self.fg = (self.fg, cterm_to_hex.get(self.fg, 0xffffff)) try: - if len(self.bg) != 2: - raise TypeError + self.bg = (bg[0], bg[1]) except TypeError: # Only the terminal color is defined, so we need to get the hex color - from lib.colors import cterm_to_hex - self.bg = [self.bg, cterm_to_hex(self.bg)] + self.bg = (self.bg, cterm_to_hex.get(self.bg, 0x000000)) diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py index 15f1ef9c..b91fd96c 100644 --- a/lib/renderers/vim.py +++ b/lib/renderers/vim.py @@ -17,43 +17,44 @@ class VimSegmentRenderer(SegmentRenderer): False, the argument is reset to the terminal defaults. If an argument is a valid color or attribute, it's added to the vim highlight group. ''' - hl_group = { - 'ctermfg': 'NONE', - 'guifg': 'NONE', - 'ctermbg': 'NONE', - 'guibg': 'NONE', - 'attr': ['NONE'], - } - # We don't need to explicitly reset attributes in vim, so skip those calls if not attr and not bg and not fg: return '' - if fg is not None and fg is not False: - hl_group['ctermfg'] = fg[0] - hl_group['guifg'] = fg[1] + if not (fg, bg, attr) in self.hl_groups: + hl_group = { + 'ctermfg': 'NONE', + 'guifg': 'NONE', + 'ctermbg': 'NONE', + 'guibg': 'NONE', + 'attr': ['NONE'], + 'name': '', + } - if bg is not None and bg is not False: - hl_group['ctermbg'] = bg[0] - hl_group['guibg'] = bg[1] + if fg is not None and fg is not False: + hl_group['ctermfg'] = fg[0] + hl_group['guifg'] = fg[1] - if attr is not None and attr is not False and attr != 0: - hl_group['attr'] = [] - if attr & Segment.ATTR_BOLD: - hl_group['attr'].append('bold') - if attr & Segment.ATTR_ITALIC: - hl_group['attr'].append('italic') - if attr & Segment.ATTR_UNDERLINE: - hl_group['attr'].append('underline') + if bg is not None and bg is not False: + hl_group['ctermbg'] = bg[0] + hl_group['guibg'] = bg[1] - hl_group_name = 'Pl_{ctermfg}_{guifg}_{ctermbg}_{guibg}_{attr}'.format( - ctermfg=hl_group['ctermfg'], - guifg=hl_group['guifg'], - ctermbg=hl_group['ctermbg'], - guibg=hl_group['guibg'], - attr=''.join(attr[0] for attr in hl_group['attr']), - ) + if attr: + hl_group['attr'] = [] + if attr & Segment.ATTR_BOLD: + hl_group['attr'].append('bold') + if attr & Segment.ATTR_ITALIC: + hl_group['attr'].append('italic') + if attr & Segment.ATTR_UNDERLINE: + hl_group['attr'].append('underline') - self.hl_groups[hl_group_name] = hl_group + hl_group['name'] = 'Pl_' + \ + str(hl_group['ctermfg']) + '_' + \ + str(hl_group['guifg']) + '_' + \ + str(hl_group['ctermbg']) + '_' + \ + str(hl_group['guibg']) + '_' + \ + ''.join(hl_group['attr']) - return '%#{0}#'.format(hl_group_name) + self.hl_groups[(fg, bg, attr)] = hl_group + + return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' From 1d3c2590706ad770fd290aa94c98aa89ffac4d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Nov 2012 11:33:10 +0100 Subject: [PATCH 0031/1472] Improve rendering performance This change removes the Segment class as this takes forever to remove from the segment array when removing low-priority segments. It has instead been replaced by a wrapper function that works the same and returns a working dict of all segment properties. The regex substitution bottleneck in the vim example has been fixed by using a single-character percent placeholder in vim segments which is later replaced with a double percent using str.replace(). --- examples/terminal/powerline.py | 2 +- examples/vim/powerline.py | 43 ++++++------ lib/core.py | 121 +++++++++++++++------------------ lib/renderers/terminal.py | 4 +- lib/renderers/vim.py | 8 +-- 5 files changed, 85 insertions(+), 93 deletions(-) diff --git a/examples/terminal/powerline.py b/examples/terminal/powerline.py index 0e6ab9fc..d16cdb4f 100755 --- a/examples/terminal/powerline.py +++ b/examples/terminal/powerline.py @@ -5,7 +5,7 @@ import os import sys -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from lib.core import Powerline, Segment from lib.renderers import TerminalSegmentRenderer diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 435392ee..742f5a28 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -4,7 +4,7 @@ import vim import os import re -from lib.core import Powerline, Segment +from lib.core import Powerline, mksegment from lib.renderers import VimSegmentRenderer modes = { @@ -28,6 +28,9 @@ modes = { '!': 'SHELL', } +# We need to replace this private use glyph with a double-percent later +percent_placeholder = ''.decode('utf-8') + if hasattr(vim, 'bindeval'): # This branch is used to avoid invoking vim parser as much as possible @@ -137,7 +140,7 @@ def statusline(winnr): 'fileformat': vim.eval('&ff'), 'fileencoding': vim.eval('&fenc'), 'filetype': vim.eval('&ft'), - 'line_percent': str(line_percent).rjust(3) + '%', + '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), @@ -151,29 +154,29 @@ def statusline(winnr): mode = None powerline = Powerline([ - Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), - Segment(windata['paste'], 231, 166, attr=Segment.ATTR_BOLD), - Segment(windata['branch'], 250, 240, priority=10), - Segment(windata['readonly'], 196, 240, draw_divider=False), - Segment(windata['filepath'], 250, 240, draw_divider=False, priority=5), - Segment(windata['filename'], windata['filename_color'], 240, attr=Segment.ATTR_BOLD, draw_divider=not len(windata['modified'])), - Segment(windata['modified'], 220, 240, attr=Segment.ATTR_BOLD), - Segment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), - Segment(filler=True, fg=236, bg=236), - Segment(windata['fileformat'], 247, 236, side='r', priority=50), - Segment(windata['fileencoding'], 247, 236, side='r', priority=50), - Segment(windata['filetype'], 247, 236, side='r', priority=50), - Segment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30), - Segment('⭡ ', 239, 252, side='r'), - Segment(windata['linecurrent'], 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), - Segment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), + mksegment(mode, 22, 148, attr=Powerline.ATTR_BOLD), + mksegment(windata['paste'], 231, 166, attr=Powerline.ATTR_BOLD), + mksegment(windata['branch'], 250, 240, priority=10), + mksegment(windata['readonly'], 196, 240, draw_divider=False), + mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=5), + mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=not len(windata['modified'])), + mksegment(windata['modified'], 220, 240, attr=Powerline.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), + 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=Powerline.ATTR_BOLD, side='r', draw_divider=False), + mksegment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), ]) renderer = VimSegmentRenderer() stl = powerline.render(renderer, winwidth) - # Escape percent chars in the statusline, but only if they aren't part of any stl escape sequence - stl = re.sub('(\w+)\%(?![-{()<=#*%])', '\\1%%', stl) + # Replace percent placeholders + stl = stl.replace(percent_placeholder, '%%') # Create highlighting groups for idx, hl in renderer.hl_groups.items(): diff --git a/lib/core.py b/lib/core.py index 25b57a91..c0baf222 100644 --- a/lib/core.py +++ b/lib/core.py @@ -3,15 +3,19 @@ from lib.colors import cterm_to_hex -class Powerline: +class Powerline(object): + ATTR_BOLD = 1 + ATTR_ITALIC = 2 + ATTR_UNDERLINE = 4 + dividers = { 'l': { - 'hard': '⮀', - 'soft': '⮁', + 'hard': u'⮀', + 'soft': u'⮁', }, 'r': { - 'hard': '⮂', - 'soft': '⮃', + 'hard': u'⮂', + 'soft': u'⮃', }, } @@ -21,7 +25,7 @@ class Powerline: Segments that have empty contents and aren't filler segments 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'] or segment['filler']] self._hl = {} def render(self, renderer, width=None): @@ -45,60 +49,60 @@ class Powerline: rendering is used for calculating the total width for dropping low-priority segments. ''' - rendered_highlighted = '' + rendered_highlighted = u'' segments_len = len(segments) - empty_segment = Segment() + 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 - compare = next if segment.side == 'l' else prev + 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_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) + 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) + 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: + if segment['filler']: # Filler segments shouldn't be padded - rendered_highlighted += segment.contents - elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare.side): + rendered_highlighted += segment['contents'] + elif segment['draw_divider'] and (divider_type == 'hard' or segment['side'] == compare['side']): # Draw divider if specified, and if the next segment is on # the opposite side only draw the divider if it's a hard # divider - if segment.side == 'l': - segment.rendered_raw += outer_padding + segment.contents + ' ' + divider + ' ' - rendered_highlighted += segment_hl + outer_padding + segment.contents + ' ' + divider_hl + divider + ' ' + 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: + 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 + 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 + 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.decode('utf-8') + return rendered_highlighted rendered_highlighted = render_segments(self.segments) @@ -107,22 +111,21 @@ class Powerline: 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] + 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): - # FIXME The remove method is quite expensive and we should find another way of removing low-priority segments self.segments.remove(segments_priority[0]) segments_priority.pop(0) # 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['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 + segment['contents'] = segments_fillers_contents # Add remainder whitespace to the first filler segment - segments_fillers[0].contents += ' ' * segments_fillers_remainder + segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder return render_segments(self.segments) @@ -132,39 +135,25 @@ class Powerline: 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]).decode('utf-8')) + return len(''.join([segment['rendered_raw'] for segment in self.segments])) -class Segment: - ATTR_BOLD = 1 - ATTR_ITALIC = 2 - ATTR_UNDERLINE = 4 +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'' - def __init__(self, contents=None, fg=False, bg=False, attr=False, side='l', draw_divider=True, priority=-1, filler=False): - '''Create a new Powerline segment. - ''' - self.contents = str(contents or '') - self.fg = fg - self.bg = bg - self.attr = attr - self.side = side - self.draw_divider = draw_divider - self.priority = priority - self.filler = filler - self.rendered_raw = '' - - if self.filler: - # Filler segments should never have any dividers - self.draw_divider = False - - try: - self.fg = (fg[0], fg[1]) - except TypeError: - # Only the terminal color is defined, so we need to get the hex color - self.fg = (self.fg, cterm_to_hex.get(self.fg, 0xffffff)) - - try: - self.bg = (bg[0], bg[1]) - except TypeError: - # Only the terminal color is defined, so we need to get the hex color - self.bg = (self.bg, cterm_to_hex.get(self.bg, 0x000000)) + 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, + 'rendered_raw': u'', + } diff --git a/lib/renderers/terminal.py b/lib/renderers/terminal.py index b80c37e1..ff55dff2 100644 --- a/lib/renderers/terminal.py +++ b/lib/renderers/terminal.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from lib.core import Segment +from lib.core import Powerline from lib.renderers import SegmentRenderer @@ -32,7 +32,7 @@ class TerminalSegmentRenderer(SegmentRenderer): if attr is False: ansi += [22] else: - if attr & Segment.ATTR_BOLD: + if attr & Powerline.ATTR_BOLD: ansi += [1] return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py index b91fd96c..07a4dbb2 100644 --- a/lib/renderers/vim.py +++ b/lib/renderers/vim.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from lib.core import Segment +from lib.core import Powerline from lib.renderers import SegmentRenderer @@ -41,11 +41,11 @@ class VimSegmentRenderer(SegmentRenderer): if attr: hl_group['attr'] = [] - if attr & Segment.ATTR_BOLD: + if attr & Powerline.ATTR_BOLD: hl_group['attr'].append('bold') - if attr & Segment.ATTR_ITALIC: + if attr & Powerline.ATTR_ITALIC: hl_group['attr'].append('italic') - if attr & Segment.ATTR_UNDERLINE: + if attr & Powerline.ATTR_UNDERLINE: hl_group['attr'].append('underline') hl_group['name'] = 'Pl_' + \ From addb7ccf7306e21a8b89e56617bd944abd0705b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Nov 2012 12:21:01 +0100 Subject: [PATCH 0032/1472] Fix various rendering issues Another rendering pass is necessary before calculating the filler segment's lengths, because center segments may lose their separators after removing low-priority segments. Some unicode and variable assignment issues has been resolved so everything renders correctly. --- examples/vim/powerline.py | 9 ++++----- lib/core.py | 5 ++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 742f5a28..dda44632 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -2,7 +2,6 @@ import vim import os -import re from lib.core import Powerline, mksegment from lib.renderers import VimSegmentRenderer @@ -29,7 +28,7 @@ modes = { } # We need to replace this private use glyph with a double-percent later -percent_placeholder = ''.decode('utf-8') +percent_placeholder = u'' if hasattr(vim, 'bindeval'): # This branch is used to avoid invoking vim parser as much as possible @@ -100,7 +99,7 @@ def statusline(winnr): except TypeError: branch = '' if branch: - branch = '⭠ ' + branch + branch = u'⭠ ' + branch # Fun gradient colored percent segment line_percent_gradient = [160, 166, 172, 178, 184, 190] @@ -156,9 +155,9 @@ def statusline(winnr): powerline = Powerline([ mksegment(mode, 22, 148, attr=Powerline.ATTR_BOLD), mksegment(windata['paste'], 231, 166, attr=Powerline.ATTR_BOLD), - mksegment(windata['branch'], 250, 240, priority=10), + mksegment(windata['branch'], 250, 240, priority=60), mksegment(windata['readonly'], 196, 240, draw_divider=False), - mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=5), + mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=40), mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=not len(windata['modified'])), mksegment(windata['modified'], 220, 240, attr=Powerline.ATTR_BOLD), mksegment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), diff --git a/lib/core.py b/lib/core.py index c0baf222..3d3e344d 100644 --- a/lib/core.py +++ b/lib/core.py @@ -57,6 +57,7 @@ class Powerline(object): 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' @@ -117,6 +118,9 @@ class Powerline(object): 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: @@ -155,5 +159,4 @@ def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg= 'draw_divider': False if filler else draw_divider, 'priority': priority, 'filler': filler, - 'rendered_raw': u'', } From 0f5a49783452ace968acdf9db083f8b30ef4b577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 23 Nov 2012 17:27:31 +0100 Subject: [PATCH 0033/1472] Add initial vim binding functions and segments Each segment is a function that can accept parameters like the branch or readonly symbol for overriding the defaults. --- vim/__init__.py | 0 vim/bindings.py | 47 +++++++++++++ vim/segments/__init__.py | 5 ++ vim/segments/core.py | 144 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 vim/__init__.py create mode 100644 vim/bindings.py create mode 100644 vim/segments/__init__.py create mode 100644 vim/segments/core.py diff --git a/vim/__init__.py b/vim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vim/bindings.py b/vim/bindings.py new file mode 100644 index 00000000..69d1db9b --- /dev/null +++ b/vim/bindings.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +import vim + +try: + _vim_globals = vim.bindeval('g:') + + def vim_set_global_var(var, val): + '''Set a global var in vim using bindeval(). + ''' + _vim_globals[var] = val + + def vim_get_func(f, rettype=None): + '''Return a vim function binding. + ''' + try: + return vim.bindeval('function("' + f + '")') + except vim.error: + return None +except AttributeError: + import json + + def vim_set_global_var(var, val): # NOQA + '''Set a global var in vim using vim.command(). + + This is a fallback function for older vim versions. + ''' + vim.command('let g:{0}={1}'.format(var, json.dumps(val))) + + class VimFunc(object): + '''Evaluate a vim function using vim.eval(). + + This is a fallback class for older vim versions. + ''' + __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 + + vim_get_func = VimFunc diff --git a/vim/segments/__init__.py b/vim/segments/__init__.py new file mode 100644 index 00000000..cf7d729a --- /dev/null +++ b/vim/segments/__init__.py @@ -0,0 +1,5 @@ +# 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/vim/segments/core.py b/vim/segments/core.py new file mode 100644 index 00000000..60d98046 --- /dev/null +++ b/vim/segments/core.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- + +import vim + +from bindings import vim_get_func + +vim_funcs = { + 'col': vim_get_func('col', rettype=int), + 'expand': vim_get_func('expand'), + 'line': vim_get_func('line', rettype=int), + 'mode': vim_get_func('mode'), + 'vcs': { + 'fugitive': vim_get_func('fugitive#head'), + }, +} + +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', +} + + +def mode(override=None): + '''Return the current vim mode. + + This function returns a tuple with the shorthand mode and the mode expanded + into a descriptive string. The longer string can be overridden by providing + a dict with the mode and the new string:: + + mode = mode({ 'n': 'NORM' }) + ''' + mode = vim_funcs['mode']() + + try: + return override[mode] + except TypeError: + return vim_modes[mode] + + +def modified_indicator(text=u'+'): + '''Return a file modified indicator. + ''' + return text if int(vim.eval('&modified')) else None + + +def paste_indicator(text='PASTE'): + '''Return a paste mode indicator. + ''' + return text if int(vim.eval('&paste')) else None + + +def readonly_indicator(text=u'⭤'): + '''Return a read-only indicator. + ''' + return text if int(vim.eval('&readonly')) else None + + +def branch(symbol=u'⭠'): + '''Return VCS branch. + + TODO: Expand this function to handle several VCS plugins. + ''' + branch = None + try: + branch = vim_funcs['vcs']['fugitive'](5) + except vim.error: + vim_funcs['vcs']['fugitive'] = None + except TypeError: + pass + + return symbol + ' ' + branch if branch else None + + +def file_directory(): + '''Return file directory (head component of the file path). + ''' + return vim_funcs['expand']('%:~:.:h') + + +def file_name(): + '''Return file name (tail component of the file path). + ''' + return vim_funcs['expand']('%:~:.:t') + + +def file_format(): + '''Return file format (i.e. line ending type). + + Returns None for unknown or missing file format. + ''' + return vim.eval('&fileformat') or None + + +def file_encoding(): + '''Return file encoding/character set. + + Returns None for unknown or missing file encoding. + ''' + return vim.eval('&fileencoding') or None + + +def file_type(): + '''Return file type. + + Returns None for unknown file types. + ''' + return vim.eval('&filetype') or None + + +def line_percent(): + '''Return the cursor position in the file as a percentage. + ''' + line_current = vim_funcs['line']('.') + line_last = vim_funcs['line']('$') + + return line_current * 100 // line_last + + +def line_current(): + '''Return the current cursor line. + ''' + return vim_funcs['line']('.') + + +def col_current(): + '''Return the current cursor column. + ''' + return vim_funcs['col']('.') From e18f3fab751a4c8315561ddcfb37eb1342e94605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 23 Nov 2012 19:03:58 +0100 Subject: [PATCH 0034/1472] Fix mode segment --- vim/segments/core.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vim/segments/core.py b/vim/segments/core.py index 60d98046..e5586d96 100644 --- a/vim/segments/core.py +++ b/vim/segments/core.py @@ -47,10 +47,13 @@ def mode(override=None): ''' mode = vim_funcs['mode']() + if not override: + return (mode, vim_modes[mode]) + try: - return override[mode] - except TypeError: - return vim_modes[mode] + return (mode, override[mode]) + except IndexError: + return (mode, vim_modes[mode]) def modified_indicator(text=u'+'): From 34da25418caa55cc31485b5bd78fd034e57f9f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 26 Nov 2012 09:17:28 +0100 Subject: [PATCH 0035/1472] Add short presentation to README --- README.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.rst b/README.rst index e69de29b..60ba2ae1 100644 --- a/README.rst +++ b/README.rst @@ -0,0 +1,29 @@ +========= +Powerline +========= + +:Author: Kim Silkebækken (kim.silkebaekken+vim@gmail.com) +:Source: https://github.com/Lokaltog/powerline + +**This is alpha software, expect things to change or break at any point.** + +This is the next version of Powerline, implemented in Python. It aims to +resolve some of the "unresolvable" problems of the vimscript implementation, +as well as providing a common code base for all projects that use Powerline +in some way (e.g. shell prompts and tmux themes). + +Some of the new features for vim are: + +* **Dynamic statusline evaluation in Python.** Python performs really well + and allows Powerline to re-render the statusline in Python instead of + relying on vim's statusline flags. This means no more caching, and much + more flexibility. +* **Automatic removal of less important segments in small windows.** Not all + information is equally important to have in the statusline, and segments + with e.g. encoding and file format information are automatically removed + in smaller windows. +* **The possibility of adding more segments.** Because of vim's hardcoded + limitation of 80 statusline options, the vimscript implementation + triggered an error when adding more segments to the default theme. Since + segment contents are now rendered as plain text in Python it's possible to + add many more segments in the statusline before hitting this limit. From 6a9bb45157121fd02215cce5a3dbdc6d9d6e4e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 26 Nov 2012 15:07:05 +0100 Subject: [PATCH 0036/1472] Fix hard divider rendering issue Hard dividers (for segments with different background colors) are now always drawn, regardless of the draw_divider option, because it looks horrible when segments with different bg colors don't have a divider between them. This resolves an issue with the filename and modified flag in the default theme where the filename segment had to set the draw_divider flag based on the contents of the modified flag to be rendered correctly. --- examples/vim/powerline.py | 4 ++-- lib/core.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index dda44632..42a35e11 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -158,11 +158,11 @@ def statusline(winnr): 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=not len(windata['modified'])), + 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['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), + 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), diff --git a/lib/core.py b/lib/core.py index 3d3e344d..d3c407d6 100644 --- a/lib/core.py +++ b/lib/core.py @@ -81,10 +81,10 @@ class Powerline(object): if segment['filler']: # Filler segments shouldn't be padded rendered_highlighted += segment['contents'] - elif segment['draw_divider'] and (divider_type == 'hard' or segment['side'] == compare['side']): - # Draw divider if specified, and if the next segment is on - # the opposite side only draw the divider if it's a hard - # divider + 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 + ' ' From 6a1912aece1326951bd7142b410694a2220cc299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 26 Nov 2012 16:16:14 +0100 Subject: [PATCH 0037/1472] Add tmux segment renderer --- lib/renderers/__init__.py | 1 + lib/renderers/tmux.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 lib/renderers/tmux.py diff --git a/lib/renderers/__init__.py b/lib/renderers/__init__.py index f5539bca..3c7c2387 100644 --- a/lib/renderers/__init__.py +++ b/lib/renderers/__init__.py @@ -3,4 +3,5 @@ class SegmentRenderer: raise NotImplementedError from lib.renderers.terminal import TerminalSegmentRenderer +from lib.renderers.tmux import TmuxSegmentRenderer from lib.renderers.vim import VimSegmentRenderer diff --git a/lib/renderers/tmux.py b/lib/renderers/tmux.py new file mode 100644 index 00000000..07db9ff1 --- /dev/null +++ b/lib/renderers/tmux.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +from lib.core import Powerline +from lib.renderers import SegmentRenderer + + +class TmuxSegmentRenderer(SegmentRenderer): + '''Powerline tmux segment renderer. + ''' + def hl(self, fg=None, bg=None, attr=None): + '''Highlight a segment. + ''' + tmux_attr = [] + + if fg is not None: + tmux_attr += ['fg=colour' + str(fg[0])] + + if bg is not None: + tmux_attr += ['bg=colour' + str(bg[0])] + + if attr is not None: + if attr is False: + tmux_attr += ['nobold', 'noitalics', 'nounderscore'] + else: + if attr & Powerline.ATTR_BOLD: + tmux_attr += ['bold'] + else: + tmux_attr += ['nobold'] + if attr & Powerline.ATTR_ITALIC: + tmux_attr += ['italics'] + else: + tmux_attr += ['noitalics'] + if attr & Powerline.ATTR_UNDERLINE: + tmux_attr += ['underscore'] + else: + tmux_attr += ['nounderscore'] + + return '#[' + ','.join(tmux_attr) + ']' From 07b23f5418733c3fc04f9b92a59cd3eea434ab48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 26 Nov 2012 16:16:39 +0100 Subject: [PATCH 0038/1472] Add tmux statusline example --- examples/tmux/powerline.py | 23 +++++++++++++++++++++++ examples/tmux/tmux.conf | 5 +++++ 2 files changed, 28 insertions(+) create mode 100755 examples/tmux/powerline.py create mode 100644 examples/tmux/tmux.conf diff --git a/examples/tmux/powerline.py b/examples/tmux/powerline.py new file mode 100755 index 00000000..7f0a5da5 --- /dev/null +++ b/examples/tmux/powerline.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +'''Powerline tmux statusline example. + +Run with `tmux -f tmux.conf`. +''' + +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 + +powerline = Powerline([ + mksegment('⭤ SSH', 220, 166, attr=Powerline.ATTR_BOLD), + mksegment('username', 153, 31), + mksegment('23:45', 248, 239), + mksegment('10.0.0.110', 231, 239, attr=Powerline.ATTR_BOLD), + mksegment(filler=True, cterm_fg=236, cterm_bg=236), +]) + +print(powerline.render(TmuxSegmentRenderer()).encode('utf-8')) diff --git a/examples/tmux/tmux.conf b/examples/tmux/tmux.conf new file mode 100644 index 00000000..15be9700 --- /dev/null +++ b/examples/tmux/tmux.conf @@ -0,0 +1,5 @@ +set-option -g status on +set-option -g status-interval 2 +set-option -g status-utf8 on +set-option -g status-left-length 100 +set-option -g status-left "#(./powerline.py)" From 091840ac67f7ad79ce1c18a39e2d212cbc58643e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 26 Nov 2012 16:17:08 +0100 Subject: [PATCH 0039/1472] Fix terminal prompt example --- examples/terminal/powerline.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/terminal/powerline.py b/examples/terminal/powerline.py index d16cdb4f..dafbf340 100755 --- a/examples/terminal/powerline.py +++ b/examples/terminal/powerline.py @@ -7,16 +7,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, Segment +from lib.core import Powerline, mksegment from lib.renderers import TerminalSegmentRenderer powerline = Powerline([ - Segment('⭤ SSH', 220, 166, attr=Segment.ATTR_BOLD), - Segment('username', 153, 31), - Segment('~', 248, 239), - Segment('projects', 248, 239), - Segment('powerline', 231, 239, attr=Segment.ATTR_BOLD), - Segment(filler=True), + mksegment('⭤ SSH', 220, 166, attr=Powerline.ATTR_BOLD), + mksegment('username', 153, 31), + mksegment('~', 248, 239), + mksegment('projects', 248, 239), + mksegment('powerline', 231, 239, attr=Powerline.ATTR_BOLD), + mksegment(filler=True), ]) print(powerline.render(TerminalSegmentRenderer())) 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 0040/1472] 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 0041/1472] 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 0042/1472] 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 0043/1472] 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 0044/1472] 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 0045/1472] 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 0046/1472] 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 0047/1472] 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 0048/1472] 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 0049/1472] 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 0050/1472] 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 0051/1472] 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 0052/1472] 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 0053/1472] 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 0054/1472] 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 0055/1472] 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 0056/1472] 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 0057/1472] 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 0058/1472] 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 0059/1472] 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 0060/1472] 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 0061/1472] 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 0062/1472] 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 0063/1472] 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 0064/1472] 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 0065/1472] 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 0066/1472] 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 0067/1472] 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 0068/1472] 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 0069/1472] 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 0070/1472] 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, From 92324a6956b8a4e2ad2954a6470a919741e0d5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 13:48:27 +0100 Subject: [PATCH 0071/1472] Fix vim segments for empty file names/directories --- powerline/colorschemes/default.json | 1 + powerline/ext/vim/segments/core.py | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index bdcf2605..018b42fb 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -59,6 +59,7 @@ "branch": { "fg": "gray9", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, + "file_name_no_file": { "fg": "gray9", "bg": "gray4", "attr": ["bold"] }, "file_name_empty": { "fg": "gray9", "bg": "gray4" }, "file_format": { "fg": "gray8", "bg": "gray2" }, "file_encoding": { "fg": "gray8", "bg": "gray2" }, diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index 5de784b8..8e96653a 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -94,13 +94,25 @@ def branch(): def file_directory(): '''Return file directory (head component of the file path). ''' - return vim_funcs['expand']('%:~:.:h') + os.sep + file_directory = vim_funcs['expand']('%:~:.:h') + return file_directory + os.sep if file_directory else None -def file_name(): +def file_name(display_no_file=False, no_file_text='[No file]'): '''Return file name (tail component of the file path). ''' - return vim_funcs['expand']('%:~:.:t') + file_name = vim_funcs['expand']('%:~:.:t') + + if not file_name and not display_no_file: + return None + + if not file_name: + return { + 'contents': no_file_text, + 'highlight': ['file_name_no_file', 'file_name'], + } + + return file_name def file_format(): From a272d907bc6fc965f2ac1a7c898f2ab5629a3163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 13:49:05 +0100 Subject: [PATCH 0072/1472] Simplify vim renderer cache check --- powerline/ext/vim/renderer.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 19ee4d59..ea4da670 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -30,13 +30,11 @@ class VimRenderer(Renderer): current = vim_getwinvar(winnr, 'current') winwidth = vim_winwidth(winnr) - 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) - if current: mode = vim_mode() contents_override = None + contents_cached = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} + vim_setwinvar(winnr, 'powerline', contents_cached) else: mode = 'nc' contents_cached = vim_getwinvar(winnr, 'powerline') From 7750fa296ae30dc1a427a7316041b1e89f1e3b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 13:49:15 +0100 Subject: [PATCH 0073/1472] Fix minor segment contents override issue --- powerline/theme.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/theme.py b/powerline/theme.py index 6a2c6bec..ee1a57b0 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -66,7 +66,11 @@ class Theme(object): continue if segment['type'] == 'function': - contents = contents_override.get(segment['key'], segment['contents_func'](**segment['args'])) + contents = contents_override.get(segment['key']) + if contents is None: + if contents_override: + continue + contents = segment['contents_func'](**segment['args']) if contents is None: continue From fbce4ac2dacaeea113f0b93a7e924baab57b34ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:06:25 +0100 Subject: [PATCH 0074/1472] Add memoziation decorator --- powerline/lib/__init__.py | 0 powerline/lib/memoize.py | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 powerline/lib/__init__.py create mode 100644 powerline/lib/memoize.py diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py new file mode 100644 index 00000000..05f71181 --- /dev/null +++ b/powerline/lib/memoize.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import time + + +class memoize(object): + '''Memoization decorator with timout. + + http://code.activestate.com/recipes/325905-memoize-decorator-with-timeout/ + ''' + _caches = {} + _timeouts = {} + + def __init__(self, timeout): + self.timeout = timeout + + def collect(self): + '''Clear cache of results which have timed out. + ''' + for func in self._caches: + cache = {} + for key in self._caches[func]: + if (time.time() - self._caches[func][key][1]) < self._timeouts[func]: + cache[key] = self._caches[func][key] + self._caches[func] = cache + + def __call__(self, f): + self.cache = self._caches[f] = {} + self._timeouts[f] = self.timeout + + def func(*args, **kwargs): + kw = kwargs.items() + kw.sort() + key = (args, tuple(kw)) + try: + v = self.cache[key] + if (time.time() - v[1]) > self.timeout: + raise KeyError + except KeyError: + v = self.cache[key] = f(*args, **kwargs), time.time() + return v[0] + func.func_name = f.func_name + + return func From 3037b9fbf1e2dd3631cb9b035e1081bf9d04c364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:06:29 +0100 Subject: [PATCH 0075/1472] Memoize git branch segment in vim This works kinda like a timer which ensures that the git branch segment isn't called more than once every 2 seconds. Refs #7. --- powerline/ext/vim/segments/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index 8e96653a..c47a06c4 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -4,6 +4,7 @@ import os import vim from powerline.ext.vim.bindings import vim_get_func +from powerline.lib.memoize import memoize vim_funcs = { 'col': vim_get_func('col', rettype=int), @@ -75,6 +76,7 @@ def readonly_indicator(text=u'⭤'): return text if int(vim.eval('&readonly')) else None +@memoize(2) def branch(): '''Return VCS branch. From eb28d759cc129853072b45b1f2045c93ad9bce01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:28:09 +0100 Subject: [PATCH 0076/1472] Ignore empty group/color colorscheme translations --- powerline/colorscheme.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 7a2edd66..bd608669 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -34,7 +34,7 @@ class Colorscheme(object): if not mode in self.modes_groups: self.modes_groups[mode] = {} - if group_name in translations['groups']: + if group_name in translations.get('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']], @@ -44,8 +44,8 @@ class Colorscheme(object): 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'])], + 'fg': self.colors[translations.get('colors', {}).get(group_props['fg'], group_props['fg'])], + 'bg': self.colors[translations.get('colors', {}).get(group_props['bg'], group_props['bg'])], 'attr': group_attr_flag, } From 9104d9255567bb05eb46f8129b6fc6d6c84edf98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:28:24 +0100 Subject: [PATCH 0077/1472] Update default colorscheme --- powerline/colorschemes/default.json | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index 018b42fb..e6edd328 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -25,6 +25,7 @@ "mediumpurple": 98, "brightpurple": 189, + "darkorange": 94, "mediumorange": 166, "brightorange": 208, "brightestorange": 214, @@ -96,8 +97,7 @@ "gradient4": "gray5", "gradient5": "gray5", "gradient6": "gray5" - }, - "groups": {} + } }, "i": { "colors": { @@ -122,6 +122,21 @@ "groups": { "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] } } + }, + "v": { + "groups": { + "mode": { "fg": "darkorange", "bg": "brightestorange", "attr": ["bold"] } + } + }, + "V": { + "groups": { + "mode": { "fg": "darkorange", "bg": "brightestorange", "attr": ["bold"] } + } + }, + "R": { + "groups": { + "mode": { "fg": "white", "bg": "brightred", "attr": ["bold"] } + } } } } From 203a32e77896e30ec73dbf77910ed25804422c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:57:04 +0100 Subject: [PATCH 0078/1472] Create empty segment for single-side statuslines --- powerline/colorscheme.py | 8 ++++---- powerline/renderer.py | 6 +++--- powerline/theme.py | 5 +++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index bd608669..db4488d2 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -2,14 +2,14 @@ class Colorscheme(object): - default_mode_key = '__default__' + DEFAULT_MODE_KEY = '__default__' def __init__(self, colorscheme): '''Initialize a colorscheme. ''' self.colors = {} self.modes_groups = { - self.default_mode_key: {} + self.DEFAULT_MODE_KEY: {} } # Create a dict of color tuples with both a cterm and hex value @@ -23,7 +23,7 @@ class Colorscheme(object): 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] = { + 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, @@ -73,7 +73,7 @@ class Colorscheme(object): the default mode is returned. ''' if not mode or mode not in self.modes_groups: - mode = self.default_mode_key + mode = self.DEFAULT_MODE_KEY try: return self.modes_groups[mode][group] diff --git a/powerline/renderer.py b/powerline/renderer.py index b60da225..070594c9 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -63,11 +63,11 @@ 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 + 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 - next_segment = self.segments[index + 1] if index < segments_len - 1 else None + prev_segment = self.segments[index - 1] if index > 0 else self.theme.EMPTY_SEGMENT + next_segment = self.segments[index + 1] if index < segments_len - 1 else self.theme.EMPTY_SEGMENT compare_segment = next_segment if segment['side'] == 'left' else prev_segment segment['rendered_raw'] = u'' diff --git a/powerline/theme.py b/powerline/theme.py index ee1a57b0..9ae869d9 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -9,6 +9,11 @@ class Theme(object): self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] + self.EMPTY_SEGMENT = { + 'contents': None, + 'highlight': {self.colorscheme.DEFAULT_MODE_KEY: {'fg': (False, False), 'bg': (False, False), 'attr': 0}} + } + for side in ['left', 'right']: for segment in theme_config['segments'].get(side, []): contents = None From f552979125531fade029bc8baa51e2d0bb9dd320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:57:55 +0100 Subject: [PATCH 0079/1472] Simplify retrieving of config home in core init method --- powerline/core.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 85dd36d1..54922066 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -11,10 +11,7 @@ from theme import Theme class Powerline(object): def __init__(self, ext): - try: - config_home = os.environ['XDG_CONFIG_HOME'] - except KeyError: - config_home = os.path.expanduser('~/.config') + config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) config_path = os.path.join(config_home, 'powerline') plugin_path = os.path.realpath(os.path.dirname(__file__)) From d40e8e05c881d8f4657dd29713ee59f96483ec7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:58:21 +0100 Subject: [PATCH 0080/1472] Handle default colors correctly in tmux renderer --- powerline/ext/tmux/renderer.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/powerline/ext/tmux/renderer.py b/powerline/ext/tmux/renderer.py index 458cb191..eb020b2a 100644 --- a/powerline/ext/tmux/renderer.py +++ b/powerline/ext/tmux/renderer.py @@ -12,10 +12,16 @@ class TmuxRenderer(Renderer): tmux_attr = [] if fg is not None: - tmux_attr += ['fg=colour' + str(fg[0])] + if fg[0] is False: + tmux_attr += ['fg=default'] + else: + tmux_attr += ['fg=colour' + str(fg[0])] if bg is not None: - tmux_attr += ['bg=colour' + str(bg[0])] + if bg[0] is False: + tmux_attr += ['bg=default'] + else: + tmux_attr += ['bg=colour' + str(bg[0])] if attr is not None: if attr is False: From 9c79344f64fab574281bb3bdd44e7500c2c034fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:58:55 +0100 Subject: [PATCH 0081/1472] Update theme/colorscheme for tmux and terminal configs --- powerline/colorschemes/default.json | 6 +++++- powerline/config.json | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index e6edd328..ada3a8ea 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -11,6 +11,7 @@ "darkestcyan": 23, "mediumcyan": 117, + "brightcyan": 159, "darkestblue": 24, "darkblue": 31, @@ -74,7 +75,10 @@ "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" } + "col_current": { "fg": "gray6", "bg": "gray10" }, + + "user_name": { "fg": "brightcyan", "bg": "darkblue", "attr": ["bold"] }, + "user_name_root": { "fg": "white", "bg": "brightred", "attr": ["bold"] } }, "mode_translations": { "nc": { diff --git a/powerline/config.json b/powerline/config.json index a570fb6a..e93788f4 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -12,6 +12,14 @@ } }, "ext": { + "terminal": { + "colorscheme": "default", + "theme": "default" + }, + "tmux": { + "colorscheme": "default", + "theme": "default" + }, "vim": { "colorscheme": "default", "theme": "default" From c5555d5637e0b0d2b56bd4851bff372f9e861142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 14:59:16 +0100 Subject: [PATCH 0082/1472] Update all examples to work with the latest changes --- examples/terminal/pl.py | 14 ++------------ examples/tmux/pl.py | 13 ++----------- examples/tmux/tmux.conf | 2 +- examples/vim/powerline.vim | 1 - 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/examples/terminal/pl.py b/examples/terminal/pl.py index cdecdaec..e4386260 100755 --- a/examples/terminal/pl.py +++ b/examples/terminal/pl.py @@ -8,16 +8,6 @@ import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from powerline.core import Powerline -from powerline.segment import mksegment -from powerline.ext.terminal import TerminalRenderer -powerline = Powerline([ - mksegment('⭤ SSH', 220, 166, attr=TerminalRenderer.ATTR_BOLD), - mksegment('username', 153, 31), - mksegment('~', 248, 239), - mksegment('projects', 248, 239), - mksegment('powerline', 231, 239, attr=TerminalRenderer.ATTR_BOLD), - mksegment(filler=True), -]) - -print(powerline.render(TerminalRenderer)) +pl = Powerline('terminal') +print(pl.renderer.render('n')) diff --git a/examples/tmux/pl.py b/examples/tmux/pl.py index 060fdbf3..526d0e3b 100755 --- a/examples/tmux/pl.py +++ b/examples/tmux/pl.py @@ -10,15 +10,6 @@ import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from powerline.core import Powerline -from powerline.segment import mksegment -from powerline.ext.tmux import TmuxRenderer -powerline = Powerline([ - 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=TmuxRenderer.ATTR_BOLD), - mksegment(filler=True, cterm_fg=236, cterm_bg=236), -]) - -print(powerline.render(TmuxRenderer).encode('utf-8')) +pl = Powerline('tmux') +print(pl.renderer.render('n').encode('utf-8')) diff --git a/examples/tmux/tmux.conf b/examples/tmux/tmux.conf index 15be9700..c0c1aec9 100644 --- a/examples/tmux/tmux.conf +++ b/examples/tmux/tmux.conf @@ -2,4 +2,4 @@ set-option -g status on set-option -g status-interval 2 set-option -g status-utf8 on set-option -g status-left-length 100 -set-option -g status-left "#(./powerline.py)" +set-option -g status-left "#(./pl.py)" diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 093636e1..f5e4a87b 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -3,7 +3,6 @@ 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') From e5c864ec1b5175564c9dd807830b9ae0d3b964ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 15:23:12 +0100 Subject: [PATCH 0083/1472] Add themes and segments for terminal and tmux --- powerline/ext/terminal/segments/__init__.py | 0 powerline/ext/terminal/segments/core.py | 11 +++++++++++ powerline/ext/tmux/segments/__init__.py | 0 powerline/ext/tmux/segments/core.py | 11 +++++++++++ powerline/themes/terminal/default.json | 10 ++++++++++ powerline/themes/tmux/default.json | 10 ++++++++++ 6 files changed, 42 insertions(+) create mode 100644 powerline/ext/terminal/segments/__init__.py create mode 100644 powerline/ext/terminal/segments/core.py create mode 100644 powerline/ext/tmux/segments/__init__.py create mode 100644 powerline/ext/tmux/segments/core.py create mode 100644 powerline/themes/terminal/default.json create mode 100644 powerline/themes/tmux/default.json diff --git a/powerline/ext/terminal/segments/__init__.py b/powerline/ext/terminal/segments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/ext/terminal/segments/core.py b/powerline/ext/terminal/segments/core.py new file mode 100644 index 00000000..c42c5faf --- /dev/null +++ b/powerline/ext/terminal/segments/core.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +import os + + +def user_name(): + user_name = os.environ.get('USER') + return { + 'contents': user_name, + 'highlight': 'user_name' if user_name != 'root' else ['user_name_root', 'user_name'], + } diff --git a/powerline/ext/tmux/segments/__init__.py b/powerline/ext/tmux/segments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/ext/tmux/segments/core.py b/powerline/ext/tmux/segments/core.py new file mode 100644 index 00000000..c42c5faf --- /dev/null +++ b/powerline/ext/tmux/segments/core.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +import os + + +def user_name(): + user_name = os.environ.get('USER') + return { + 'contents': user_name, + 'highlight': 'user_name' if user_name != 'root' else ['user_name_root', 'user_name'], + } diff --git a/powerline/themes/terminal/default.json b/powerline/themes/terminal/default.json new file mode 100644 index 00000000..05013239 --- /dev/null +++ b/powerline/themes/terminal/default.json @@ -0,0 +1,10 @@ +{ + "name": "default", + "segments": { + "left": [ + { + "name": "user_name" + } + ] + } +} diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json new file mode 100644 index 00000000..05013239 --- /dev/null +++ b/powerline/themes/tmux/default.json @@ -0,0 +1,10 @@ +{ + "name": "default", + "segments": { + "left": [ + { + "name": "user_name" + } + ] + } +} From cd41910fb3be46235ac46ae99b9a416bc9619dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 15:43:05 +0100 Subject: [PATCH 0084/1472] Add Sphinx docs skeleton --- docs/.gitignore | 3 +++ docs/Makefile | 26 ++++++++++++++++++++++++++ docs/conf.py | 18 ++++++++++++++++++ docs/index.rst | 13 +++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..fd39b782 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +_build +_static +_templates diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..fd967611 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,26 @@ +# Makefile for Sphinx documentation + +SPHINXOPTS = +SPHINXBUILD = sphinx-build2 +PAPER = +BUILDDIR = _build + +# Internal variables +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..64b3266d --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +import sys, os + +sys.path.insert(0, os.path.abspath('../powerline')) + +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'Powerline' +copyright = u'Kim Silkebækken' +version = 'dev' +release = 'dev' +exclude_patterns = ['_build'] +pygments_style = 'sphinx' +html_theme = 'nature' +html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..07d14c06 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,13 @@ +Powerline +========= + +.. toctree:: + :maxdepth: 3 + :glob: + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` From 82b52094a31b4cb8ff7f225cb5825828399bf07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 15:43:38 +0100 Subject: [PATCH 0085/1472] Add setup.py --- MANIFEST.in | 2 ++ setup.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 MANIFEST.in create mode 100755 setup.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..b8df330d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include powerline *.json +exclude powerline/tests/*.xml diff --git a/setup.py b/setup.py new file mode 100755 index 00000000..80e099d4 --- /dev/null +++ b/setup.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +try: + README = open(os.path.join(here, 'README.rst')).read() +except IOError: + README = '' + +install_requires = [] + +docs_extras = [ + 'Sphinx', + 'docutils', +] + +setup(name='Powerline', + version='β', + description='The ultimate statusline/prompt utility.', + long_description=README, + classifiers=[], + author='Kim Silkebækken', + author_email='kim.silkebaekken+vim@gmail.com', + url='https://github.com/Lokaltog/powerline', + keywords='', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + test_suite='powerline', + install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, +) From 298f2776e323f1da7f96ee1f7ab363091b5a90d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 15:43:49 +0100 Subject: [PATCH 0086/1472] Add .gitignore --- .gitignore | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5152e47d --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +*.py[co] +__pycache__ +env + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.tox +junit-*.xml +.coverage +coverage.xml + +# Translations +*.mo +*.pot + +# SQLite databases +*.db From 175fb1b58e82cb8321e39d0100a8df67a1423641 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Dec 2012 21:09:41 +0400 Subject: [PATCH 0087/1472] =?UTF-8?q?Don=E2=80=99t=20change=20windows=20us?= =?UTF-8?q?ing=20windo,=20use=20setwinvar()=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/vim/powerline.vim | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index f5e4a87b..d1e341b7 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -19,16 +19,23 @@ function! Powerline(winnr) return s:pyeval('pl.renderer.render('. a:winnr .')') endfunction -function! s:WinDoPowerline() - if ! exists('w:powerline') - let w:powerline = {} - endif +function! s:UpdateAllWindows() + for w in range(1, winnr('$')) + " getwinvar() returns empty string for undefined variables. + " Use has_key(getwinvar(w, ''), 'powerline') if you care about variable + " being really defined (currently with w:powerline=='' it will throw + " E706: variable type mismatch). + if getwinvar(w, 'powerline') is# '' + call setwinvar(w, 'powerline', {}) + endif - let &l:stl = '%!Powerline('. winnr() .')' + call setwinvar(w, '&statusline', '%!Powerline('.w.')') + endfor endfunction augroup Powerline autocmd! - autocmd BufEnter,BufWinEnter,WinEnter * let w:current = 1 | let currwin = winnr() | windo call s:WinDoPowerline() | exec currwin . 'wincmd w' + autocmd BufEnter,BufWinEnter,WinEnter * let w:current = 1 | call s:UpdateAllWindows() autocmd BufLeave,BufWinLeave,WinLeave * let w:current = 0 augroup END +" vim: ft=vim noet From f074c1fb297095c5665d9c93f0c5b674c22e8b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 22:01:33 +0100 Subject: [PATCH 0088/1472] Move the vim example to a separate plugin folder When Powerline is installed as a package in the system-wide site-packages folder it can easily be enabled by adding a simple python statement to the user's vimrc file. Otherwise it can be added by doing a :source path/to/plugin/vim/powerline.vim in vimrc, but the other way of doing it works without specifying the full path as long as Powerline is installed somewhere in sys.path. --- MANIFEST.in | 1 + {examples/vim => plugin}/__init__.py | 0 plugin/vim/__init__.py | 0 plugin/vim/load_vim_plugin.py | 4 ++++ {examples => plugin}/vim/powerline.vim | 15 +++++++++------ 5 files changed, 14 insertions(+), 6 deletions(-) rename {examples/vim => plugin}/__init__.py (100%) create mode 100644 plugin/vim/__init__.py create mode 100644 plugin/vim/load_vim_plugin.py rename {examples => plugin}/vim/powerline.vim (79%) diff --git a/MANIFEST.in b/MANIFEST.in index b8df330d..f1b4d444 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ recursive-include powerline *.json +recursive-include plugin *.vim exclude powerline/tests/*.xml diff --git a/examples/vim/__init__.py b/plugin/__init__.py similarity index 100% rename from examples/vim/__init__.py rename to plugin/__init__.py diff --git a/plugin/vim/__init__.py b/plugin/vim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugin/vim/load_vim_plugin.py b/plugin/vim/load_vim_plugin.py new file mode 100644 index 00000000..f60c9c08 --- /dev/null +++ b/plugin/vim/load_vim_plugin.py @@ -0,0 +1,4 @@ +import os +import vim + +vim.command('source ' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim')) diff --git a/examples/vim/powerline.vim b/plugin/vim/powerline.vim similarity index 79% rename from examples/vim/powerline.vim rename to plugin/vim/powerline.vim index d1e341b7..87bd9965 100644 --- a/examples/vim/powerline.vim +++ b/plugin/vim/powerline.vim @@ -1,5 +1,9 @@ -" Powerline vim example -" Run with :source % +" Powerline vim plugin +" +" If Powerline is installed in a Python search path, load the plugin by +" adding the following line to your .vimrc: +" +" python import plugin.vim.load_vim_plugin python import sys, vim, os python sys.path.append(vim.eval('expand(":h:h:h")')) @@ -22,14 +26,14 @@ endfunction function! s:UpdateAllWindows() for w in range(1, winnr('$')) " getwinvar() returns empty string for undefined variables. - " Use has_key(getwinvar(w, ''), 'powerline') if you care about variable - " being really defined (currently with w:powerline=='' it will throw + " Use has_key(getwinvar(w, ''), 'powerline') if you care about variable + " being really defined (currently with w:powerline=='' it will throw " E706: variable type mismatch). if getwinvar(w, 'powerline') is# '' call setwinvar(w, 'powerline', {}) endif - call setwinvar(w, '&statusline', '%!Powerline('.w.')') + call setwinvar(w, '&statusline', '%!Powerline('. w .')') endfor endfunction @@ -38,4 +42,3 @@ augroup Powerline autocmd BufEnter,BufWinEnter,WinEnter * let w:current = 1 | call s:UpdateAllWindows() autocmd BufLeave,BufWinLeave,WinLeave * let w:current = 0 augroup END -" vim: ft=vim noet From 21032e989ed3c8fb5a4ef62f8c4981f7f670ce9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 22:43:58 +0100 Subject: [PATCH 0089/1472] Add documentation files --- docs/configuration.rst | 4 ++++ docs/configuration/vim.rst | 2 ++ docs/index.rst | 4 ++++ docs/installation.rst | 10 ++++++++++ docs/installation/vim.rst | 20 ++++++++++++++++++++ docs/introduction.rst | 2 ++ 6 files changed, 42 insertions(+) create mode 100644 docs/configuration.rst create mode 100644 docs/configuration/vim.rst create mode 100644 docs/installation.rst create mode 100644 docs/installation/vim.rst create mode 100644 docs/introduction.rst diff --git a/docs/configuration.rst b/docs/configuration.rst new file mode 100644 index 00000000..839f6004 --- /dev/null +++ b/docs/configuration.rst @@ -0,0 +1,4 @@ +Configuration +============= + +.. include:: configuration/vim.rst diff --git a/docs/configuration/vim.rst b/docs/configuration/vim.rst new file mode 100644 index 00000000..c5fea4e6 --- /dev/null +++ b/docs/configuration/vim.rst @@ -0,0 +1,2 @@ +Vim configuration +----------------- diff --git a/docs/index.rst b/docs/index.rst index 07d14c06..47c19cd8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,6 +5,10 @@ Powerline :maxdepth: 3 :glob: + introduction + installation + configuration + Indices and tables ================== diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 00000000..407dcc3c --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,10 @@ +Installation +============ + +Powerline is intended to be installed as a system-wide Python package that +can be easily included in other projects. + +.. note:: This project is currently unavailable on the PyPI due to a naming + conflict with an unrelated project. + +.. include:: installation/vim.rst diff --git a/docs/installation/vim.rst b/docs/installation/vim.rst new file mode 100644 index 00000000..2d6d4bd9 --- /dev/null +++ b/docs/installation/vim.rst @@ -0,0 +1,20 @@ +Vim installation +---------------- + +As a system-wide Python package +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Add the following line to your ``vimrc``:: + + python import plugin.vim.load_vim_plugin + +Outside Python's search path +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This requires you to source the plugin file with an absolute path to the +plugin location. + +Add the following line to your ``vimrc``, where ``{path}`` is the path to +the main Powerline project folder:: + + source {path}/plugin/vim/powerline.vim diff --git a/docs/introduction.rst b/docs/introduction.rst new file mode 100644 index 00000000..c516b331 --- /dev/null +++ b/docs/introduction.rst @@ -0,0 +1,2 @@ +Introduction +============ From 79e2f2aee62634ff5495da97ce0033baa3f3c7eb Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Dec 2012 22:13:03 +0400 Subject: [PATCH 0090/1472] Add a way to import extension segments from more convenient locations I.e. not powerline.ext.{ext}.segments.{smth}, but just {smth}. Refs #3. --- powerline/segments.py | 58 +++++++++++++++++++++++++++++++++++++++++++ powerline/theme.py | 42 +++---------------------------- 2 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 powerline/segments.py diff --git a/powerline/segments.py b/powerline/segments.py new file mode 100644 index 00000000..665dcdf7 --- /dev/null +++ b/powerline/segments.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +from importlib import import_module +import sys +import os + + +class Segments(object): + def __init__(self, ext, common_config, colorscheme): + self.ext = ext + self.path = [os.path.expanduser(path) for path in common_config.get('paths', [])] + self.colorscheme = colorscheme + + def get_function(self, segment): + oldpath = sys.path + sys.path = self.path + sys.path + segment_module = str(segment.get('module', 'powerline.ext.{0}.segments.core'.format(self.ext))) + + try: + return None, getattr(import_module(segment_module), segment['name']), segment_module + finally: + sys.path = oldpath + + @staticmethod + def get_string(segment): + return segment.get('contents'), None, None + + @staticmethod + def get_filler(segment): + return None, None, None + + def get(self, segment, side): + segment_type = segment.get('type', 'function') + + try: + contents, contents_func, key = getattr(self, 'get_{0}'.format(segment_type))(segment) + except AttributeError: + raise TypeError('Unknown segment type: {0}'.format(segment_type)) + + highlighting_group = segment.get('highlight', segment.get('name')) + + return { + 'key': key, + 'type': segment_type, + '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), + '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', []), + } diff --git a/powerline/theme.py b/powerline/theme.py index 9ae869d9..e52b7260 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import importlib +from powerline.segments import Segments class Theme(object): @@ -14,44 +14,10 @@ class Theme(object): 'highlight': {self.colorscheme.DEFAULT_MODE_KEY: {'fg': (False, False), 'bg': (False, False), 'attr': 0}} } + get_segment = Segments(ext, common_config, colorscheme).get + for side in ['left', 'right']: - for segment in theme_config['segments'].get(side, []): - 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.{1}'.format(ext, segment_module) - function_name = segment['name'] - contents_func = 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)) - - 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', ''), - 'after': segment.get('after', ''), - 'contents_func': contents_func, - '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', []), - }) + self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) def get_divider(self, side='left', type='soft'): '''Return segment divider. From f3a03aa26a5ed73b84669a8a2aa329e2dcf75f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 14 Dec 2012 14:03:59 +0100 Subject: [PATCH 0091/1472] Update docs structure for gh-pages --- docs/Makefile | 24 +++++++++++++++++------- docs/{ => source}/conf.py | 1 - docs/{ => source}/configuration.rst | 0 docs/{ => source}/configuration/vim.rst | 0 docs/{ => source}/index.rst | 0 docs/{ => source}/installation.rst | 0 docs/{ => source}/installation/vim.rst | 0 docs/{ => source}/introduction.rst | 0 8 files changed, 17 insertions(+), 8 deletions(-) rename docs/{ => source}/conf.py (93%) rename docs/{ => source}/configuration.rst (100%) rename docs/{ => source}/configuration/vim.rst (100%) rename docs/{ => source}/index.rst (100%) rename docs/{ => source}/installation.rst (100%) rename docs/{ => source}/installation/vim.rst (100%) rename docs/{ => source}/introduction.rst (100%) diff --git a/docs/Makefile b/docs/Makefile index fd967611..3eaec8f6 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,5 +1,4 @@ # Makefile for Sphinx documentation - SPHINXOPTS = SPHINXBUILD = sphinx-build2 PAPER = @@ -8,14 +7,12 @@ BUILDDIR = _build # Internal variables PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -.PHONY: help clean html +GH_PAGES_SOURCES = source Makefile +GH_SOURCE_BRANCH = develop -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" +.PHONY: clean html gh-pages clean: -rm -rf $(BUILDDIR)/* @@ -24,3 +21,16 @@ html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +gh-pages: + git checkout gh-pages + find .. -maxdepth 1 ! -name '.git*' -and ! -name 'docs' -exec rm -rf {} \; + git checkout ${GH_SOURCE_BRANCH} ${GH_PAGES_SOURCES} + git reset HEAD + make html + mv -fv _build/html/* .. + rm -rf _build ${GH_PAGES_SOURCES} + git add .. + git commit -m "Create gh-pages for '`git log develop -1 --pretty=oneline --abbrev-commit`'" + #git push origin gh-pages + git checkout ${GH_SOURCE_BRANCH} diff --git a/docs/conf.py b/docs/source/conf.py similarity index 93% rename from docs/conf.py rename to docs/source/conf.py index 64b3266d..05947c04 100644 --- a/docs/conf.py +++ b/docs/source/conf.py @@ -5,7 +5,6 @@ import sys, os sys.path.insert(0, os.path.abspath('../powerline')) extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] -templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' project = u'Powerline' diff --git a/docs/configuration.rst b/docs/source/configuration.rst similarity index 100% rename from docs/configuration.rst rename to docs/source/configuration.rst diff --git a/docs/configuration/vim.rst b/docs/source/configuration/vim.rst similarity index 100% rename from docs/configuration/vim.rst rename to docs/source/configuration/vim.rst diff --git a/docs/index.rst b/docs/source/index.rst similarity index 100% rename from docs/index.rst rename to docs/source/index.rst diff --git a/docs/installation.rst b/docs/source/installation.rst similarity index 100% rename from docs/installation.rst rename to docs/source/installation.rst diff --git a/docs/installation/vim.rst b/docs/source/installation/vim.rst similarity index 100% rename from docs/installation/vim.rst rename to docs/source/installation/vim.rst diff --git a/docs/introduction.rst b/docs/source/introduction.rst similarity index 100% rename from docs/introduction.rst rename to docs/source/introduction.rst From 05505c3dd124e6acadea2d7d734c14b45a7609f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 14 Dec 2012 15:23:26 +0100 Subject: [PATCH 0092/1472] Restructure docs --- docs/source/index.rst | 2 +- docs/source/{installation.rst => overview.rst} | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) rename docs/source/{installation.rst => overview.rst} (79%) diff --git a/docs/source/index.rst b/docs/source/index.rst index 47c19cd8..56e71c0f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,7 +6,7 @@ Powerline :glob: introduction - installation + overview configuration Indices and tables diff --git a/docs/source/installation.rst b/docs/source/overview.rst similarity index 79% rename from docs/source/installation.rst rename to docs/source/overview.rst index 407dcc3c..6c6ed9f3 100644 --- a/docs/source/installation.rst +++ b/docs/source/overview.rst @@ -1,5 +1,11 @@ +Overview +======== + +Requirements +------------ + Installation -============ +------------ Powerline is intended to be installed as a system-wide Python package that can be easily included in other projects. @@ -8,3 +14,6 @@ can be easily included in other projects. conflict with an unrelated project. .. include:: installation/vim.rst + +Usage +----- From f550bd37f83f9b32b1cb70a818b0591f2ba0f7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 14 Dec 2012 16:03:41 +0100 Subject: [PATCH 0093/1472] Fix issue with function segment key generation --- powerline/segments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments.py b/powerline/segments.py index 665dcdf7..407065d0 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -17,7 +17,7 @@ class Segments(object): segment_module = str(segment.get('module', 'powerline.ext.{0}.segments.core'.format(self.ext))) try: - return None, getattr(import_module(segment_module), segment['name']), segment_module + return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) finally: sys.path = oldpath From da6367a897cab477c9bc2d2f88ba7ba630522a97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 14 Dec 2012 18:18:42 +0400 Subject: [PATCH 0094/1472] Added local theme support (mainly for buffer-local themes) --- powerline/config.json | 5 ++- powerline/core.py | 30 +++++++++++++++--- powerline/ext/vim/matchers.py | 5 +++ powerline/ext/vim/renderer.py | 6 ++-- powerline/matchers.py | 24 ++++++++++++++ powerline/renderer.py | 57 ++++++++++++++++++++++------------ powerline/segments.py | 5 ++- powerline/theme.py | 6 +--- powerline/themes/vim/help.json | 33 ++++++++++++++++++++ 9 files changed, 134 insertions(+), 37 deletions(-) create mode 100644 powerline/ext/vim/matchers.py create mode 100644 powerline/matchers.py create mode 100644 powerline/themes/vim/help.json diff --git a/powerline/config.json b/powerline/config.json index e93788f4..5f745f9b 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -22,7 +22,10 @@ }, "vim": { "colorscheme": "default", - "theme": "default" + "theme": "default", + "local_themes": { + "help": "help" + } } } } diff --git a/powerline/core.py b/powerline/core.py index 54922066..85591053 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -6,7 +6,8 @@ import os import sys from colorscheme import Colorscheme -from theme import Theme +from segments import Segments +from matchers import Matchers class Powerline(object): @@ -29,14 +30,33 @@ class Powerline(object): 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, colorscheme, theme_config, self.config) + theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) + + path = [os.path.expanduser(path) for path in self.config.get('paths', [])] + + get_segment = Segments(ext, path, colorscheme).get + get_matcher = Matchers(ext, path).get + + theme_kwargs = { + 'ext': ext, + 'colorscheme': colorscheme, + 'common_config': self.config, + 'get_segment': get_segment + } + + local_themes = {} + for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): + key = get_matcher(key) + local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} # 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.theme) + Renderer = getattr(importlib.import_module(renderer_module_name), renderer_class_name) + self.renderer = Renderer(theme_config, local_themes, theme_kwargs) + + def _load_theme_config(self, ext, name): + return self._load_json_config(os.path.join('themes', ext, name)) def _load_json_config(self, config_file): config_file += '.json' diff --git a/powerline/ext/vim/matchers.py b/powerline/ext/vim/matchers.py new file mode 100644 index 00000000..4d11c9cd --- /dev/null +++ b/powerline/ext/vim/matchers.py @@ -0,0 +1,5 @@ +import vim + + +def help(): + return bool(int(vim.eval('&buftype is# "help"'))) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index ea4da670..c948c780 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -16,8 +16,8 @@ class VimRenderer(Renderer): ''' PERCENT_PLACEHOLDER = u'' - def __init__(self, theme): - super(VimRenderer, self).__init__(theme) + def __init__(self, *args, **kwargs): + super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} def render(self, winnr): @@ -40,7 +40,7 @@ class VimRenderer(Renderer): 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 = super(VimRenderer, self).render(mode, width=winwidth, contents_override=contents_override) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') return statusline diff --git a/powerline/matchers.py b/powerline/matchers.py new file mode 100644 index 00000000..6d16cb0d --- /dev/null +++ b/powerline/matchers.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from importlib import import_module +import sys + + +class Matchers(object): + def __init__(self, ext, path): + self.ext = ext + self.path = path + + def get(self, match_name): + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.ext.{0}.matchers'.format(self.ext) + match_function = match_name + + oldpath = sys.path + sys.path = self.path + sys.path + + try: + return getattr(import_module(match_module), match_function) + finally: + sys.path = oldpath diff --git a/powerline/renderer.py b/powerline/renderer.py index 070594c9..71da016a 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from colorscheme import Colorscheme +from theme import Theme class Renderer(object): @@ -8,9 +9,21 @@ class Renderer(object): ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - def __init__(self, theme): + def __init__(self, theme_config, local_themes, theme_kwargs): + self.theme = Theme(theme_config=theme_config, **theme_kwargs) + self.local_themes = local_themes + self.theme_kwargs = theme_kwargs self.segments = [] - self.theme = theme + + def get_theme(self): + for matcher in self.local_themes.iterkeys(): + if matcher(): + match = self.local_themes[matcher] + if 'config' in match: + match['theme'] = Theme(theme_config=match.pop('config'), **self.theme_kwargs) + return match['theme'] + else: + return self.theme def render(self, mode, width=None, contents_override=None): '''Render all segments. @@ -21,36 +34,40 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - self.segments = self.theme.get_segments(mode, contents_override) - rendered_highlighted = self._render_segments(mode) + + theme = self.get_theme() + + segments = theme.get_segments(mode, contents_override) + self.segments = segments + rendered_highlighted = self._render_segments(mode, theme, 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] + segments_priority = [segment for segment in sorted(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]) + while self._total_len(segments) > width and len(segments_priority): + segments.remove(segments_priority[0]) segments_priority.pop(0) # Do another render pass so we can calculate the correct amount of filler space - self._render_segments(mode, False) + self._render_segments(mode, theme, segments, render_highlighted=False) # Distribute the remaining space on the filler segments - segments_fillers = [segment for segment in self.segments if segment['type'] == 'filler'] + segments_fillers = [segment for segment in segments if segment['type'] == 'filler'] if segments_fillers: - segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers)) + segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len(segments)), 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 self._render_segments(mode) + return self._render_segments(mode, theme, segments) - def _render_segments(self, mode, render_highlighted=True): + def _render_segments(self, mode, theme, segments, render_highlighted=True): '''Internal segment rendering method. This method loops through the segment array and compares the @@ -62,18 +79,18 @@ class Renderer(object): statusline if render_highlighted is True. ''' rendered_highlighted = u'' - segments_len = len(self.segments) - mode = mode if mode in self.segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY + segments_len = len(segments) + mode = mode if mode in 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 self.theme.EMPTY_SEGMENT - next_segment = self.segments[index + 1] if index < segments_len - 1 else self.theme.EMPTY_SEGMENT + for index, segment in enumerate(segments): + prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT + next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT 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 = theme.get_divider(segment['side'], divider_type) divider_hl = '' segment_hl = '' @@ -109,13 +126,13 @@ class Renderer(object): return rendered_highlighted - def _total_len(self): + def _total_len(self, segments): '''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 len(''.join([segment['rendered_raw'] for segment in segments])) def hl(self, fg=None, bg=None, attr=None): raise NotImplementedError diff --git a/powerline/segments.py b/powerline/segments.py index 407065d0..750a564c 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -2,13 +2,12 @@ from importlib import import_module import sys -import os class Segments(object): - def __init__(self, ext, common_config, colorscheme): + def __init__(self, ext, path, colorscheme): self.ext = ext - self.path = [os.path.expanduser(path) for path in common_config.get('paths', [])] + self.path = path self.colorscheme = colorscheme def get_function(self, segment): diff --git a/powerline/theme.py b/powerline/theme.py index e52b7260..c7f8e4fa 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -from powerline.segments import Segments - class Theme(object): - def __init__(self, ext, colorscheme, theme_config, common_config): + def __init__(self, ext, colorscheme, theme_config, common_config, get_segment): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] @@ -14,8 +12,6 @@ class Theme(object): 'highlight': {self.colorscheme.DEFAULT_MODE_KEY: {'fg': (False, False), 'bg': (False, False), 'attr': 0}} } - get_segment = Segments(ext, common_config, colorscheme).get - for side in ['left', 'right']: self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) diff --git a/powerline/themes/vim/help.json b/powerline/themes/vim/help.json new file mode 100644 index 00000000..f51a1b87 --- /dev/null +++ b/powerline/themes/vim/help.json @@ -0,0 +1,33 @@ +{ + "segments": { + "left": [ + { + "name": "file_name", + "draw_divider": false + }, + { + "type": "filler", + "highlight": ["background"] + } + ], + "right": [ + { + "name": "line_percent", + "args": { "gradient": true }, + "priority": 30, + "after": "", + "rjust": 4 + }, + { + "type": "string", + "contents": "⭡ ", + "highlight": ["line_current_symbol", "line_current"] + }, + { + "name": "line_current", + "draw_divider": false, + "rjust": 3 + } + ] + } +} From 0bf23adbccddaee8f2285d99a6f82565d26950cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 15 Dec 2012 18:48:12 +0100 Subject: [PATCH 0095/1472] Cache statusline contents in Python Each window is now tagged with an UUID and this UUID is used to cache the window's statusline contents in the Python VimRenderer in order to avoid translating the statusline contents to and from a vimdict unnecessarily. Refs #11. --- plugin/vim/powerline.vim | 17 +++++++++-------- powerline/ext/vim/renderer.py | 8 +++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/plugin/vim/powerline.vim b/plugin/vim/powerline.vim index 87bd9965..05cc6746 100644 --- a/plugin/vim/powerline.vim +++ b/plugin/vim/powerline.vim @@ -7,6 +7,7 @@ python import sys, vim, os python sys.path.append(vim.eval('expand(":h:h:h")')) +python import uuid python from powerline.core import Powerline python pl = Powerline('vim') @@ -24,16 +25,16 @@ function! Powerline(winnr) endfunction function! s:UpdateAllWindows() - for w in range(1, winnr('$')) - " getwinvar() returns empty string for undefined variables. - " Use has_key(getwinvar(w, ''), 'powerline') if you care about variable - " being really defined (currently with w:powerline=='' it will throw - " E706: variable type mismatch). - if getwinvar(w, 'powerline') is# '' - call setwinvar(w, 'powerline', {}) + for winnr in range(1, winnr('$')) + " getwinvar() returns empty string for undefined variables. Use + " has_key(getwinvar(winnr, ''), 'window_id') if you care about + " variable being really defined (currently with w:window_id=='' it + " will throw E706: variable type mismatch). + if getwinvar(winnr, 'window_id') is# '' + call setwinvar(winnr, 'window_id', s:pyeval('str(uuid.uuid4())')) endif - call setwinvar(w, '&statusline', '%!Powerline('. w .')') + call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .')') endfor endfunction diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index c948c780..2c7e32aa 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -19,6 +19,7 @@ class VimRenderer(Renderer): def __init__(self, *args, **kwargs): super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} + self.window_cache = {} def render(self, winnr): '''Render all segments. @@ -28,17 +29,18 @@ class VimRenderer(Renderer): used in non-current windows. ''' current = vim_getwinvar(winnr, 'current') + window_id = vim_getwinvar(winnr, 'window_id') + winwidth = vim_winwidth(winnr) if current: mode = vim_mode() contents_override = None contents_cached = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} - vim_setwinvar(winnr, 'powerline', contents_cached) + self.window_cache[window_id] = contents_cached else: mode = 'nc' - contents_cached = vim_getwinvar(winnr, 'powerline') - contents_override = {k: contents_cached[k].decode('utf-8') for k in contents_cached.keys()} + contents_override = self.window_cache.get(window_id) statusline = super(VimRenderer, self).render(mode, width=winwidth, contents_override=contents_override) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') From 3b19dac9e71ade138abe6bf42332fa6ee62ddb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 15 Dec 2012 19:36:14 +0100 Subject: [PATCH 0096/1472] Cleanup code and comments --- plugin/vim/powerline.vim | 4 ---- powerline/ext/vim/renderer.py | 3 +-- powerline/renderer.py | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/plugin/vim/powerline.vim b/plugin/vim/powerline.vim index 05cc6746..595c254e 100644 --- a/plugin/vim/powerline.vim +++ b/plugin/vim/powerline.vim @@ -26,10 +26,6 @@ endfunction function! s:UpdateAllWindows() for winnr in range(1, winnr('$')) - " getwinvar() returns empty string for undefined variables. Use - " has_key(getwinvar(winnr, ''), 'window_id') if you care about - " variable being really defined (currently with w:window_id=='' it - " will throw E706: variable type mismatch). if getwinvar(winnr, 'window_id') is# '' call setwinvar(winnr, 'window_id', s:pyeval('str(uuid.uuid4())')) endif diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 2c7e32aa..44058379 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -36,8 +36,7 @@ class VimRenderer(Renderer): if current: mode = vim_mode() contents_override = None - contents_cached = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} - self.window_cache[window_id] = contents_cached + self.window_cache[window_id] = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} else: mode = 'nc' contents_override = self.window_cache.get(window_id) diff --git a/powerline/renderer.py b/powerline/renderer.py index 71da016a..1c86ade7 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -34,7 +34,6 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - theme = self.get_theme() segments = theme.get_segments(mode, contents_override) From 2a4baa0a5983b1f7558c9a17fdf8c4faf9a0c53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 16 Dec 2012 13:10:55 +0100 Subject: [PATCH 0097/1472] Minor code cleanup --- powerline/theme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/theme.py b/powerline/theme.py index c7f8e4fa..db90f315 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -52,7 +52,7 @@ class Theme(object): else: continue - if not segment['key'] in contents_override: + if segment['key'] not 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'])\ From 70818341e65b8a8130c2b604ac74151e5f26ffd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 11:24:38 +0100 Subject: [PATCH 0098/1472] Cache segment contents after rendering the statusline Non-current segments used the wrong contents because outdated information was cached (self.segments was repopulated in the render() method which was called *after* the contents was cached). Now the contents are cached after the parent class' render() method is called so the correct information is cached. Closes #11. --- powerline/ext/vim/renderer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 44058379..bab06ae9 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -36,7 +36,6 @@ class VimRenderer(Renderer): if current: mode = vim_mode() contents_override = None - self.window_cache[window_id] = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} else: mode = 'nc' contents_override = self.window_cache.get(window_id) @@ -44,6 +43,9 @@ class VimRenderer(Renderer): statusline = super(VimRenderer, self).render(mode, width=winwidth, contents_override=contents_override) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') + if current: + self.window_cache[window_id] = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} + return statusline def hl(self, fg=None, bg=None, attr=None): From 3a8e64b7672a415b4bc364b036061f6cc3c4e9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 12:03:11 +0100 Subject: [PATCH 0099/1472] Pass the current window as a parameter to the render method This change simplifies the code somewhat as the BufLeave, etc. autocmds aren't required anymore. --- plugin/vim/powerline.vim | 18 ++++++++---------- powerline/ext/vim/renderer.py | 4 +--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/plugin/vim/powerline.vim b/plugin/vim/powerline.vim index 595c254e..64bc1c78 100644 --- a/plugin/vim/powerline.vim +++ b/plugin/vim/powerline.vim @@ -20,22 +20,20 @@ else endfunction endif -function! Powerline(winnr) - return s:pyeval('pl.renderer.render('. a:winnr .')') +function! Powerline(winnr, current) + return s:pyeval('pl.renderer.render('. a:winnr .', '. a:current .')') endfunction -function! s:UpdateAllWindows() +function! s:UpdateWindows() + if ! exists('w:window_id') + let w:window_id = s:pyeval('str(uuid.uuid4())') + endif for winnr in range(1, winnr('$')) - if getwinvar(winnr, 'window_id') is# '' - call setwinvar(winnr, 'window_id', s:pyeval('str(uuid.uuid4())')) - endif - - call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .')') + call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .', '. (w:window_id == getwinvar(winnr, 'window_id')) .')') endfor endfunction augroup Powerline autocmd! - autocmd BufEnter,BufWinEnter,WinEnter * let w:current = 1 | call s:UpdateAllWindows() - autocmd BufLeave,BufWinLeave,WinLeave * let w:current = 0 + autocmd BufEnter,BufWinEnter,WinEnter * call s:UpdateWindows() augroup END diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index bab06ae9..b4284c22 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -21,16 +21,14 @@ class VimRenderer(Renderer): self.hl_groups = {} self.window_cache = {} - def render(self, winnr): + def render(self, winnr, current): '''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') window_id = vim_getwinvar(winnr, 'window_id') - winwidth = vim_winwidth(winnr) if current: From ccd6b4c28ac61ff8d1c9a2af08929a6424e0df72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 13:18:40 +0100 Subject: [PATCH 0100/1472] Restructure segment rendering methods Theme.get_segments() is now a generator which returns segment copies, and it no longer handles segment content replacement. Renderer.render() accepts a theme and a segments parameter in order to override the default actions (calling self.get_theme() and self.get_segments()). This is necessary to be able to cache themes and segment contents somewhere else and provide the cached data to the render method. The render method now also handles removing excluded segments, which was previously handled in Theme.get_segments(). Finally, VimRenderer.render() caches all theme and segment data and provides this data to the renderer for non-current windows. Closes #12. --- powerline/ext/vim/renderer.py | 11 +++++------ powerline/renderer.py | 12 +++++++----- powerline/theme.py | 30 +++++++++++------------------- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index b4284c22..f3a16255 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -33,17 +33,16 @@ class VimRenderer(Renderer): if current: mode = vim_mode() - contents_override = None + theme = self.get_theme() + segments = [segment for segment in theme.get_segments()] + self.window_cache[window_id] = (theme, segments) else: mode = 'nc' - contents_override = self.window_cache.get(window_id) + theme, segments = self.window_cache.get(window_id, (None, None)) - statusline = super(VimRenderer, self).render(mode, width=winwidth, contents_override=contents_override) + statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') - if current: - self.window_cache[window_id] = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} - return statusline def hl(self, fg=None, bg=None, attr=None): diff --git a/powerline/renderer.py b/powerline/renderer.py index 1c86ade7..0dc062a7 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -13,7 +13,6 @@ class Renderer(object): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs - self.segments = [] def get_theme(self): for matcher in self.local_themes.iterkeys(): @@ -25,7 +24,7 @@ class Renderer(object): else: return self.theme - def render(self, mode, width=None, contents_override=None): + def render(self, mode, width=None, theme=None, segments=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -34,10 +33,13 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - theme = self.get_theme() + theme = theme or self.get_theme() + segments = segments or theme.get_segments() + + # Handle excluded/included segments for the current mode + segments = [segment for segment in segments\ + if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] - segments = theme.get_segments(mode, contents_override) - self.segments = segments rendered_highlighted = self._render_segments(mode, theme, segments) if not width: diff --git a/powerline/theme.py b/powerline/theme.py index db90f315..73d126a4 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +import copy + class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config, get_segment): @@ -20,24 +22,15 @@ class Theme(object): ''' return self.dividers[side][type] - def get_segments(self, mode, contents_override=None): + def get_segments(self): '''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 = contents_override.get(segment['key']) - if contents is None: - if contents_override: - continue - contents = segment['contents_func'](**segment['args']) + contents = segment['contents_func'](**segment['args']) if contents is None: continue @@ -52,12 +45,11 @@ class Theme(object): else: continue - if segment['key'] not 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']) + segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ + .ljust(segment['ljust'])\ + .rjust(segment['rjust']) - return_segments.append(segment) - - return return_segments + # We need to yield a copy of the segment, or else mode-dependent + # segment contents can't be cached correctly e.g. when caching + # non-current window contents for vim statuslines + yield copy.copy(segment) From 11082311aa6c8a65eab2e0bdcced7f51b942b953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 13:33:29 +0100 Subject: [PATCH 0101/1472] Update readonly/modified segments in default theme --- powerline/colorschemes/default.json | 2 ++ powerline/themes/vim/default.json | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index ada3a8ea..a199a09f 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -83,6 +83,8 @@ "mode_translations": { "nc": { "colors": { + "brightyellow": "darkorange", + "brightestred": "darkred", "gray0": "gray0", "gray1": "gray0", "gray2": "gray0", diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index 0b2d1ac2..f4b2b6f7 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -19,8 +19,8 @@ }, { "name": "readonly_indicator", - "exclude_modes": ["nc"], - "draw_divider": false + "draw_divider": false, + "after": " " }, { "name": "file_directory", @@ -34,7 +34,6 @@ { "name": "modified_indicator", "args": { "text": "+" }, - "exclude_modes": ["nc"], "before": " " }, { From 8ca4c531e9105725d3a03aa2a65cb6078293c27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 13:40:28 +0100 Subject: [PATCH 0102/1472] Do a :redrawstatus after setting window statuslines For some reason the statusline doesn't always get redrawn automatically after setting the statusline property for a window, and running :redrawstatus when a buffer or window is entered seems to resolve this issue. --- plugin/vim/powerline.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/vim/powerline.vim b/plugin/vim/powerline.vim index 64bc1c78..6771006b 100644 --- a/plugin/vim/powerline.vim +++ b/plugin/vim/powerline.vim @@ -31,6 +31,7 @@ function! s:UpdateWindows() for winnr in range(1, winnr('$')) call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .', '. (w:window_id == getwinvar(winnr, 'window_id')) .')') endfor + redrawstatus endfunction augroup Powerline From ea760ea1d472ddbbc4835e63ff4d0f040915bf0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 14:58:13 +0100 Subject: [PATCH 0103/1472] Move plugins to powerline directory --- MANIFEST.in | 3 +-- {plugin => powerline/plugin}/__init__.py | 0 {plugin => powerline/plugin}/vim/__init__.py | 0 {plugin => powerline/plugin}/vim/load_vim_plugin.py | 0 {plugin => powerline/plugin}/vim/powerline.vim | 4 ++-- 5 files changed, 3 insertions(+), 4 deletions(-) rename {plugin => powerline/plugin}/__init__.py (100%) rename {plugin => powerline/plugin}/vim/__init__.py (100%) rename {plugin => powerline/plugin}/vim/load_vim_plugin.py (100%) rename {plugin => powerline/plugin}/vim/powerline.vim (89%) diff --git a/MANIFEST.in b/MANIFEST.in index f1b4d444..0e2b40e9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,2 @@ -recursive-include powerline *.json -recursive-include plugin *.vim +recursive-include powerline *.json *.vim exclude powerline/tests/*.xml diff --git a/plugin/__init__.py b/powerline/plugin/__init__.py similarity index 100% rename from plugin/__init__.py rename to powerline/plugin/__init__.py diff --git a/plugin/vim/__init__.py b/powerline/plugin/vim/__init__.py similarity index 100% rename from plugin/vim/__init__.py rename to powerline/plugin/vim/__init__.py diff --git a/plugin/vim/load_vim_plugin.py b/powerline/plugin/vim/load_vim_plugin.py similarity index 100% rename from plugin/vim/load_vim_plugin.py rename to powerline/plugin/vim/load_vim_plugin.py diff --git a/plugin/vim/powerline.vim b/powerline/plugin/vim/powerline.vim similarity index 89% rename from plugin/vim/powerline.vim rename to powerline/plugin/vim/powerline.vim index 6771006b..9eb9e9e3 100644 --- a/plugin/vim/powerline.vim +++ b/powerline/plugin/vim/powerline.vim @@ -3,10 +3,10 @@ " If Powerline is installed in a Python search path, load the plugin by " adding the following line to your .vimrc: " -" python import plugin.vim.load_vim_plugin +" python import powerline.plugin.vim.load_vim_plugin python import sys, vim, os -python sys.path.append(vim.eval('expand(":h:h:h")')) +python sys.path.append(vim.eval('expand(":h:h:h:h")')) python import uuid python from powerline.core import Powerline python pl = Powerline('vim') From 254473419b9b322bb26e7669866e3f1849b2ce7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 15:01:56 +0100 Subject: [PATCH 0104/1472] Remove unnecessary init file --- examples/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/__init__.py diff --git a/examples/__init__.py b/examples/__init__.py deleted file mode 100644 index e69de29b..00000000 From 8ccf2232139e1b8114625b0934ad6cdf71f23998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 15:06:14 +0100 Subject: [PATCH 0105/1472] Add PKGBUILD for Arch Linux users --- package/.gitignore | 3 +++ package/PKGBUILD | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 package/.gitignore create mode 100644 package/PKGBUILD diff --git a/package/.gitignore b/package/.gitignore new file mode 100644 index 00000000..e087cfea --- /dev/null +++ b/package/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!PKGBUILD diff --git a/package/PKGBUILD b/package/PKGBUILD new file mode 100644 index 00000000..859a0005 --- /dev/null +++ b/package/PKGBUILD @@ -0,0 +1,37 @@ +# Maintainer: Kim Silkebækken + +pkgname=powerline-git +pkgver=20121217 +pkgrel=1 +pkgdesc='The ultimate statusline/prompt utility.' +url='https://github.com/Lokaltog/powerline' +license=('CC BY-SA 3.0') +arch=('any') +depends=('vim' 'python2>=2.7') +makedepends=('git') +source=() + +_gitroot="git://github.com/Lokaltog/powerline.git" +_gitname="powerline" +_gitbranch="develop" + +build() { + cd ${srcdir} + + msg "Connecting to GitHub..." + + if [ -d ${srcdir}/${_gitname} ]; then + cd ${_gitname} && git pull origin ${_gitbranch} + msg "The local files are updated." + else + git clone ${_gitroot} + git checkout ${_gitbranch} + fi + + msg "Git checkout done or server timeout." + + cd ${srcdir}/${_gitname} + + python2 setup.py build || return 1 + python2 setup.py install --root=${pkgdir} || return 1 +} From 9ba8570bf7e01078232f99dce2743525a4ec9e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 15:24:42 +0100 Subject: [PATCH 0106/1472] Update documentation and version --- README.rst | 28 +++++----------------- docs/source/conf.py | 4 ++-- docs/source/configuration.rst | 2 -- docs/source/configuration/vim.rst | 2 -- docs/source/fontpatching.rst | 2 ++ docs/source/index.rst | 3 +++ docs/source/installation/vim.rst | 20 ---------------- docs/source/introduction.rst | 35 +++++++++++++++++++++++++++ docs/source/licence-credits.rst | 40 +++++++++++++++++++++++++++++++ docs/source/overview.rst | 33 +++++++++++++++++++++++-- docs/source/troubleshooting.rst | 2 ++ setup.py | 2 +- 12 files changed, 122 insertions(+), 51 deletions(-) delete mode 100644 docs/source/configuration/vim.rst create mode 100644 docs/source/fontpatching.rst delete mode 100644 docs/source/installation/vim.rst create mode 100644 docs/source/licence-credits.rst create mode 100644 docs/source/troubleshooting.rst diff --git a/README.rst b/README.rst index 60ba2ae1..dd61a3eb 100644 --- a/README.rst +++ b/README.rst @@ -1,29 +1,13 @@ -========= Powerline ========= :Author: Kim Silkebækken (kim.silkebaekken+vim@gmail.com) :Source: https://github.com/Lokaltog/powerline +:Version: beta -**This is alpha software, expect things to change or break at any point.** +This is the next version of Powerline, implemented in Python. The project is +currently in beta, and most of the functionality in the old vimscript +project is already implemented. -This is the next version of Powerline, implemented in Python. It aims to -resolve some of the "unresolvable" problems of the vimscript implementation, -as well as providing a common code base for all projects that use Powerline -in some way (e.g. shell prompts and tmux themes). - -Some of the new features for vim are: - -* **Dynamic statusline evaluation in Python.** Python performs really well - and allows Powerline to re-render the statusline in Python instead of - relying on vim's statusline flags. This means no more caching, and much - more flexibility. -* **Automatic removal of less important segments in small windows.** Not all - information is equally important to have in the statusline, and segments - with e.g. encoding and file format information are automatically removed - in smaller windows. -* **The possibility of adding more segments.** Because of vim's hardcoded - limitation of 80 statusline options, the vimscript implementation - triggered an error when adding more segments to the default theme. Since - segment contents are now rendered as plain text in Python it's possible to - add many more segments in the statusline before hitting this limit. +Check out the `documentation `_ for +more information and installation instructions. diff --git a/docs/source/conf.py b/docs/source/conf.py index 05947c04..be00d779 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -9,8 +9,8 @@ source_suffix = '.rst' master_doc = 'index' project = u'Powerline' copyright = u'Kim Silkebækken' -version = 'dev' -release = 'dev' +version = 'beta' +release = 'beta' exclude_patterns = ['_build'] pygments_style = 'sphinx' html_theme = 'nature' diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 839f6004..6e79ebdf 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -1,4 +1,2 @@ Configuration ============= - -.. include:: configuration/vim.rst diff --git a/docs/source/configuration/vim.rst b/docs/source/configuration/vim.rst deleted file mode 100644 index c5fea4e6..00000000 --- a/docs/source/configuration/vim.rst +++ /dev/null @@ -1,2 +0,0 @@ -Vim configuration ------------------ diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst new file mode 100644 index 00000000..066369e0 --- /dev/null +++ b/docs/source/fontpatching.rst @@ -0,0 +1,2 @@ +Font patching +============= diff --git a/docs/source/index.rst b/docs/source/index.rst index 56e71c0f..5f1e50ee 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,7 +7,10 @@ Powerline introduction overview + fontpatching configuration + troubleshooting + license-credits Indices and tables ================== diff --git a/docs/source/installation/vim.rst b/docs/source/installation/vim.rst deleted file mode 100644 index 2d6d4bd9..00000000 --- a/docs/source/installation/vim.rst +++ /dev/null @@ -1,20 +0,0 @@ -Vim installation ----------------- - -As a system-wide Python package -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Add the following line to your ``vimrc``:: - - python import plugin.vim.load_vim_plugin - -Outside Python's search path -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This requires you to source the plugin file with an absolute path to the -plugin location. - -Add the following line to your ``vimrc``, where ``{path}`` is the path to -the main Powerline project folder:: - - source {path}/plugin/vim/powerline.vim diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index c516b331..07ba4f2a 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -1,2 +1,37 @@ Introduction ============ + +This is the next version of Powerline, implemented in Python. It aims to +resolve some of the "unresolvable" problems of the vimscript implementation, +as well as providing a common code base for all projects that use Powerline +in some way (e.g. shell prompts and tmux themes). + +The project is currently in beta, and most of the functionality in the old +vimscript project is already implemented. + +Feature highlights +------------------ + +* **Better performance.** Python performs quite a bit better than vimscript, + and by having most of the code in Python instead of vimscript it's also + much easier to profile the code and eliminate bottlenecks. +* **A much leaner code base.** With most of the functionality of the old + project implemented the new version consists of less than half the amount + of code. +* **Automatic removal of less important segments in small windows.** Not all + information is equally important to have in the statusline, and segments + with e.g. encoding and file format information are automatically removed + in smaller windows. +* **Dynamic statusline evaluation in Python.** Statuslines are dynamically + rendered in Python instead of relying on vim's statusline flags, which + allows much more flexibility when creating statuslines. +* **The possibility of adding more segments.** Because of vim's hardcoded + limitation of 80 statusline options, the vimscript implementation + triggered an error when adding more segments to the default theme. Since + segment contents are now rendered as plain text in Python it's possible to + add many more segments in the statusline before reaching this limit. +* **New and improved theme and colorscheme syntax.** Themes and colorschemes + are now written in JSON, with a much cleaner syntax that's easier to learn + and work with. Themes and colorschemes are also much more configurable, + and it's easy to write your own and store them in your home config + directory (usually ``~/.config/powerline``). diff --git a/docs/source/licence-credits.rst b/docs/source/licence-credits.rst new file mode 100644 index 00000000..8db542bb --- /dev/null +++ b/docs/source/licence-credits.rst @@ -0,0 +1,40 @@ +License +======= + +.. image:: http://i.creativecommons.org/l/by-sa/3.0/88x31.png + :target: `Creative Commons Attribution-ShareAlike 3.0 Unported License`_ + +Powerline is licensed under a `Creative Commons Attribution-ShareAlike 3.0 +Unported License`_. + +.. _`Creative Commons Attribution-ShareAlike 3.0 Unported License`: http://creativecommons.org/licenses/by-sa/3.0/ + +Credits +======= + +:Author: `Kim Silkebækken `_ +:Main contributors: + * `ZyX-I `_ + +Contributing +============ + +If you experience any bugs or have any feature requests, please `open an +issue on GitHub `_. + +Pull request guidelines +----------------------- + +This project uses `Git Flow`_ for maintaining a clean history and +a consistent way of branching and merging new features. All commit messages +must follow the guidelines described in `Tim Pope's blog post about git +commit messages`_. + +All code must use tabs for indentation and spaces for alignment. + +Python code must pass flake8 tests with ``flake8 --ignore=W191,E501`` (ignore +tab warnings and line length errors). + +.. _`Git Flow`: http://nvie.com/posts/a-successful-git-branching-model/ +.. _`Tim Pope's blog post about git commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html + diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 6c6ed9f3..87caf07d 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -4,16 +4,45 @@ Overview Requirements ------------ +Powerline requires Python 2.7 to work. + +Vim plugin requirements +^^^^^^^^^^^^^^^^^^^^^^^ + +The vim plugin requires a vim version with Python 2.7 support compiled in. +You can check if your vim supports Python 2 by running ``vim --version +| grep +python``. + +Vim version 7.3.661 or newer is recommended for performance reasons. + Installation ------------ Powerline is intended to be installed as a system-wide Python package that can be easily included in other projects. +Powerline is available `on the AUR +`_ for Arch Linux users. + .. note:: This project is currently unavailable on the PyPI due to a naming conflict with an unrelated project. -.. include:: installation/vim.rst - Usage ----- + +Vim usage +^^^^^^^^^ + +If Powerline is installed as a system-wide Python package, you can enable +the plugin by adding the following line to your ``vimrc``:: + + python import powerline.plugin.vim.load_vim_plugin + +If Powerline is installed outside Python's search path (e.g. by having the +git repo in your dotfiles folder) you'll have to source the vim plugin file +with an absolute path to the plugin location. + +Add the following line to your ``vimrc``, where ``{path}`` is the path to +the main Powerline project folder:: + + source {path}/powerline/plugin/vim/powerline.vim diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst new file mode 100644 index 00000000..4edddddf --- /dev/null +++ b/docs/source/troubleshooting.rst @@ -0,0 +1,2 @@ +Troubleshooting +=============== diff --git a/setup.py b/setup.py index 80e099d4..35375bdf 100755 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ docs_extras = [ ] setup(name='Powerline', - version='β', + version='beta', description='The ultimate statusline/prompt utility.', long_description=README, classifiers=[], From df0244700c2565de1e23f907b814040401f0ec8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 15:32:01 +0100 Subject: [PATCH 0107/1472] Fix typo in doc filename --- docs/source/{licence-credits.rst => license-credits.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/{licence-credits.rst => license-credits.rst} (100%) diff --git a/docs/source/licence-credits.rst b/docs/source/license-credits.rst similarity index 100% rename from docs/source/licence-credits.rst rename to docs/source/license-credits.rst From 37b43a77fc3a365fa5f66ae277038efa85f01dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 15:32:22 +0100 Subject: [PATCH 0108/1472] Automatically push docs with `make gh-pages` --- docs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index 3eaec8f6..3a9d3de8 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -32,5 +32,5 @@ gh-pages: rm -rf _build ${GH_PAGES_SOURCES} git add .. git commit -m "Create gh-pages for '`git log develop -1 --pretty=oneline --abbrev-commit`'" - #git push origin gh-pages + git push origin gh-pages git checkout ${GH_SOURCE_BRANCH} From af1f3ec7bdda23668fd0c62132c23a504273e7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 17 Dec 2012 15:45:21 +0100 Subject: [PATCH 0109/1472] Move vim plugin to the vim extension directory --- docs/source/overview.rst | 4 ++-- powerline/ext/vim/__init__.py | 8 +++++++- powerline/{plugin => ext}/vim/powerline.vim | 7 ------- powerline/plugin/__init__.py | 0 powerline/plugin/vim/__init__.py | 0 powerline/plugin/vim/load_vim_plugin.py | 4 ---- 6 files changed, 9 insertions(+), 14 deletions(-) rename powerline/{plugin => ext}/vim/powerline.vim (82%) delete mode 100644 powerline/plugin/__init__.py delete mode 100644 powerline/plugin/vim/__init__.py delete mode 100644 powerline/plugin/vim/load_vim_plugin.py diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 87caf07d..495be83b 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -36,7 +36,7 @@ Vim usage If Powerline is installed as a system-wide Python package, you can enable the plugin by adding the following line to your ``vimrc``:: - python import powerline.plugin.vim.load_vim_plugin + python from powerline.ext.vim import source_plugin; source_plugin() If Powerline is installed outside Python's search path (e.g. by having the git repo in your dotfiles folder) you'll have to source the vim plugin file @@ -45,4 +45,4 @@ with an absolute path to the plugin location. Add the following line to your ``vimrc``, where ``{path}`` is the path to the main Powerline project folder:: - source {path}/powerline/plugin/vim/powerline.vim + source {path}/powerline/ext/vim/powerline.vim diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py index 69d94051..189d565d 100644 --- a/powerline/ext/vim/__init__.py +++ b/powerline/ext/vim/__init__.py @@ -1 +1,7 @@ -from renderer import VimRenderer # NOQA +# -*- coding: utf-8 -*- + +def source_plugin(): + import os + import vim + + vim.command('source ' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim')) diff --git a/powerline/plugin/vim/powerline.vim b/powerline/ext/vim/powerline.vim similarity index 82% rename from powerline/plugin/vim/powerline.vim rename to powerline/ext/vim/powerline.vim index 9eb9e9e3..d60ca7e6 100644 --- a/powerline/plugin/vim/powerline.vim +++ b/powerline/ext/vim/powerline.vim @@ -1,10 +1,3 @@ -" Powerline vim plugin -" -" If Powerline is installed in a Python search path, load the plugin by -" adding the following line to your .vimrc: -" -" python import powerline.plugin.vim.load_vim_plugin - python import sys, vim, os python sys.path.append(vim.eval('expand(":h:h:h:h")')) python import uuid diff --git a/powerline/plugin/__init__.py b/powerline/plugin/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/powerline/plugin/vim/__init__.py b/powerline/plugin/vim/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/powerline/plugin/vim/load_vim_plugin.py b/powerline/plugin/vim/load_vim_plugin.py deleted file mode 100644 index f60c9c08..00000000 --- a/powerline/plugin/vim/load_vim_plugin.py +++ /dev/null @@ -1,4 +0,0 @@ -import os -import vim - -vim.command('source ' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim')) From 9a5b94dc28054d28330e9b55db1707ae30083c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 15:20:56 +0100 Subject: [PATCH 0110/1472] Add font patcher script --- powerline/fontpatcher/fontpatcher.py | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100755 powerline/fontpatcher/fontpatcher.py diff --git a/powerline/fontpatcher/fontpatcher.py b/powerline/fontpatcher/fontpatcher.py new file mode 100755 index 00000000..d95683e8 --- /dev/null +++ b/powerline/fontpatcher/fontpatcher.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +import argparse +import os +import re +import sys + +try: + import fontforge + import psMat +except ImportError: + sys.stderr.write('The required FontForge modules could not be loaded.\n\n') + + if sys.version_info.major > 2: + sys.stderr.write('FontForge only supports Python 2. Please run this script with the Python 2 executable - e.g. "python2 {0}"\n'.format(sys.argv[0])) + else: + sys.stderr.write('You need FontForge with Python bindings for this script to work.\n') + + sys.exit(1) + +# Handle command-line arguments +parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') + +parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) +parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') +parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) + +args = parser.parse_args() + + +class FontPatcher(object): + def __init__(self, source_font, target_fonts, rename_font=True): + self.source_font = fontforge.open(source_font.name) + self.target_fonts = (fontforge.open(target_font.name) for target_font in target_fonts) + self.rename_font = rename_font + + def patch(self): + for target_font in self.target_fonts: + source_font = self.source_font + + target_font.encoding = 'ISO10646' + + # Rename font + if self.rename_font: + target_font.familyname += ' for Powerline' + target_font.fullname += ' for Powerline' + target_font.fontname += 'ForPowerline' + target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) + target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) + + source_bb = source_font['block'].boundingBox() + target_bb = [0, 0, 0, 0] + + # Find the biggest char width and height in the Latin-1 extended range and the box drawing range + # This isn't ideal, but it works fairly well - some fonts may need tuning after patching + for cp in range(0x00, 0x17f) + range(0x2500, 0x2600): + try: + bbox = target_font[cp].boundingBox() + except TypeError: + continue + + if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] + if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] + if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] + if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] + + # Find source and target size difference for scaling + x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) + y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) + scale = psMat.scale(x_ratio, y_ratio) + + # Find source and target midpoints for translating + x_diff = target_bb[0] - source_bb[0] + y_diff = (target_bb[3] + target_bb[1]) - (source_bb[3] + source_bb[1]) + + translate = psMat.translate(x_diff, y_diff) + + transform = psMat.compose(scale, translate) + + # Create new glyphs from symbol font + for source_glyph in source_font.glyphs(): + if source_glyph == source_font['block']: + # Skip the symbol font block glyph + continue + + # Select and copy symbol from its encoding point + source_font.selection.select(source_glyph.encoding) + source_font.copy() + + # Select and paste symbol to its unicode code point + target_font.selection.select(source_glyph.unicode) + target_font.paste() + + # Transform the glyph + target_font.transform(transform) + + # Generate patched font + target_font.generate('{0}.otf'.format(target_font.fullname)) + +fp = FontPatcher(args.source_font, args.target_fonts, args.rename_font) +fp.patch() From 5fde2dcd9d1e55cdb64be94bac3bff5dea4f18f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 15:21:03 +0100 Subject: [PATCH 0111/1472] Add font patcher symbol font --- powerline/fontpatcher/fontpatcher-symbols.sfd | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 powerline/fontpatcher/fontpatcher-symbols.sfd diff --git a/powerline/fontpatcher/fontpatcher-symbols.sfd b/powerline/fontpatcher/fontpatcher-symbols.sfd new file mode 100644 index 00000000..8102ff7f --- /dev/null +++ b/powerline/fontpatcher/fontpatcher-symbols.sfd @@ -0,0 +1,243 @@ +SplineFontDB: 3.0 +FontName: PowerlineSymbols +FullName: PowerlineSymbols +FamilyName: PowerlineSymbols +Weight: Medium +Copyright: Created with FontForge 2.0 (http://fontforge.sf.net) +UComments: "2012-12-17: Created." +Version: 001.000 +ItalicAngle: 0 +UnderlinePosition: -100 +UnderlineWidth: 50 +Ascent: 800 +Descent: 200 +LayerCount: 2 +Layer: 0 0 "Back" 1 +Layer: 1 0 "Fore" 0 +XUID: [1021 211 26716215 11183012] +OS2Version: 0 +OS2_WeightWidthSlopeOnly: 0 +OS2_UseTypoMetrics: 1 +CreationTime: 1355758773 +ModificationTime: 1355765025 +OS2TypoAscent: 0 +OS2TypoAOffset: 1 +OS2TypoDescent: 0 +OS2TypoDOffset: 1 +OS2TypoLinegap: 0 +OS2WinAscent: 0 +OS2WinAOffset: 1 +OS2WinDescent: 0 +OS2WinDOffset: 1 +HheadAscent: 0 +HheadAOffset: 1 +HheadDescent: 0 +HheadDOffset: 1 +OS2Vendor: 'PfEd' +MarkAttachClasses: 1 +DEI: 91125 +Encoding: UnicodeBmp +Compacted: 1 +UnicodeInterp: none +NameList: Adobe Glyph List +DisplaySize: -24 +AntiAlias: 1 +FitToEm: 1 +WinInfo: 0 31 16 +BeginPrivate: 0 +EndPrivate +BeginChars: 65536 8 + +StartChar: uniE0A0 +Encoding: 57504 57504 0 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +426 -365 m 1 + 150 -365 l 1 + 150 117 l 2 + 150 236.333333333 176.333333333 333.666666667 229 409 c 1 + 265 461.666666667 321.333333333 513 398 563 c 2 + 550 662 l 2 + 610 701.333333333 652.833333333 747.5 678.5 800.5 c 128 + 704.166666667 853.5 717 923.333333333 717 1010 c 2 + 717 1339 l 1 + 508 1339 l 1 + 800 1681 l 1 + 1092 1339 l 1 + 883 1339 l 1 + 883 954 l 2 + 883 826 865.166666667 727.833333333 829.5 659.5 c 128 + 793.833333333 591.166666667 740.333333333 533.666666667 669 487 c 1 + 594 437 l 2 + 532 396.333333333 489.333333333 355 466 313 c 0 + 439.333333333 265.666666667 426 200.333333333 426 117 c 2 + 426 -365 l 1 +426 820 m 1 + 150 642 l 1 + 150 1950 l 1 + 426 1950 l 1 + 426 820 l 1 +EndSplineSet +EndChar + +StartChar: uniE0A1 +Encoding: 57505 57505 1 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +700 963 m 1 + 700 831 l 1 + 194 831 l 1 + 194 1731 l 1 + 349 1731 l 1 + 349 963 l 1 + 700 963 l 1 +706 750 m 1 + 865 750 l 1 + 865 -150 l 1 + 698 -150 l 1 + 453 435 l 1 + 472 45 l 1 + 472 -150 l 1 + 315 -150 l 1 + 315 750 l 1 + 480 750 l 1 + 727 163 l 1 + 706 592 l 1 + 706 750 l 1 +EndSplineSet +EndChar + +StartChar: uniE0A2 +Encoding: 57506 57506 2 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +265 0 m 2 + 171 0 124 47 124 141 c 2 + 124 830 l 2 + 124 930.666666667 164.333333333 981 245 981 c 1 + 245 1287 l 2 + 245 1378.33333333 273 1454.33333333 329 1515 c 0 + 383 1573 450 1602 530 1602 c 256 + 610 1602 677 1573 731 1515 c 0 + 787 1454.33333333 815 1378.33333333 815 1287 c 2 + 815 981 l 1 + 895.666666667 981 936 930.666666667 936 830 c 2 + 936 141 l 2 + 936 47 889 0 795 0 c 2 + 265 0 l 2 +530 1472 m 256 + 485.333333333 1472 449.5 1455.33333333 422.5 1422 c 128 + 395.5 1388.66666667 382 1343.66666667 382 1287 c 2 + 382 981 l 1 + 678 981 l 1 + 678 1287 l 2 + 678 1343.66666667 664.5 1388.66666667 637.5 1422 c 128 + 610.5 1455.33333333 574.666666667 1472 530 1472 c 256 +586 236 m 1 + 586 559 l 1 + 631.333333333 582.333333333 654 619.333333333 654 670 c 0 + 654 704 641.833333333 733.166666667 617.5 757.5 c 128 + 593.166666667 781.833333333 564 794 530 794 c 256 + 496 794 466.833333333 781.833333333 442.5 757.5 c 128 + 418.166666667 733.166666667 406 704 406 670 c 0 + 406 619.333333333 428.666666667 582.333333333 474 559 c 1 + 474 236 l 1 + 586 236 l 1 +EndSplineSet +EndChar + +StartChar: uniE0B0 +Encoding: 57520 57520 3 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +0 1950 m 1 + 1060 788 l 1 + 0 -375 l 1 + 0 1950 l 1 +EndSplineSet +EndChar + +StartChar: uniE0B1 +Encoding: 57521 57521 4 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-57 1812 m 1 + 39 1907 l 1 + 1060 788 l 1 + 39 -331 l 1 + -57 -236 l 1 + 875 788 l 1 + -57 1812 l 1 +EndSplineSet +EndChar + +StartChar: uniE0B2 +Encoding: 57522 57522 5 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +1060 -375 m 1 + 0 788 l 1 + 1060 1950 l 1 + 1060 -375 l 1 +EndSplineSet +EndChar + +StartChar: uniE0B3 +Encoding: 57523 57523 6 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +185 788 m 1 + 1117 -236 l 1 + 1021 -331 l 1 + 0 788 l 1 + 1021 1907 l 1 + 1117 1812 l 1 + 185 788 l 1 +EndSplineSet +EndChar + +StartChar: block +Encoding: 9608 9608 7 +Width: 1060 +VWidth: 2048 +Flags: HW +LayerCount: 2 +Fore +SplineSet +0 1950 m 1 + 1060 1950 l 1 + 1060 -375 l 1 + 0 -375 l 1 + 0 1950 l 1 +EndSplineSet +EndChar +EndChars +EndSplineFont From 53fbfe15fead8cc7598bcb4ee9714a221ab7e446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 15:22:08 +0100 Subject: [PATCH 0112/1472] Update symbol code points in the default config --- powerline/config.json | 8 ++++---- powerline/ext/vim/segments/core.py | 2 +- powerline/themes/vim/default.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/powerline/config.json b/powerline/config.json index 5f745f9b..70c85d46 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -2,12 +2,12 @@ "common": { "dividers": { "left": { - "hard": "⮀", - "soft": "⮁" + "hard": "", + "soft": "" }, "right": { - "hard": "⮂", - "soft": "⮃" + "hard": "", + "soft": "" } } }, diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index c47a06c4..5fcdb4c2 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -70,7 +70,7 @@ def paste_indicator(text='PASTE'): return text if int(vim.eval('&paste')) else None -def readonly_indicator(text=u'⭤'): +def readonly_indicator(text=u''): '''Return a read-only indicator. ''' return text if int(vim.eval('&readonly')) else None diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index f4b2b6f7..c9ce2ca0 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -15,7 +15,7 @@ "name": "branch", "exclude_modes": ["nc"], "priority": 60, - "before": "⭠ " + "before": " " }, { "name": "readonly_indicator", @@ -67,7 +67,7 @@ }, { "type": "string", - "contents": "⭡ ", + "contents": " ", "highlight": ["line_current_symbol", "line_current"] }, { From d28003c973584cda43636f0059abb75830864c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 15:22:31 +0100 Subject: [PATCH 0113/1472] Add font patcher docs --- docs/source/fontpatching.rst | 130 +++++++++++++++++++++++++++++++- docs/source/license-credits.rst | 5 ++ 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index 066369e0..d49b4438 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -1,2 +1,128 @@ -Font patching -============= +Font patcher +============ + +Powerline provides a font patcher for custom glyphs like the segment +dividers (arrows), branch symbol, padlock symbol, etc. The font patcher +requires FontForge with Python bindings to work. + +Powerline stores all special glyphs in the Unicode *Private Use Area* +(``U+E000``-``U+F8FF``). + +.. warning:: The code points have changed in this version of Powerline! This + means that you either have to patch your font again, or change the glyphs + Powerline uses in your user configuration. + +.. note:: Powerline no longer works with rxvt-unicode unless you either use + rxvt-unicode compiled with ``--enable-unicode3``, or you use fonts patched + with the legacy font patcher and change the glyphs in your user + configuration. + +Glyph table +----------- + +========== ===== =========== +Code point Glyph Description +========== ===== =========== +``U+E0A0``  Version control branch +``U+E0A1``  LN (line) symbol +``U+E0A2``  Closed padlock +``U+E0B0``  Rightwards black arrowhead +``U+E0B1``  Rightwards arrowhead +``U+E0B2``  Leftwards black arrowhead +``U+E0B3``  Leftwards arrowhead +========== ===== =========== + +Usage +----- + +The font patcher is located at ``powerline/fontpatcher/fontpatcher.py``. It +requires Python 2.7 and FontForge compiled with Python bindings to work. + +Patched fonts are renamed by default (" for Powerline" is added to the font +name) so they don't conflict with existing fonts. Use the ``--no-rename`` +option to disable font renaming. + +.. note:: Bitmap fonts are not supported, and will probably never be + supported officially due to difficulty creating a font patcher that works + for bitmap fonts. The recommended method of patching bitmap fonts is to draw + the glyphs manually using a tool like ``gbdfed``. + +Linux +^^^^^ + +1. Install fontforge with Python bindings. For Ubuntu users the required + package is ``python-fontforge``, for Arch Linux users the required + package is ``fontforge``. It should be something similar for other + distros. + +2. Run the font patcher:: + + $ /path/to/fontpatcher.py MyFontFile.ttf + +3. Copy the font file into ``~/.fonts`` (or another X font directory):: + + $ cp "MyFontFile for Powerline.otf" ~/.fonts + +4. Update your font cache:: + + $ sudo fc-cache -vf + + If you're using vim in a terminal you may need to close all open terminal + windows after updating the font cache. + +5. **Gvim users:** Update the GUI font in your ``vimrc`` file:: + + set guifont=MyFont\ for\ Powerline + + **Terminal users:** Update your terminal configuration to use the patched + font. + +6. Open vim and enjoy your new statusline! + +OS X +^^^^ + +1. Check if you have a FontForge version with Python support by running + ``fontforge -version``. You should see something like this:: + + $ fontforge -version + Copyright (c) 2000-2011 by George Williams. + Executable based on sources from 13:48 GMT 22-Feb-2011-D. + Library based on sources from 13:48 GMT 22-Feb-2011. + fontforge 20110222 + libfontforge 20110222 + + Make sure that the executable version number doesn't have ``NoPython`` in + it. If everything looks OK, skip ahead to step 4. + +2. If you have FontForge but with ``NoPython`` in the version number, please + try to update to a later version:: + + $ brew uninstall fontforge + $ brew update + $ brew install --use-gcc fontforge + + **Note:** You may have to use ``--use-clang`` instead of ``--use-gcc`` + when compiling FontForge. + +3. If you don't have FontForge, install it with Homebrew:: + + $ brew update + $ brew install --use-gcc fontforge + +4. Patch your fonts by passing the ``fontpatcher`` script as a parameter to + FontForge:: + + $ fontforge -script /path/to/fontpatcher.py MyFontFile.ttf + +5. Install the font by double-clicking the font file in Finder and click + "Install this font" from the preview window. + +6. **Gvim users:** Update the GUI font in your ``vimrc`` file:: + + set guifont=MyFont\ for\ Powerline + + **Terminal users:** Update your terminal configuration to use the patched + font. + +7. Open vim and enjoy your new statusline! diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index 8db542bb..e045e6ef 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -16,6 +16,11 @@ Credits :Main contributors: * `ZyX-I `_ +The glyphs in the font patcher are created by Fabrizio Schiavi, creator of +the excellent coding font `Pragmata Pro`_. + +.. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm + Contributing ============ From e89ce2c7dbcb23f90256051595a37dcb28d24c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 15:30:03 +0100 Subject: [PATCH 0114/1472] Fix minor flake8 issues in font patcher --- powerline/fontpatcher/fontpatcher.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/powerline/fontpatcher/fontpatcher.py b/powerline/fontpatcher/fontpatcher.py index d95683e8..6ff32237 100755 --- a/powerline/fontpatcher/fontpatcher.py +++ b/powerline/fontpatcher/fontpatcher.py @@ -2,8 +2,6 @@ # -*- coding: utf-8 -*- import argparse -import os -import re import sys try: @@ -60,10 +58,14 @@ class FontPatcher(object): except TypeError: continue - if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] - if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] - if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] - if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] + if bbox[0] < target_bb[0]: + target_bb[0] = bbox[0] + if bbox[1] < target_bb[1]: + target_bb[1] = bbox[1] + if bbox[2] > target_bb[2]: + target_bb[2] = bbox[2] + if bbox[3] > target_bb[3]: + target_bb[3] = bbox[3] # Find source and target size difference for scaling x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) From bda5c59889fab367395670894b82c41d79908145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 16:03:15 +0100 Subject: [PATCH 0115/1472] Add screenshots to docs --- docs/.gitignore | 2 -- docs/source/_static/img/pl-mode-insert.png | Bin 0 -> 6894 bytes docs/source/_static/img/pl-mode-normal.png | Bin 0 -> 6879 bytes docs/source/_static/img/pl-mode-replace.png | Bin 0 -> 6949 bytes docs/source/_static/img/pl-mode-visual.png | Bin 0 -> 6868 bytes docs/source/_static/img/pl-truncate1.png | Bin 0 -> 6882 bytes docs/source/_static/img/pl-truncate2.png | Bin 0 -> 5541 bytes docs/source/_static/img/pl-truncate3.png | Bin 0 -> 3093 bytes docs/source/introduction.rst | 27 ++++++++++++++++++++ 9 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 docs/source/_static/img/pl-mode-insert.png create mode 100644 docs/source/_static/img/pl-mode-normal.png create mode 100644 docs/source/_static/img/pl-mode-replace.png create mode 100644 docs/source/_static/img/pl-mode-visual.png create mode 100644 docs/source/_static/img/pl-truncate1.png create mode 100644 docs/source/_static/img/pl-truncate2.png create mode 100644 docs/source/_static/img/pl-truncate3.png diff --git a/docs/.gitignore b/docs/.gitignore index fd39b782..e35d8850 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,3 +1 @@ _build -_static -_templates diff --git a/docs/source/_static/img/pl-mode-insert.png b/docs/source/_static/img/pl-mode-insert.png new file mode 100644 index 0000000000000000000000000000000000000000..9b09e185d4b50b55d3d1b1b7c6545259973d8cbc GIT binary patch literal 6894 zcmV2%Y4lp<>qOlRt9KS$prbp7pHf zx61QuL6Rhi+n5gIr1R`I#pGFf#wXJR@bugm{7t2--bDt0I?vcbtS;N;{DK3Wh4RWV3c zlgcN7$Cuqp!jOURNOIgqFXD;uCXO60y$wOn?Ji%}>liLScTgw8Js^&i-GMrDIlDgk zl!LXuysS(&UcpMa{>=ATjV$m%!v zBtnC=HHWqDZ{)A()wHRGF!PQjY`8y;Aj>%(eEv1I8Lz%mB>w!BV$Q4TbQ_~l8Rntnlx?^tVjr;HZ75@ zW$oB(DoSsjm>Wamv3-2iFfaz@;6H^w#JK||QjYFrd;I_zvZyCH`53ARlX+^ih4<1c zXdlG2MMcgCSe-Os00rrs}pL&C{=Dw5%~nS^XCr0D0EWz_nDBtccnVCHU+E-lUq z2X}nDJZaGMcZ4OwC29drO%?e5vu{b)^o_fq+3eiDlhgGQzH^@8*%+-&88pdUN$Q?A z*nZZD2cG*QV;zJ(%;Ihg!7XGe&yCVvL`bpxcA^h6N3ae`+=v!pdJRP z)4|eKu#>;O_AWch`g4xRa2h^`r)D{D?28>FHCP5v1_8&gx!g0nfn6Kl=abZ0S`5~% zV;4b7%)+}E-FB39>(=vOnu5uT=Mbj(Da!ZL3kGoX0$=R_C)1pm96y#Q{W;d{+t0@H za;C?RB}y&aAXySZjI5F)Hv4%2C-2d$i1WwkC#hqR=E6g)|6m8F>w5+!ExHtTeew!B zO3ldnVNF$ZHxY(J%;nGRXLviK8JFR+`SXmS*k7q?JAvZFy(CAepffHpSFt;{9SG&U z)&G|zs(Nw^&P2K>5e%Jo4&tT9mNV~4*loC&8B_%FACEmtNWdQn-&(=JbM?p<&*k0a zpiBE3U*u-v61j*C%fso(au*gmk^QD0m~o_@#vbV1qA%go17@C_J%S5I6WOb?{IZiL zmL?KUo|LDEr0tHC{okLqRNs+O|Pm4AOus{0@OL zcyj&_Qnzm9hnj)TCMj?W@Ia{C&AIA24AtM;5MjFzJVc~q=Q%FujF6E=)#RxJXcgob zEI%>%eq!X{0sbzx0Nb>3BIyS*4UN=WfYKsLqs9{9C?l)s%HdEGKHM8a$x+HIHrl7h zNS4P3;QA9)v8YSulWnIc6I==MYUO;jtu|(&Ds>lS{wsMdx|okXJv-3Au6wfgA5U;$ z5*x}iTxtfA+2N@MDCMJ+8t$9nMU>jo zC}I{wBqT)9iba&Vyobgul!xLXh_EwLSabo60F9keX*{_jW;9d098ehQIF_Ewo+<+h z_p!V*QN`9hCp$KJ2pVFi&SpmSVg8ZRjCd2i?(`zJ^I=}x7$&)^v9{Han3lr9S|g&u zi^t}TLsMRh#@h=Ip_R0ZBz9bA#TIjv%nckkk%B5=Jj*O6*qGOZu|M3sHjqh9(@-0S_Q;cSK2p-gvcHnDvWHlfRozF`TIS24Zl zBmS8gz|%LAy}4s;a}1ur%6rEWsBE}uY~|W8<}I4TB!362y7MIN-o?R^HbixBmp0KQ z*?EA!+buF|31e&HrL{om;EspP$a#|lRvr5j_i@;2K(r5JvTG~H(_82s*9mCcB6wm_ z2p&J-DJ|1RFl%BnZ6hb(mH82GRqL)bVCque;=TP@C`YVf<@|AIWdJJtr@z9}f`GNQ zxPP9duNTl;gLr1Nii*lBT2K{UJUV{_c9_`z@n2Yaq?+rNBFlMR`)C8pHf-dT4Bd78 z7n0aY>bY6~yu7ri`cM5#oHm88;SJwfUSibZS~o+>~+rcc>%gvNAST2;4DY1TXq35fm=`zk5gVu&0&XX(b#} zy0UWNI99sIu;^=8m(|qK7;S3c}OO7@Fm8y;rm=;n3&E3uh~Sr^AvaANd=g~Xaq^4|LQ*p#Ja>OHeB zmzuk#$Jv0TaAEq`P#kLVNNW`Vk+$|WbSCf&2*JIffJ_@oJt~*sJT^HT z-`>a|ws7w7H+->kC;vWK$$&QZ2=;_en@)`TP1Xvjh2qRJv`AJOvX4@EB_bDuejTE| zciy&{h@F$b)%#QA1WtX1eT#xn(!y6CzRE+1Ro5(HEgU{|j7`T9*;1^(8rWGfSn%v~ zEI8f*s%V~%aY5d%BOqtytcm>9xBII~ZF<@zP&>KfCbQ6Lk-G86?u4jYNiM9UO0OgR zTrOpuEDBdfyP8PNEumE3N?}nhd7=xEPI5#;Jq1R4hC3@SUdN3Pm7eS-bC-PGO{`I_ z7BX{+Db}}=UzkII;6ZdJpJX97?*i3Uk=D8r&Y84~aOqhRS2R?z<76RDBPKG(wQr*& zs6CnLBaxL~i(a~HqTcRmz{;t#ES7Egp5V6+@xlc&az}S&I|H@(JhwlC|Hw1|&Mfj& zU~aDBZzn4$1JIT8>i3zva4?T=TWoaehH`$rJDq2eS^zmCeC=!tE^fd_Qw5vq0WV)B zIM_2U!VzfTqhez(GSM>d@&~)YZOOMaNhWfRrBh+F(p;KLwcv_>ck^A+b9VnB%7f-| zf5J4D%y1=ZPa?%#C8*THSn$A9)F-#|O<^0iWPg#Vgw1cf!G>%DhTJXuY3*8GUi%sw z3r%RFAK@>nSF`4tzgO-FL=8yhRvOz>_=OF}LujF)wVSq>zRD+=$UTuswOORK^bF?= z9*po-Bk9j`>Vlf+(cY*4`(ZJJiUpjiupt{LnaN2!#<@Bj7tWrcf{SCT@?w;)napG9 z6gM`IpLC1@Spd-<%HG+8%pUwbejHFIs0ojb=MGH;|4u7IcjX`{+?X^>PIi7Zt=+j} z#Sj+6M-k8)cc=7V%EI|11p5&(Jc1}6In7Oa%s*BimEgjNJ0B)4G?s~Tmk_KPxLfso zue?c*EMOL^ybtn9)s;dxF!`R}aftt6#?JHX-1s^#`~8JC{5$VFirsI$cy*86C30fQ zNGDEaRp2@GZtiuH_q*W5>1SA5b@LRyB({;2b%M`|YA|R+cz8k}j&1oAbrWI86l&C1 zXt@XgMT>!U@$z>L3VWtZTgW6y0zrmc5Xn;j))um=E&R&Y8FfV+DtABJ+snyo7m=yE z;OjEUsK!CCP}XV5qM2rk9Cu~sEU{v+ikCp1-U=GE0vSCheap2>7!`uNG>ycjzBI)N zxcChx*jU0ljh3s@*C$iTs16*;oQ1Q=jx690hw~_I1-Vky(PMe3)Y?Ig8d={X7SW0s z30-T)vuIRu+pc*dK5G-7B-QikJTF#{Gveg|hN42gZt10mX{rXp;6Z}7H?Xr{FkKvD z(Sk)1P}tB_E$TAZzBiN?7fryJ_Bk61yWVArv5bQUvRO5I7UK)Pw0U#jzEX_BhClly%N+v7KhK5_h$% zcQzrmLv=IwX>aj33b+g%%hCV~KO9Xb!ysMtor7m6QPvB5V9VweO9KZpFAydWpt4I&*&9&@3oon-PDK~YdVjD+K9OGE4c^dF05rag&&A%w&B=6)k;*b?GN=!Zvmz4IrH`7GR7PNmfG`<5nCzhxc-;l`SBvQuI;n7COL=xRQR zvfO;Wo*B=5@qvJeO{c1<>5aacE1s(Zy7|Rj@p}mBZX%qF|O6@|Kcq5W(kt_b0TILf;ZLQ_#D)hN;0E3g{cxp{|-h^{=1^>PKzj`itT`_vUwb}E^Qg&j$YUjp?VElCm} zpj67Q8jQMvME;&qhe@y}c5E1KKwGN;o1uuplY1ulf|$#?j3)YPgJfet8dJY5qqN(V zUygs&6jnxhpl-}%!-3-*ZtVW)MT-$5a_r@u8%Y!>h0EXRB{>?a0bP50$DhG)$*4`b z3*_R-X!jw+xEMK8-ch3Nj_;;JV>P0e6lgmiK&C{iu+rQafug{%^OzbziL(+6MvHBr zNM^_Au?ftQvUxwXj3&`Gw`k`U!u=B`aQ85$t9tU(t|7#!>o{88e%<}mT2~@kN_b}T zK1L+AfZ)Q4kxnShCiEAxdPOx&oY7fmYHTL2Gvbw`4!aR#$OQrA#pqTN#SS}E$WbGp zQr-Z)$(7j41l*lFLOX-W+VNa64NDf!rTO{!=$A(`Lt`7CWT@r!%!|zk&856q-V0FP zS_PHOgP`uVKy@M(QI1M+(e~sjl>JaLQ~OP3$vBaNpKm9{c{&fp4Y475x6+Wgiw{ny zn73puBb{XZBZDN-QDEqwTA(0Tq7Y5!O`R186Rjo*g;F*!@&O1c9PKY^P^QLNBVp*= z%x9_NbW#HW(u}`u~<~H=A8g1vR?$%iF*qnB}u)vdFzV3l(W7O=f*YO|M{?pmM~+ltp5hgz`u2 zV3BCA*vs~9Z?Yq!Vn9p6U5hETlXPG!s}GeU0gd_lcxn3)a?IB+*edm9U?=L5dHjDi z@_*j?J;VR&znS>vF&dCDX+q%T^Jgw?p6J*>@n4zE*MK)UzQM!L0%tQT>5oGm^d*^0 z+ficMd2b3grUW?w4Sb!HLy^(8Q4kIF6dD~ERwPLa38H zUfz+cymJ!qof{UcO{7*sbW|k4#xk-xkz03sHzP|E84U`eBZ3HWbRsM?f>5!R%ob~> zrzAWof{|KB0t3StschzKlMR4F&=56xGIy!+`7pWUa#pchJ~bnvqlxwMA%4tAf)$rP z4;}7{P*FhcHF_4*LwIZ1R1%j@=d1Z+`1=^Ajtp3XiT2id_UQoEaK3zS5~o+rqF~ho z9(V50i%G8|+XM^=WAlU4seXPsZ-?7KTMGpiz&(_I&5Pk*qck^KA^B`c0{@=q09eVc zG;}x>g%kI8HhH`Ga<_}zHaM>+SpU)tP|Pl3so8S!CwySOkkDxP?^X0l6KZ3sqtAs>LCAbi?n zMtZstI3|IS>RQfL87~2jru;O@B@;QP@~`eA>y7NDYBDO6L?^@$AC1lq2 zH4i93V>#5`ig#=%@$+^>UwMH_%k|l0zmNdzs`JS)^wg1AsVPcg{keL2>N+LCg}7KP zMM;IU{FogqQWIzQe@;qa8YlN|p=bb;hus3Z^dENfZboy*|1~N6Yxk*+Me$l-r@D-~ zWzOUH_;*WLI?Rr;^Z8_n9?V|w8@5bwzao0<+&s~-f#QqO>l<5DT5mjow)z?yGeTX= znTl*?{JW4$v#s;l4p#+W)>Cilfe~7De1AHJrhqB@>8@EUuxq?ra+3_4NlD@qK|D5d zHZR2b&{C8`Mw@uacq>Y4z}XRfdHH3))D@rHod@PGX3c_lhRapNPFcvB#dk7WE9>A( zO(sP?oaM7-^5`%#M^0yuXSx(3l2;{X+LT$WjMR{omP}H2X+S|m=*UUj<5lQVEGAJ)N%kR&dIxv-4$GWtRqjG9X&a>&-w|v};Pv*eVI9Cws zc;lSDXC<5VS}1FP!M3{Fhr}k5_xwO@_!3@u@d;+BOQ^8rgnWe;^PgGGnin5tj7CYs zgTH0X>fdmWpAx0#7~-@Q96nQr1u#}5bG*r$_()e9!IX)Lr0sl?U$_(N2E?( zfs$ztuVCe52eQ7~NzxUIk-cv?p89kAPV@o53unIFMT&9)%U3+bqtQl=e7%pn zemUJpg!&5ue^innLoJu#6f})j7cXYb;>E04d>2bS6rdWy6kmxmh4u9ADJ)dvpX7r) zUC&&hWW_!_o?uH;H*E<6Oo9^uBcpLZhHJ!Re3S!tD@l@W>l{En5K@!RjLqjb(t{{L zrU~HbxiR>gN?E@*mEzm&uv~=~kIfrP>+yZIzk*Cq5)hHVue@sb^jJ2<=G)M8I?tnP zD<)xGI>*iZ!>9I$yM+*~FViYWaD*$3hNX0pP0o8IPZu(2-MkGNk)gqe=Qq*1em; zxD`tna_l|c%MN7uvp?46iCagv{h0~PHAO7g(lbyuNH?5KICza=iBCOSPTZ!TZUA-X z>g7QQaG|`JCYwQB;~=OTq+3r?JB0_g@MT>zOJ@b58I-3<&lkS^2`Rh;`HuH0N3V{F}Q7Qr-Sj__oj3jr`{h&bIYj zL_y`nU2$RfSSs0(TtM|;84Ls^j`Hr$@*AXnqWoO24(fW4eyp%xjib^`t5F=3@3!;h o3J4GV*^l3vMe_#d=?&=r13_W^t*eRG=Kufz07*qoM6N<$f^R=qwEzGB literal 0 HcmV?d00001 diff --git a/docs/source/_static/img/pl-mode-normal.png b/docs/source/_static/img/pl-mode-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..29d37166a2bd3a2e4d5acf5290468b3b4f2e1771 GIT binary patch literal 6879 zcmV<58X)C~P)6O8f-~K zK~#9!?VWdgRM(Zqzi+BE>b-X%ia>yb^_HNe6?#AA% z&DyaOI|gHHT)}kFMYj=zKo&`Am8DzIX0D_uSuU zcZALPh}^|gcYYG5H^0dj+WjMQETBICjqC3*({2yW&eQ-v|1UW zL8i|3TI#A>Fo{EBF+{hTWcZ9-#C+egY}&8JIKx^`N{RHI6%`SK<_x(cvS@ZJu9Bg}Gw7vJo(uWe+cAocCa8Bq^Tp3s|0L;>=0ST?l&acK&+b#}uBygx}GiPKwvqr99pj zs52I__T}I6wc32o=?Cc}FYs^I{nY@!eFNS;3a;_cu^oQ44Qj(-st4%n;hoRnzjwoz zS( zH^Jw5Fl{_|bb*M|U%>O*pehWec{;|R32^b(5bEwpWsy4;2fZJH!j}5ijKj#52BGK zwFYrm64Qg)D64G6JQ&_2MJP#+EhDz2fYO_A4a!0uMg<{luAr*ba(no@ z5#ALMEPdu7!m4v9ZMWTWxskgE5fS8uY%oEEeDDLWzE023s}mC`lVm0Gg&kx1SetOm5(Ys zfpBXnIo0=;Eqz_@j7JFJ{x0{hw|I!Iu&^+c?dF5G*O5^@klqLhF40LmFx#24-=ve< zGMM=m0;=dFlA~HUym15Da+=T?ExjAef-56Z<}+OX9bc?l&z4*{;}<3q>GXZ%@B3#A z$Xo*m4|X3^P=~CI`+oH;BkB_PM_MYM?6WZArIUG1ry4ol(j zrK529o|@v?tPQ;K%eUBH)xW*irmH6VPzL83Z3x0ZtYk+-LJ9Tfp!kv-T07uW3*ZGW zKL)=}07Uq<{!&B)%ND?|r`tc~ltZ@~3Xwxb2^?vL0Qn^<%K+z^0fqhZnZ|(|cDKog zxj(r9ML!45)>g>AFAq{@?I36C7BXv1+|y}K=0>vj?4>*)i3Jk_ZwM^+X6eR^2|$;h zN5egz*Vsb}uQ0;>R72oyhz4!rcMhAu@;TmI*t&zP#=#C>N%08tMW{bdNkcOgnTN@4 z=;NL;4??_c)Z`RU*Vck2FNa2%A3?6t?;(HRFOs_W5gzJ^e9#SlH%`8p%=)!a)HJwK zc_k4$861?)$Rr+JF&2;S=@?rZ_VMTc+)9ZhkWo>}{KHM_j=y@Jd;L?fK%i=o__8jriZ&Awia_>!#jL*v=$*|HE+F^f+s-ulf1Rz zNuZgXzxf3lO{u*8#wr3U*7D+S590Rd|MI8BK9v0JXZ-$D9Iw3pb4D18`L@uL$zy!b zo?6SF*6gKM=ZNbRUcF8`?dX?z|DVTVIPwRc|7$+7Nx$R$U&hn6{}uk}gCd7>e3U+6 zgxV^oY%&amW$$>T9>LNd&qudq9s8=ypon7m({pIsyp97+AuRpz0wg)bC`OJXP}WM} z(H-o|X~qI5B9`!zWl>16QnuxNwpEzyeL4?k#gAst_|-=osMUi@RF*9WrDE46GV4q?%|k}#XdYQT3ts@gCwXByKr5M_Y~Vzb6%ddrq*%@U@S0iM zscn@oG0G1`MK#i(XuPb=Wc8WiaufVLy$EHI8bwqjUV@ejhj)^Z-+~Z4m(|Nu?ES}9 z%1w4}V*Eo-F`?o!Hl7b-)%AF9xBF965z|?@IDuf9_OiK^xW+JJVG?75RM^@pIJI{V zCpCIRMMSSM(d!=I>_=Frcek%(&!sq)8o_(kC&;yOM&ss*J0U0*Y?nx8LnU+oanTSefx2>NA5iz04WHx#%$U;JKktV`iUhH5Po2z%#5t4j zs>)!~7wHr^#o*gL%7P0qNpl%#$zj(QUy`0LFd}&-kKA!hdd#F@{r>=0DoIPa}Asu<-cL?}Q ze4NL|xHuMmzeYPteF1fVi@y&_L563T4+2nM)P$*@O4HddoDeT1L$xb+IZ2d+25HIo z=v_YC+erAVBw}6c5@@Q}#yjtBqQH0spfOaEt(Gx-LIf&7g8T4^gqw;u*9VZ1`%hp= zYAkhoH*-v57?5a$t?ewIzP*-=1}n|S*7Nq7HLO|lksZ`2!dUp+b37QQL>c=4KTS&` zE$s!CMJoF0l=J(JqaB{gg9{h4a()n+Lm8Cn#lhoGkmK*JA${$;e7L)gh~x!~_K;v{ z)zB>X65-vY^%Ootcv@(vX~*0f?_F~n8Dr(l*X#KFcn4A4b1S$qe0~ZeO{e*K{l{#} zS2AJo{X{tREfd!jBPSoi!hHrIWyS$=YG&q7FQCdEMRu(OL<#yP7uq^Re3J_CE*!!6 z#=aj+5jcgFk4_`tMtHC`bL!J~ShHpg?`-GUv9}Zn*gqTU-)$c)t*WQHJasfG{#`fH>!wF_bLUM4I=g;m?qpaNic7z`c z_W&B9a=^Xj76KL=A|a%2-;~(ihEW8$XCQ7;Gx|QN1th9Kf}L9_$gQBcy@}$?EE>C; zD13?WGg5T6kovY(s`IkY*!+m_lws4?QmuC*)LY)wk3T_9ZKxYfy|(LJqxlf+Z>B8k z0<~?eROMt*B?%(htE-*OOy#*Ev=$rsngS|}UPSox&#trSDmcC;57m?uCi>qTv=f{| zNr)6F%dWy8_BfS2gFOH<#rrP=QUG{rRGNkCsAM^Q&f#!>3S z=P+;bG#1S8r1-!Is(ZtAiV$WlnTq`Mb`F>8Z%vAcsghmqzRRXUBgW#*ytifzYu3EY zmU1&LBUkZ4S{i9ryq;YYjsg(PowVr`_{T)!CF!75*GFF7&GILjC_j}?li7w&lSir1 zhZuh)qE1atwUS}+0muQTkTHbW%E+y8#8k6Tems-X<~HgIb7|pa$8`m z|G+Zdd1eCnHL_ma{$=0oX5V=YCa-wr%o~Aodp0$Fxcvp0Gje(WplH@%wD$bwBzI-Z zvZsg_?VGejVnZVXEG?92%*>1OMP6QwJTMxMjxs7cYzWT1wCnX*oNyA%G?==c*=9s% zk>jqATtwa`i|wM)Fqj1t&N2jWTmj0w896N;N#Rk>HQr28oCMeC1cLMhY`HAgMo-c1 z4p+63AJ|V4Hx;%+doz_R;=i?_9Owhv%`pS9ZY-c`ZmFZmFcgfvyMdhrW494$vtShkqz+(tTl;zT z9gN}!DT$bJHnF8IFk`CW0OHhto%8JDcY2C1y-0M}h-YkEjwPzkgGkQ~(juhJ|KV8DCg<}ah-ip(>FiaP| z#vxp1Gov$fZ3$;oo;(C;L3aGtPAAkq!GgXekLT`Jc zXsG5~u9Cp93z;`D7^SJ2%mZ08-E1M1t(~0oU6+Lftp+=gX)01E)i`kTQTQcs4*d!fzU&*jBxB5kMNW1;@a0$8-hD~!HeP@G1X>$_4MRiQzu~tko*}x|NQTPG134h^_QqMNxoB8kMg>n-2BW~TU6-G*O$-s3p?4cQ_fS1;&8sfXDZpci?SCL$OOYZp#>xk%7=YU zE`fm(CH6r!fX)WGE>E1R>WWS!Fm*IwgY=S~ygZA!3lLhlxEM^WP!9;6Fl#i-h_HWO zUkd{TVN8XSIPgsbI$Pfg(r@AtQYQu@Z#c`2FVZ>J(Dze_7CmNUD7(*~k;*SxRiZ_Y z!6rjt)zR76X@BWD={DXhO~q)$U}6G;iS^S{Qr3#ifp*t_76S%Rj!U&EUg7r19&|sz4G&@~jY~#`dnl0|RvxWvCkr=;|t|>F635(Pr;U zB10kwNTov8*A{!iuFMHJ5@d>Ns64p}g+zv%pS|+OtnaFXmU=Vo=YM7G-`{85>rWAX zWeL-D+KC-`Y(K2EU(=XDdQtz+@v(N)qONHhoU;Rm=vG4*EH(-9Zqbt}kPSe{Oc6AN z1>;3BH*F)|eL5*)y&c%lt+eLvX7edI(;rMG*5T8~``|&e8~lPq7?2DiK`KWgntIY< zL^C>*h*T!+qyHP_50J=lagtutpRNiKlfFwwTARts(h@vv5F2DI3p$uVQF-WJKluj2Yz#)N^d#X=)q{ec5!iRO{V|iS|Y= zQ4%m}EP>slY-*smPEOQ>ctSkg@Cu(u>O%_|)m^Y)X{1Oa5H)cELHZKPT5XQCyB?XF zD6N+hHGU+49-f4b8Bd6Lc~;{iyq?p=`Q()I)zScK48_o!SmLfl>Y_B}#(EP9XSZT`Dp^Cd^B~oS9Chri^3h zc{op*%V-Zr)Vzh}?44}MaVP14nM62}nAH~#qLG#s3+_?T_- zQFf-34)G>=H#JaLD<^jH7(%?fiAbDGxJ^S*)6KdyM2y-h)D5>Fhf**Tl-Q2$bR>0# ztLGqiK`GAXMpD$;U#M@P@$_as&Cv845Q&06Nhv;@-=0Ow?aW{i4OHjkpsuT>>})QL zgXrGvAFz{OChx3#0A~6@eGO<;kg^1luW`tX%mq9EV=Fz$lGl(hld0!E5fjm?w%rHA%&fl;Kae3%vYN1#7{hLVdXkXvXh(twW#hSIX0Fl=w_WP!|n;%T0K zay}taC*o39^YqgXGtpZLVkaesGANWMkh*Fm^M{$p*?$bpMZIK1qn0x^brq?JD$0)> zdd+q<+l!VH#=AJ;-pSoTz0#B`xh~l7nQ(d`C0NwU$$P%~%0b-8s&+`I8Xm;b0if zOv9ONY%dCA{_^_>>Fa>XK6x-vb#y$q_$pvLy zq3J%T4{Rf|(b3h^PyV`eWJQ^ri7TFD@tUF@J> z?;i3M6Ik)Y4_GqN#FQ)K&Q3pv_Uvm20JYzJ#l{nL{o}!A#cj%R;!SnEgnVxS2}*pz0#G8L3JJ%3 z5M$%Q=KQ<+tlf6NwQeK_*Co(kaNJ4|T!ypk(V6%fi`c&L5Vd#PVY%`!QXZd;KK(Ne zJIb7(ARuuO^TIUj+j*MWyU~5reI8xg0bWgepF-onw3COe;`Kj2h=2Y&{OmuD(&E6- znE-{c3AMTb-Mw^3{t(?>5}XK|v4WJqLbh(nq~3Io@^|GqeOLA4%IK9(kXZQzpM2ZI zov}ovxCBHH=B2oU=TAlCoFDMWMDM%hR-7t=P+&@q^MAuwRIEBq1DYA}(>31op8^p15i5x=!&{SOM@PF3Yj9My(K;01C zY7z}p9^K6Trjaa~6^3$%ziX3I2qXOTl$5k^=PXexo%L#}TJK=AF6t;h@F|B^Ddvf|2(6d?*1iw*L&>JUw@cZUH?Iy;1tT#q;YtgDmb2zgLY_h z1&IP@2hQ>vqJg2Zq5_Q8p|KvK+qd3TGD}af)ewJo(atYp#PHZVn+K^2FTeES&^o;a Z{XYvd+$xH`nil{7002ovPDHLkV1nVmS3m#& literal 0 HcmV?d00001 diff --git a/docs/source/_static/img/pl-mode-replace.png b/docs/source/_static/img/pl-mode-replace.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c89a455f707a650ac949755ca9f55f542e3bba GIT binary patch literal 6949 zcmV+=8`|WFP)g4-E0t7|O6|Y-BMBfvQUM|$x#!e5IrXdid+)yG zzVF>$gnSUWN2xe$7@J4Lpp$lwPjKnW%mricH5amR&0eZ(+~XvmiWo;)Tot=^pQX`G zFZD3BG*wYu(SSwl9gAM-36kM6D2)evPqB8F7IUwBKO&N#^a>^_!1XScL5|6(Z@|D)w3+%o_uHy-A5Q!JtKdnWCzBU%A1aO-`NYfS1+CR}R$0109@Hlei z_XeWZ{u;HH1aQZ7vxRukKyd1{7MClUjQTdd!v936yyB562KV<45Ni>M7xhGa`7#KdF4=F3cnYwr(qy1$)Ho>as2U)UY2}_nd z&e*`7YX`f5f<0TvRwR=)Is#2kfjdPgDUUzS#Bk-!+3O(>@xN=FQ~={jgyTYxO}s`1yPue)wTt-l4=f0|^4T|-9MQP`{&W0BUJ1x*F@BaoV@3vz86ROd)p$j0*N$c5YMOHuC}#hW zkhe#6-Jl{2O|nd3SKgQ9S!Ef}bCStT~6)b5fLZ-X`SFDM$oo`f`2pC^^8Q zla*+0U@WDc{YafU7$2FM=#(^u2P?Ug&RJ9O20qHz&PDqVu()k{@^)?GockEkl0#4l zw_Y)6!V?L%7n4(QZ`s+`_1t)b5bp1C4||L6MG`TUY~!Q%R*+e7E4>jCT%uE$Jxl_HT;M{a>jxEQif97GVCr=Um z$Np$0Mj)K-c$S6knMJf1t{nfM_CG3V{@oI4jrl~K`8|s9DaaJrowwyR7)>9b$qH@* zaOv+tV`URayiv_d0%~Y@@P4X}c;NErU@Twk!XZn&eqWpo{50)8Q*A9$~%+H9IM)t;bTk&xzB@ zgAi{AmD|oyZLmOI4t2vv5acSQ#ALt2f%Rus3$${C$zb7^slL$(fd$u0`|sH z{>Tyf1lu`Na^-ZG=rjEyrpyUkG GBU4x~XAmBD=os5;ck(~~y`CZ=kp5A6&XhRo ze6V9EDM0U=L`qmAhxga~K)|lOaYuDi+xhCL?zYlsGc>zC;U)1?PzqrF^j}DxeGGL< zB*Ly7M5zaIFMv8MH5RZ{(2`Nn#^iQutl)q%dsrx&$S3{1u{g`+uw%135CPF{$L

Bd+0b_Xt*Ss;2O`&=bmBK zkU*5yI`Xz<@czHDsIv*EA}6urxrZ4Q?t<8Op53ed#>xX#2r-L!_pQlz0)#)oyQ`i6 z>e>A2&sk%c&O3jaM_}o4Ui{4-+!p*Vf0*V&(FZ@}x5wgm{llLTZ$8hr1)dBa=!5>) za{jP%2c0@jT$6d@I(+&8ukhio2VvU(CeQypAK9?i`0%9!47*q40E^b{mH=TY?QN1&{cf&-h_ znNyDqP((b)zs-z7ik*^mAF`peU0bR9GUut$)P1>dx9560W^~J=_(HE?0|q&A;oU(hACoiqN-8C z&?r9?r4>knqVclVbF#(J<@s~N@-0b2S*S%36^WOi=j^`CWac*@1W#n~Obt8!xt?I7 zTM-ozovw*a^}O1TuuyLlKahc7sq~?~17HNy;ww{9YLI}%i#@MB$N{q&(eFI>_i^XQ zKRui%%V|Zl#FnsQ0@a#K+hiBK(G}?Ks1*eVSjclvd|<(4+%jn zv>7>`3F8=tfWy#uYkVM1PnXOG)r6QfP+m}b*(;?OhR+8p30nCfzG;D1aRuZ0RtBNp zyp95}ZrzTp(>F2tlvq@OK^EuVkE)_j5Z)Gqg(dV5I32L30}9r{jVS(Ar56lHWw}_as|0Vfv#?>!U%CM>6LZPckap%BdZDIH!nZ!t?1&43J`P$YIC6;x_Q!TEM<-TiCK` zJGsu5^D3hVY0e|RS-@xLLLMFH;#~Os8ho~zb5sK^{yr!L8J=N22tZAt&S~S~l11wm zPKcM1-rAKP0ZEjE2I5g;^jNBQe9ggf(=CZOI1Hy){oZmiYwgq@%;3GHOIf;fWgDnd zgfaEG=XfYii86LJ&!(r7p8f(eBNbh}lruXIq3=7Lho(+r?gK%T@6DvRRqQ$W1Udfh zHENB`dNbmZjA~2b`3q}%fBQn_QIkx(5EM2;k_tzC+>PdM84+bw- z$k>qU=wQ)|PFsyxbkK59i@|;k=hv-LAI_brX@u%(2z}faj9bxfxOl}wKQxa#fyWa| za8L5Os(TU?Q|96l*Npj-=O}+It8;IH#0%x5K0qm+p@EDB#@{LB=dDxk*D zNJZXB${l`0c*=0JR#DOFMyR*E{XPB!sSIfAES>UouI+q?_P0`U@+?(`MlR-@oYhn2F^h3IV#S}V^{YW5<+r+YS^!&u6p?RjXDCo}XW3q%BUD2b6GCD|9boL4Ht zGuQ*La(?G(mi^Z+`S+i`LPow3iB~*{zJPW;%U7;o)jmBCK~lIHyY4va*B@`QDr>*x zt4}h>ShbFm^-co2mP0K6;3NLIu>w#M8td&GaJ>c})koNW5r|AC*4>x;k^@03WN%)> z4IHo%TmnMzQ};r^-p$G-grW9B#BrN z&Vw&JN7|q`1`Hj+uoxNon#S9AMU zNmH5aa`@A-nKHj=*U4)ur+#Q0HOE_>YewA{mjMFQoWN35iv64k`V2!8rKIJoGYX@# zzBo?}F-aWomUS8uC^@o?ooC9icqK4lQatLWY%0452?#PZaxMcv(Q3qOzx+%kcV*zr zMI?xAhqOfEpj-yn8Yn5ZGAYUz`GpGPfzfz0mr&O1Ku~wW*J)o=s01sumi9h7%ouHQ z+!Y<5z>dY{xTH`_Rsn@thCnwiK9qUWZ)5_Jf&-kcyP2do39iwJ1ht-HUG0@I&sGYz z9inmIXa+sMlt10SktqdZ6-^1V*yobLp3^6Z!q@m2X;2h9fqRAhFuieA{Sj5Q%z^s zxhIOpCJ(`qvzB#Tff-9BNB0#lZ`@cCE^H#Z{&ps>inJDO<&hlv)O0#Ai-V{5|EN?o-9*N`4An7GC~dW<>3 zoN+P^zd4LNz3`o9n#8ljo1@4ma3U^dF6ZEmJYr`h(CrIaByL1Ti+uFGvfP_{tk>?D$F52Htt^FnR}o z15>Tx)_EnaS-vDi<)%h#X~yhuhR;EL)_RUT{ws#hm;f}hZ{ukUH_%C&p#p7nGq&DK zi|=8Qq4DxS4oJMcQA^7&8IRXdIy4vVVy$cnb7wt(^~-JK8*X0-j>FP~zWzFDz&DO~ z&sx4+ca{d52&eXvoj9KX;TrOCo3FF4Z?Sw=Y+Xym3Gz&H7?Lm$Wq^U~J$2k!HC-aM zrbKp7+(fTcvyr@7XnC=NJ;C#%+QcTk)`5+sMS6Xn-EQpBYw%oR&tr#*g zFzl&mL%B&|2=+#74FJ9<{1qSzLi2bU@>}RcPtLMUjlvAm1LJRe=n3T_lYJ%pUKB>rt{0sE`lVA)UhE* z%}wov2PUdZOVHMuFjkjQ+1x%bqND9y5*ZRfKq?j5zqYm64>A>UB*+xkP=|623W*Fi zzqT9!YpbPgT%_KN{Mp}G{*Mn?@y;R=G=funVsjoF_vzbyQVq3kOAQ5hfEiOpqNJPu!lo!#8(IO(1NxOLejmif|_0Eil+@FE{J%Jhy?mSMVlc6t%v5JaTcVeQwkxP^W^dCfEM|+l9&R5HcN=hKa z(+#iip-g{xD*ZbO7HoACmJ37;O(LkZh>}K!b9~pGP94QHa-s(JBhbT>(1C*qaa2;Q zw_O5uVZ(+HM5;rVp~ZxM2t#fgffRMdkS#cI2JTS-amr4un78(P9~_s z&=XXIBu2r7Y|5@mk`^=xEPm@vUVrB;e(}uHJU$@=5UH0NeYxZ$A^`AtquU~wq0nusl6P=8nnItUhM87 zTN72yass3LQA(5q4N4;TW?d>WKawUTV$Ir0R(S~rx8|Wvp2z?XXOx+V57-y{+}nTWjrB4XBGL|gj<HBjKJf#@*>3$S8b{Fc~s5|~O zt24{H4Twa+pOncyoY{DihTEB?BATek$w6CPMaijL>Uz+<+pW;MSX0nmZXbo^`M(^)jw zMs1On21i)?;2J63y9@8}qnSK+7!r$~l9LB0x^y$Tjk>~e=-U_5iIU4<*v|N5fs9)) zojx6vuG1G2M-!*ke#x==7K-*};*&ax>GP7?t~=Rvka9C12OMU~wQ>ecpNB8?6zt!{ zSwk1QLQoMhd@7?o&arJ<9(r5%7Jb$Ea!h`~Sc?nksAG`0M3NY6rf_5R_pVG4jc54u zPb-i;{Sc$ak3(YDQ@C?A%l7MVSnFB)mlb$FJCBhG0}-3Ge7kNv$2;QX##}P~u>-Hg z6NycTprv#_+Nx$8#$48XdX&(|hLbSRlfwlqL?&ESy<^NJOYh6L7?JuDt681X9tJXB zV8_l922P8@nzx<(7cI{5Ndp);B^K=YY&ulaEyX3a)}k%1zkb-am+nx9T_btBj}Sg} z7S9YYQkHw3T6^EF`J{e~eqsSbI&z$6K1(`Aa=%!?K5+p3U8~raSC1XASD)szA(6y5 z4|22gPOK{{wI?>PQ9hgLv+if}+QVGzOuuz0gN2I+c9ENw!n`L&VXDbv|C!bs_Z(Tu z*}LH&sS_vhXp$1qSVi9M4P@0hyJNa3A8mTDv_#I(IZxm()RDhyGr5hIB~S&%;%zM8 zVENT3<_2UJn>n{*JAPABnDh7;98Fq|ZONq61UG+Ii35|a46P)ba359AWittOlnMu~ zlXmdxq{}cP@2y#USz67=WPzNw{V<8#Kxo)P)sZh*qg=qk`vaVp!C}WOc@_y9-sO~d z8~?Ygpu{IE0HwYLX-GKkJs2Ao^0{~K)2#=*Jo~XSHIY(<^GjWD>C4OoWAHT>vT@B` zs(x&j<;ugDyl@Rvq1D!6?7fw^mu~5)pdxJa93}@Auzp<@HI{o+zN_!) z``&w888G*8hLnBIr{C)ClFPxRxCBHH=B2oc=TAfAgvWSfsCU%p;ei5y4( zpgUjIb@~4#M}nTRUQpLdJwc+0vIAeUOV^LIv0*5C<-0biLWuWkrKqUkF8Yrzc3Nt* zTx`6H(Yk2l!tT|q&DM3TT|LI|pCA986mJ*${bU)Re)v57RX_eMd{2Aq{yuv?waV^; zIzbi6h?M*Awv=)xGY5U|;R+H3YA5dU>!n*mC57iO+k3~lmwI+jYh<>|1y#NB{fJI} r8S#B%?`|2SF1+%q7kl^VHR%5V&EgK6)Q>7b00000NkvXXu0mjf7`k7S literal 0 HcmV?d00001 diff --git a/docs/source/_static/img/pl-mode-visual.png b/docs/source/_static/img/pl-mode-visual.png new file mode 100644 index 0000000000000000000000000000000000000000..d654763b44460312603db2ce3f76b76fe16b911f GIT binary patch literal 6868 zcmV;_8Y|_AP)sU9jmLL^p`40!Tt_H0m0SG?GTs-`^iIf(48;0tpQ0JkLDOJoC>N_)xFAmbI`&Jx;;VdHi~O6~VX6seoLqM0($fiik;jnnE(jnU;i?WN`Bgxa#r_ zBR1RzxiB~vgLI8-gpOm;^7)Jk7zm9d#V>U(i!&^oKB2t_LC?L;U(e^5!Y_iDyBgF< z@gKR6Cwc>Q<`P!F{3>5+toNOMkpB8netn~hjpyONk3g9RjPwJC0p8yZd+H!94vd%J ztwV75qU+TJnXw=TL<=0tgU?RF!BVKULUa(wJFdP*YZ1JC^vZ7tfaVmua{>-m!_au} zbRBGE$KbtFpbLZ)Pxlz?00Morj8*&bO|1_mcg0^3L`qsLYI_SMg;iLBGFh;ADq-#A zROzkmyEzpx%zJVHVYL^~bUFrP5mgBfvSP_pGBQ%}sy$Ct$H4p!q8Xh=i#Q~UN#O=6 zs@t#*gf~eMMb_hsNNO#n{1#k;vdD+|fA776nyRQV^c)(IdJkdhqK8>HYcgYpha+vNqNdGuXZX7r-W9PdeD)Ed zYYQlEcieWlk^6)b8}5y4AVEb)HQ6ua^5}DCnK-c(Q4@-`Sq4fSId5;`!Qcq;&U)Zb z=$ZYe?TphUP*^K-d;NTdFW{*KDX4q}4 z9wcPtFfKyLcgcxjD&NcpYj;s&zYWO6rl)A{PKte|kTX6KmC*mnM;((+jJ=$K+WX6v zzM*%V0JV@7ASePny^1<6{$gS;9Z-fNTge)GK=E2#ocTm_mkogt@>Vzz2 z#J6&A{l{!B&|x&&dN!B^FH*ASFtqD9pRHNT#zHxx=glBa^wOVXP(pKrZK+9SA~y?Bt$2K`uyaGP!`A70iHK1#$sAf?#T#>$?Fk zHQwp1!wzLS=jZb#!;0DPOg2mpa6@HTp|XR^2(hfa|E4{B$U$C7Yr5MSULl2lG%>;I zL2x%n19tFxL{Db%%m6NK+RB;cfev3u@r@2bXgp6@Qwvpj2gz^hJO?&vme{Q187RK;+WnV^Os3<`nlu6dOSk(P+ zp7sdd6`tS;bQ-`0SdCx;0{wd#D1fKy3;DIqaB7g7W`eaHnt-5auv9=@GuVUarvptS zcG!M?X`99G&j+|2mK7a#Y<7ne+}iEf9b%8nD?-xA%E}`4Z1K#?6>S zYN!W}4lU;n=5nUigeZ?D`-vGS%4<=?M&m2Aa_QJ!j+VCJ4xSP%4V>DsAKCm_%(3oZ z$Ju83Lsd!xQW);5<6NoXGH-%c3X7h9j>Q>aD6P#D?Oe;d|C2|vO+Xbln`fSXgh?@; zh;5hH_wir&=x{wk;tJmST{eCIF;DT<$4>z*Y3k$Nl(e>ex@#x?AzwSpp^ z#m~&d@YNdj=^|P9!?{QbE}R~k%lMWZ*SpDuPH9Kz~3`D zC`∾^Xia^jti+job^Z2obYbu}IDCzi*<_;`An_KJqk~RiCo{d^F2$#CyBbpQ4VP z#M1fcM9B14t*yi>k;(J27#Xg{(O$*L-McuU?Lt(<_9zoQo&g@gL`ManxJ?Fvr7n<` zZh#TmM378Ll|cd)fA;_CK@OWe5Q8uAzz`pf|J@h68~0R2P2thyV+s5&gXf?LJn~R8 z?Nb*Kec+e;vZCo)7ZDv5$2pYWge)qP$r+J&2u>r%FLfHz5O5gU`o}-g(?z)#gh;^F z4h9G4o1LFW1okpe0wabv!C&P$*jNSbI$sPrCwPnqfj|kgHbGavy2l(0N!!PZ$!#e6 zG*eO}h`oF2WPxPN9E*QVE*n1EL8&T{pzg^MJW0%&#V}g|+duo99Tx;rW=tW@L%`A5 zf>GjypNH$q5;Xy8kxqRV_8!LdydOMCoHm_g>p8Z5_Bp#s6{OFcL{vB95;;NMwd`BJ zhV}cKh?TO-5OpPvvE0tsr6PN|tAYVJkku zkxMdC7d+1VA!-D997|sOK9gdsoZY>jVnq@&f3lKUp;GLv1?)apjs>8rgo8V`vwiC> z3f;}wQ^ph7QN)D~fxt0O@OYZ1d*SzQ8E0!OrUCE_2}CK#@QV&a02)hmSo)|mJ%VG1 z^j9)iyK;|{L`hV*p4^Y#;ltg{#7xa1$rWm_RBh%j?`)vhd<~#6RgUE&mLqFJ-t; zd;WPIPFA8!dWavbT*=CnKV?yzqPLrJe$P?#Ll^Myy!kAh6OQ&kF6F%(5VypiASc8} z%Z}CW@Zrt|VrR@{gs%i!o0b+Kh}Zy^)>8x$>t~~>t{rPnyt~#mF44~EFW2xtM>~k` zUR%M7p>whsW;w-|YyZLK3raHQPbXH@yG-0rj8uU{#{>>Q%8VPNYGKNcE~3sKPJX=v zLN;klr2#t--w~;;&sRj(t-0!InHb#s-!1BP};ilMbz$}8?FAQ&~m9Ac@1tjV) zB0SnCF07)ZUB{)oGcbsQ2}z-eL@IV z8PGIadK}mD`UR2@Vx{8DMd}T0)D)bdMiNefziXVsO7*!?^fm`wb;VSf{fQ0in_cHH zR&jJ!5$f^Tj0w3lXeW3?ksc>fkza#Jyy8^$i|_@kT-vjacmCsF_~#$L%-Rb^B>pL+ z2LYN*to~>XA0N~Mv1G=0VAq{u)235S%dzPjzWm2p)_%N^Gc9fkQ|D1u|Lp_*zNHpW z5|tF-9&r7ZaWov~Pz?|_o+O_jMvV^xv6KTvtv7LqO7IMgBG_XP+TGJ6TB$fuNTbD0 zhxQUW{0QwWT=249R+N5D*m#v_gd*Iy(Sg2y-V;FF;fZJPxcYhSIZezz3$wdvvBz zZUB@JqyjH5paZIm5a-jsj5n2MvZ+PZquamY_)hkm*JANcW#;S@JlgZA>&5La$UKm9 z1ptaxBWC-RYbv=HX^WmFRdgQG5{Uz?46wCQp|vtQJ_vbbE%LAgd^;+r?rZEz zuY`2MyNcO(Rjy4Jr8|$(mNuD@KUu{eAK1cY>%OK*4-%!f)9ZRU)aue3p7L9_f9zK5 z*y++6ofz3e;bvviw~Vt@aA<2W%bp$0qMSB-W59ZL8+p2$3#eLK8_<~sgR%EEu(M(A zHXG>GvFvq{kHVeR@dlp`0iWd1zbKK_cr8&$X7Du1H=$~uTvx-2doDYjF2 zs*I`gMiP0n4b`Yo9MJD@KXpkClih? zni~vwMGkPm#J8-Y@08;#nI_}d8)GTb3s+rJr(Yz+9M9SkH-Lm$%aPqhBrQ&*&kb56 z-ozz{eDHrh!twq8=GCB${QMOYE8kv8s_IV2w}3n#kRo60KQ>>vq%{Ej2<%!l5AepRNLqW)c%rG6 zbA?L6M$TjQmjZDA(bU7(i#)kt>!? z5996_NqrcaHJ1@x$JlbTimqG18d5)oruw5V+{@L)Y9fYH@A9v|E@A2NhnbzRh?S$# z348fxYVhyL?y_FcRNyh)7=U(iI` zt_+$SR=k6)*jv>Ytv8m>_KVy2c$=K3bCU76$!99tw4I6<6vzbAeW3+X{%EWbG(OS7YQ1sOc%K^BaOK;Cqgt)K1STvP8rO0;!hMTWAw2aQyI*{Tw4U6>p) z6m}z>ot@5?v6F7&&8EAAR*WDqEP|xqF3Kv}aJVt<#?NBHB+BvZ_RvX`sAX1+-5xwa zhKJI%nt~Ef1z1cr_dt=vi^QopWO9;i2g`A-z zI<6}WkOw9Zsn6xZH&*g5pVojRo*7dkk(%3Gg$E`Ysw&VlnJ_j~Q`g~|7}4Qs&m%)3 z2uP)Z>tmZUVOOR?js%(FTO?YpMj?^m9qcTuw|2QIp``&#{Mlbw{oebmdGl#fuPtG^ z!8oy_h%EPlkkcE9V+ZE1J~<0Aq%dejrbN?5UK8#&3g zUgfhje-oeVuHa_6p9voRM3@fp^#up=JDR~FVr?no^s#R^r)g&(&8ObkltHrZWkj3I zH$z_zV?DK9J|rdtAeSf!9X^t8PVzZ@UcaqQ_+FvhIiDXq`z$}0?sxsOdQ9MjfB!drzBml9P*bLNDk*sc<43vN z#}VVmyf!c4M&q#3qH*>B((K%erjHuRVQfS*`1fM>4%ynN?~oG~AA(Y%Bz$Bh5x446 zkp+`EI~{A@4)U}W9NAHX$M{){@O9@jv(b{jjg199WIZ&6Sa%Y$dgDPf)7omoCq97? zzd#Zu=8%=_eba?MHhnqO=5WSlCJ^M~OX$!{(*4>g*LJu8T5f_r*8vziX*@|meng~> zB}u8HtkHbMn^ayvgJ_}RbU7X3E%I(@qNH9<(zrAt{R4>27)OjlOR4TwBQqjqeGQtX z+mJ&kSP9Q)$9O7^2GjLx5HYtLk8{H)ZR;!4x6yp+E7s*|`wWOgA%v{#K+bPDL+hQ) zU=dB!78IaqsHfs=A>hyREu13f<+toQ=9ZgVJ?R=f9 z#Vr(X)1~v6k!!u9KAyTR$dBF;vd1g6d z0;C{zQg$Gh5_vicmMvw@5DNu+kD$G*m&|B2az-v##)1qrm526ovA0g_f{Ms-^O)>g z%+9?<=xu!y?D|VuOgLySFQvPVLE;%ldW4zMqK4~QrijM#eDcRN9IWvpWBN2k2Iwi> z^H<(Eq{CsYWW%4*KBT7kQCre^{j(Yi$E_B$nQMB(k`lQ7?_oK&|ps1$F9ifm0Gx_@~S^50K z3{}dBU-TnZu6%|W;WA`FBN*;g&&i?|?0}`=9On!nq$m5j8HTgcbb2#eO2e45_yHn& zYf#xM4;E?;@1>9omOb?ZvtrEDH+J2;=g6Yvz~&?9<7V^til=#GQXraro5^##x~`A> zxpZVzg`6={JHd`p;lOM5ZeCgcDX*{pl-JjP!O~P4cr`F_yg{`eQHKi1GU9vpVO$1f(;($GW5_coBABrrM@B?9Wm7<>jWHZF)?-P^r(>w(bL zVf=P~Iu|VNTM2^aP!>Hlg&=b&Th_b0GAAeq&B$SPw3a>F zPEmg^x{td1(HZvf`pWkyG560ndGs>g{KLb9T=)w={_Wu&^EkpZ61gsz_3TyC^Gbb-X%ia>yb^_HNe6?#AA% z&DyaOI|gHHT)}kFMYj=zKo&`ADO8-h1cXbI<*q zb|nC8&PU`frn>W!IK4TBc1!>G1ef6~dvqqg#v-5^0#zV(;_}Mn74Tq^7ps$B_K8OF_ z4PRzKt2-q4fz$;5y9WMz2tGdwpMML*!1z!QYT+mEz{i!4G8(ie;mHkfOa>{TcEp*t z9iH6;pXb4}@!-(~Bu;+;&u@dOFqr1)n2;vG#a~0HyC;=J?pPd*f(Qy5p*=@h+$)hlL^m>7?1^?52f2Io15 zMw-+b#9>KH4{D>VvK8}SM3oewBt5o_*p>oHZz4n}3we6zGnh0g2x)T#RjroW zBj1h4u83giGY=6~okMB6?Uw6}+&zehAU9-#2`v08NqH%QN1i>)v}rAf>Hsv2GEg?L z=FbzvEE z-7!A+{Y(7$!!J4InarFR6%P65)^^lchd6jrgDNrt51DWitGFMu?JU@2m+w5AHo>8K z2nqquPS~{-4zym*moO5d1UP*dHdI~0UJV6$$i8s(j+R&5^|KaA^aC^Cs&Pk2N~uA4 z1LGz297*!R@%YG`iB4L?S6-$Rx47N8f4mc0GDDR7_3qA4Lb2?K9{ zRN)DPTT98QzPEhp>qcojLJ0SFxrd|1Lv)3gg`sRWAH2PejOu}mN=R^tPU3;t&Yb-w zo!pketi2FWMJJIQ)xzP88`zf9gwAN`-DwtF8Idxd;rj3RV%>VSxUt+claIP6p*ndCMIB-Mn zHu*63CpVzz=iu4e3fcGNLF%j>&?xgG$W{71l<)gRQWrnMLp_lXy7TYG$v2Z(zcz}R z23IN%#i5ZkGA#HCAT1aemRf}&BaSMAgZden#G@<5;_*ElV{5}c{`{X?DX|1HDoWWO zPTI-}(fUpxX>tUr0oYF+2JlKh2&jg=4VM5OBj^E?ppt;tmBR1naTGgbxQp5@_$nF3us0pFN$?q3%dKYsfp6L8iJBN#HCVNpT|M z(>-X+fS@97>BEfEe8bnLwTRc@>npG?afxJd@^s<@oUwIiICnUMtZD(2 z=bmMGVjxO$BYC^m^WNu~G+G3lBIoh+a}O~++y$|$Ma-0m7f; ztqo5C&FuWmFW6{G<@GmK5m>R77k_&Yw@3e%KP~p56Xz4m5?Z^v4U33!sQt!cUe(A;n7BmiO6KVY1)Tc{nS6G=s*kKH^ZF1r!l1{?P)&9c$TH zY3kKxDTAi6Y(Xd$yEc(oXS!(_GCD`|$m&`60{A`23)2Bw$^2vkCz`B)fK(yHYVJq$ z%-T+EtAvSBekdxckp@NMWo;&_&vKWWknib5D2vo6q9XAUv|Kp6lZ^Zpgy6ZXUZ!I2 zKekeCvPTu;A9{)j6`!&3d>E^)Cwsd+uA+*V&dS9J1k1FS&8@^Wh8YW!7#pO*)?UG> zy?Z#R(IYA%dewOF%m~FG+1F_3^X@gvbl>rAWndbp~Ne3k0?S(h5ctw z?;b=cgn)Y|1jT~w676iLgbpAs8bT#dR}Sq18YZ*hlYD>~Q(F5M1(8URAod-xllhQ1 zXA)jj8EpC@og$|ge7i?la3LmXE+Z{D?E2zM((?sIB+n$$S-{rWj85W;r*qfE5*6Mm zkxs21YcG7guL&1oX5UYo`5ZgG_>w(E3KHf_C$!tCkjU|MtLDJQb!qQ^Y|1YhYz5IP(|9;B5KF-^jup#@PkoHV!&C_J zNLKvnhfEJQb9V1R3KX%-c|MJ~0aC0jIqW@LiU~kp$l=|)*tKI1xsKNGDWeGK$Roc) zz-Qv)JT}I~u~hsu_$>7W)B!I3J}3nlo?$)+Kz&gYrhXbuXTNYlyp#;}v)tt*Q4$)Y zCF7%a`EYL|;j@y6b+K!psbU-Nyt|14;}xLBP)W90#_$Ods00b_!zU7MD&kxpU`Fmg zfhDQ2)a~8OF^yqBvJ$qovwZsYS~41}G#^{f+iTXaX3a--V5bOU;d9UNV4M;wEX zEseCa7g!dl=%6Xeu}y3y1tU+-4UhiHE@ zWmy-fZEK|}CyOdc5Yb*;d^R(c=Zer;Z0KtWs4#jF;nP3=&Zev2_?|peQ&O1de{=9o za1JFQQlu=q3WL}aVD=350L&Ec`;>S8_-p>@XD_opUx&nN1PQ)?dMj%`TE~XNS|EZ6 z;m%l_PP29EX}bs6@GW0|yq@(Nwvg5AAh_#1&f33yz(2NE14=?;y&VIu*Wjb>JC0NV zkyD6u_hsCaKoE;Kl-F_t8>s}BfKdFLhv3~^O`@5yleyHJtaNCKX~L6$zS57Pj*^U{ z)Q8Vu-sEX4nBhtBffH2shV2v~%v>@R`RVN(F4y0hauZV}yWV}5O@&5`#hZC=%^KFM zd7CZeW?V+D;)S#{(ysVCyC@t5AeuXA(<$(eiN;IPL94EhI=!2fPc%_}DxW5^4V@;B zQlk$s{z^ohnw)AS!{P&w15P1h2(y)uTjPkkW}*CeCZ)}7)D`B^&^@;bKjH#R6raqY zxML375<@qINuInCm0#_)DdQRhq3kV5nR@{;?%^H`z;h~5A3_i zO&Lz|s{0AOk$MzGI7?o5jzweR7(H<+lVW6O>sxQ%(NcmF!DGe{5g0`DxY76yV)3Kn zw>E)Rig?k!QA;E?G%~=_LYc;M@yeukYf7lVGO7)b-9b zBRY#5cZK94`Zif?7Y&EOETC|fA%NpXQ0C3ZY4J!3k8-Z@W{Tt_xJD-sq%UC0WwkeY zigq8Tb<7OLKEH-H?%U26pMFDw79>hHlyFJfwaa$6$Zy^cvYN4CMX$9N49J}o4t6|U zgU?dNksSrBdS*P!7PaCL4(78v$!xl@3t2UL7V+gf$Zo!!g{~s{l5{?)yL$cFOzoJZzD!w~ zLUhwU)^999Id&;47B8fA!)9`?*Y$d{@`=`-c@WL$O#X?j6JUsWF58Bw0V^3abPn-Jm;}E(kqQI1zjL6+m*S zvPogZQYilfKF)$;Q(l zXUVZ1kEa*D+eT7%h9@mxwEhI!k5{0-8N4C&WO%$6+T8tI-oz%-cKSX3`QHjz`S63x zOI$|UxC8=UewFRrm(*_K^|w!;wE@^LG_(y|1mc>lOH!QNoDn-aFxnj9v(cQnl~Yf= z#N?%OfDR6CKS$dQyriYA8g*TV!$6WgtLdpPI#En;VSZSJFc_l2UxyK zmbIz+40(nXB*u?H8PG=d!A8DcJzyf1_8KZ`1R{gnZdo&y;91F{)ir3pOyR(VD*&8S z$Eef>TK6Q~-#^9{aJ$<|$zy{JGY814xx(2wG( zu6elx4oZ~R2igES8|bSk8 ziufyAo36trcI2`Bu-1M}V+QF({XgT!+EI(TrfqP}7965m4P&s_B*?o}PpUvR0O>PD z&=eMo7s=eTjePg%q>S};U{kl!n!lUPr{qk3Fqv3~Pblw02GQ>H3ld>KDvAWD9EoV^ zDUA`$=u9F~nY54l->7_mM2?G-^rHQARfw4MT{hC%OkS3j;AxZasn4RI#dcG&+wHsp z*Y4v7Bu==HT8+A{^A+3=o~N31@U9lJcQ0=v?nGNuH3bNsjq1QxP- z%}y4@S^Ct^mh7#3vbU_CHOgIj&$^;=BY zCA+cI1rj}h7Wm@^c;lS?`zhCmuz3gBBXgByT>)B{_cr-r8>e{Pnow4HuTO@f`RnhIU6rRqoH2QJz3O$M&73#=$h0O;<~`-kq3e zZ{!js0i(te*gevw28!$CL`{e%#M2G0@QI{8w2)EVO$?SsiZlXI6DJU)FQKf}<{01g z$lOF}y_~4=BMJ2IBy`MpLTojZYAqMRUD%{VqP#r_95IeaMKcwR4lEyuiiqipn5xWS z*TF&@c7z2Lk@rm{E-ZpkGo}#K?bHcQgd{{kc{Y_-6h{lHcvipm2VQyoHGcK%k9lHF z2q4m2(~e1df$T;g@Bx1L@^AR#n}6ZWw|>QJKf5uuw3OT1>_eCF;>-WZ-`@WdKb-6f zts3e~K*(Z#^vpB-=>BV*i0wR;7k~S&{9<_^V4|u-Yu7<}FjL04+ph_p%!EL>V|FQAr{VTG&hb-;XcE|~g@<%CA5;S%K!8aRPk@+!UUIOOKbTTz% z981r`dCFWydpKh1Ei`BEWJ``aNe|2sJFmM0Co%}MLXXOJh(+}!vK&yh3C6IiLLuh0!-~kw0=_#PRhL)L3Jnthhe{{rNMLTs*1VLSvBzd^|9emi44$d*hP@GW&_A zdHTuugh-u;OI^*=Pe05=Zz+hKlpM;SP@X{Qs+G(iW+G?*F*Fx#lM#(t&e+sdq$a8; zKXQ-@eVy1YI1w^=Au~J**u6gwt)+j;U0bZdfQ|OjBD#AUBrcI81RE*JtGg;fE9%bk z$s6l9T;)mP{j(YCt)*z+UwQXP6E<@>oBpzn<7y+p@nZ-V)qJ~UE2q12=ek_h|9vmT zZ7QPUBcQ$#b!`VWT`n6xIZ3_Qo%k`HR2Oz${jR!PGPPd#Nky8^e9EUeU1=XUv54-U1So_=E+ZL_if1 zj{6|y$pwJT`FHnuyzPK%-AE3uOQ6BvxVIp<3}@M+Gx0SRv3=tqYVWoKb>(5CJU$zJ z`ez(=R5(FFK;k0ig=yHg^E9=0V;HIXyt=jnyqfktg~oy5lZUP1^*=v|fBrlC>_3ju z;=s_E0EMv$wYmY_y>yuV5Zzu9oCuq-f|S5QwrBWV~I*}35X!fOK}I!pNhyiKj4vx-hDg9ErI|DQV%(S)x`t>(x}X-oc1n)KPxmQ#NHc z^=)}QX7cYJ{s&3kE{uHUT|RmLc}6+i{Y&_sh}@;W{xGe&{sTL~DU_*6u&1nEtRiVaA`=NVyh4kA66s?U$))aE`kS#eP}Q%h47)fM%aMb}vL9Fh?*bQ+KJI?JX#YD_Nv zdJsua`h^i2?0FCKASdwtS^Q*TU)NH-0Sy~AOmL0DkCP-lok7*06C}?m!9(hr>~2@H zULiz>dLwtKliN4u!K0Y{*mQ>ZcZYyW@r|FtoZ)6pA1~{Hz^AA4cQX=D_=XX6PXjwC zenV&RWLv<_l*h^?|H;>Ci%Vg+$OY~IFv1_+hPiLUw>mieZ^z#ss2yJ$YT$#<9M|Uk zAN(T+EC6PB|9!{xxo^YVcj2wW&?JJn5}tn>=C6ka5h{;4?s@en*qo2e{?^&<6Hwc( zyw0hw9M|6~=@^r4@kdCWQHFbWL0Lrv6P|vWiBZa1iy?$R%KPirv;Kdl5_FG(QK@$r zQ6XN)TmbL37-*@=kHEk{eBC5nt{S}hO=9jCfn%G~$ZfLULRVfSJn=G5jqts(!jbyW zf8rv3o|3{V&(CFaggb4CkJPI#4=#9tS5s1W`MEiaj%vG2xAE5*-WAczdg-T343pmx zxsiK^5*6Y}cd8j_P$^UXxP{l(Z(zxL2N^rS?Et+)~!H8)q4WTvGwd>ly?lA+4U7<>`Uid zogKmDAiJo$!JVLlnsZRlO4w}8aIzlogC&dLHwl0UM{8QUZZLZa{AR4<-^^lXWDf|J zLwX?`&_Zx4=T$VqIW3@Y{C~Qx>zMT#wV#KQYf*INqLFyfXGk#moNQ`cd3{#`6Mm6I z1*%*C?;bS%?vdk|^RPb`wr=N4U3YI~N%4v7g-~;z!dfjQC-#$3+r~3xK7{+*sm#o! zy0M`dxpfrNTW??V3WE|Pi#5*6V~x5o>uUL5}*nKdh8sjT&+bYDNpWOeiny9P)L zM*7Yu$C%!aa$`H?GiCx$%o~c&T{_0L+THy1f3{L+4Pj7hM|N{;G#2Hd>otN2qoPr{ zz@3Y3#lLFLP0BzBu7I@KRvv~43;;?{NkDX_ZZx-WUHDn$bgxWCE_>vmQE)EZ0S+VT zK<~V6(tsL>j0fWx$XA0UhAvV@BEI9kC&@gK^aK8M1s0R~4`7gwhI9Fim-!Yv2Qd4k zpEGB82ue#G*}K-T;>#1%Sq0o;Ch@{c3mF^bf!I*M-nAdI`d}48pT&Im)?|DEqMqTy zwa)-rQkVXU_2wD8^X>vdN>=jfAHK)yiTC--^Z*J!`33)ZvLA1J`b!3w3OJhQ%cvm% z=uWQWFU!)nX)wn#kvD&!oo?_FKKovFnND57Wb)7h~|u~D?;Q?`|u9euj@XWsMUsN1-jebrV_L^J*QDTv!wl3HrM zso_+Hj%N0h2ugNr;zYIimU&2T8O-C0ljsEy_zbU%1!&;Jy0sk9*Z={kLW<4Of#{j7 ziK+$(_s0gJD5*di+8aNcmNPAFN!cy%_rf%k`DzrgG585OF6>VwJ*OTaY$A(it4RCT zR*KDl;7R<#XBknlf%WGjS@1)=H#vN3RrFZqPfs9Brn_oxC7yj4H+2F-Lsi(DN;sMJ z9mmTIh>GZ&%EV3mQ1?J0Bm6J<&E4)Km@5O&UNSVnwe*rHDQT2|*^lpEpUFX!J7Qn~ zWBPh?>|Z|E+R>+5#CRTmY$O49Gl1?jnuT-gXiAz*dk0fzUXxx6(?rrO*uY>kSbSs4jNo>UAC22}s_{xWtAoh$<-vj_-%EB7@?~ zhDAZ|`6)a$JOpd*Ar2MDh@Y{D>3vlQ@)+j*`Z>l%SvZ^aJ-LcF9)3B6iNR8A^_isY zzla&YkjMUAJJ_-PJF*(uY4nt_gg0lC(<~5h|C1~l;?cewq#Lxe*5pzRcmxHY6lC~D z1|R@6`5Md}8Xp1oz$n7~ls&(4r`uMjl88_p>8qFXSy~-YNfU_kaLlZ^WE&qZ-$btI z8c<^_B||Nv|A=T*f&}mW_Y-B#=Uf|LMjkYRnKR<3PTS0(GGmvN(c2r(^2J9hNw2k` zJ+y|8mMvr1vegb?r-)?gOE2+gKPAezxxAQ?LQ2Xj%#KmC)hXw9A4b=I29Hji&iqG0 zDchIMMT6LV{0VY`yvx|Rayg&vswO&l3WI$lSR2aF3cZN-cWO380MWiyYAc(t+>CeU z+{W~=ar*03e0jK;*h_OOc+&ro$qY1~;_Ee^vn@x-i0Kaz?bfzT{Gb@Q1rQk(fZLsj z2Dxb&|KbHy8H32El7J||pz)xwS)|v5JpA(paK5hX7gK~JGXIGO2<{9IHZ3Q=_?TtO zma$?>A;#|HSMcG!C!QoJ{0H=&XhNf|#w6NlDOb~IyINq4oWTH-=*%RmEbA+NGO}e znextOo(S#{B*cgm?JDO=-m47XFdx7|!R{|ue#FFA-d1XLCL}=vNazKqx3Y5e9=z;s2%5=TWcWwg3T0slSxIr6;2;VJ!}$v1LPFfc0wj3DuN;cozbD&OoEBN&mJJj zKa!1aN7L9^1-NNQe8@`fM8ZiEdPf;O)XY1klo$y{pAMRzE2hXsUSnBw2@2Mme@Dd3*~W4y0iH<)0- z<1o?PxxMSqb)2aMR1oO_aRGoCvg*5@@s`sg$f=aw($IPKP9W5fOZHVcLI(189i{;} zLtkFTyRXfpk285fqVz%u&TZG#yfxVmkl^Mazjc4jX2FIHgU+$REq7P6ug@E`vsQ6n zdoFh6eaxOVgGo_fIhV=_P3ORwrM?=C(FN=}2kxwxEKbX3w_+0or0rO>_NMdf{=S0H z#Nl{e*wr?mF;{YYe;#hblZf}f)7D1BP`HzIYuB>&n$MT}E3rs>F>=;qdTVyGX2lA= zJZfX;^r^&qOS+Fg(RO7XL<;GyWL!9+Yw9y^&<=gsZ+& zC0t;DDVC#o?MS*Ym2oI7o90fbYA<4XiyZuXHE9je3{>pmk4smRf5+Q!Du0A3q~4 zr-_i^vq|~u8_bDprNFAT@y_c@S-Ny7OP4O;^?FKp$v{v-Nd2kV*Xk^$Xs!n zY-1$DEut z+Xd_dltF_@nmmaI`uSe9hb<3`r@wn88AZ+46${7%dK0co=d(Yh@S6?gAc-Y8DIBS( z$q8hQRF@Q?t~H{sE~T>Bsd7cTqc4dJi69`A3eL+`hf+5`S$8)(^B3o3r_LzjG75E}fnE9apKg<|@>cjaXd3 z?xse|YL_6tG^Nt6F+7UUM5f#)a$?gqa=gbfd8mIoA_O)Xa(1!#q@1yjCKKl==^7qH zhw~>$gf7V_5~Ok@q8UT0NSfX(B9%$o=)bf40TMYLZqm!j&r=~{HaNF}ZCbL==m>jY zBmp&N$gQ{E67QnbfYGkN^U}FZ5;r_#W}0v5r(Z-Y4Hc-XTW(WvlO6x0Y~l=YY~SH~ z9oUtI7Lgd{&*8%!5R%8@WvNW-XKhnH>oc~pF0H78xxY@iarYz4xSwxw?8xtE60nFx zTgd4nM>(gyy@1{(CK0D;?JpgRXDq9||g*v|Obe&L4pfGo9GPAD$iqUhUAF)}s5^yRi>V?OjcR zikLB@=@$ruODHYcMX3c4+z3yIh2q_mUX#QvsNz}t*6Spz)SNtAggibR5YbjPVb)zB zqYk1%=JMK-0a#*&5G|f&#nQjAuhxdOzL;_&ghkBe)g@yI?k#d?#e1yDDx=yA;o;MH z{%6s6j`6+zUEN3X>K}Y)i3tJBlo#q8VH8C9g_O(x@R^*k6LI|0tsDWu$6u)x{Mt>B><{ zB8d)k@7~mX!9wWpCiJI9QEj|_4#K8f#QoeT@*4y?c37R2x>K9^LcWlvAByPEmZm5K zF=4Wiuf9ElTy_V4u1qvik(r5F2h@s$gpDECm3QZ&I}*9Oq~7FL!@~J<#p(*?i?W5u=0HW3y-geXzX(Jfm!b*XwzpT(Mgq*2hQqIY~W)RdyGYR0b5V*R?~ z)L6WUAL2_zUd#3Gs?Xwt&W}K;i1zdse39Ag7&B8bX}gOs0FBw-aiHAXzIM`J9+(mb zwj8z}uIZ3>DH>`~muWG%((KwHm?5)Ke&f1hO^+ACz%*!qN>Kw`3RD^jD6bt$?yyNF4t9JkFF|i)1>%yVy)_+IQqAMlkQ`ro@S``i;zww}NLi4!Sqae9SNYe31HfNQoIxm^7%>wdh@Y z4Lv|RxDh#S9+N}z*t+EeHRc~Ff7hPlcU?c844(fq!%M$n-BHawvdt#NBRHB!KgB&f ze=1@geu~HMZ@bb@tl!7$ukIxIgVzb$_)FeAGK4o*q!91cvzPF&VZ&V6g+Eg2@yCx@ ztNU``^BxN9x=(B35i^8fKvPiK_Av#QgBv%h_4+j2hIqN=snL%!@ zCGMggY{h$eRP8aAXnt>$4mbY)Tu@5qv1Gv00000NkvXXu0mjf?jy%( literal 0 HcmV?d00001 diff --git a/docs/source/_static/img/pl-truncate3.png b/docs/source/_static/img/pl-truncate3.png new file mode 100644 index 0000000000000000000000000000000000000000..83e5b21711e983bea3a9154238604a42bc8ff3b6 GIT binary patch literal 3093 zcmV+w4C?cVP)*tfCQL$eftVOzrAQNmg;fwyk>b&{t%^z!QCLK9mBn4L z0A~>pmtI_tm5%g;rgTU^QcN--A$2A_)8GCv0|`i(#E@j+e$L4`GjHDSz4yECzPaD| z{qFrm`tBSuw=sEV@8#HcZ7H)MaFdF%T=H^?u{715A$brna2&Ir>@059HHeFglSC^B z1OmaGi{f^7(7V$S20W69k5b%hcTxL>5!uoYb(53`Dnk2=<8L<;cIIT3PkV=N^j2|0 z1pEU6 zkAXKL-FMP6`Va%GN_K9HpvutzjjDZ=xgU)rOuveW)3=gu|NrY4O22S)vP@=f8MY=9 z!BZ04;{~Qa*BPIi?J8R-D>9&H;ET8B9bnl%7qBZ^ruAPJ@xh>A_kKay%g~=Z#PKW* zU54~U=P8130)apvs2gra?lztTM@=bchi|`i3cHqC2&jOq8(??Im3FZmp|u3Z;$cm8 z4f7m0Uj?Usxq6U;q#W04BcxT;kE7mk`{_UUA{sGfx{caji>`$a$|llP13Ev=-(K#4 zf0Jx2JH``hm^pbqyRvI1C>@nKoZh#WQ-uyB(LV(Ofk4m@;!e^){wYYUG4R;S;b<`s z2-C*GlvqH9!}&D~l`wn=OzGu(dnN-eCCRjE*qa6k1_)JGo5&_OWdJ;!?~fPOpR>N* z;<!8(H8f@QohL)QSCQ-bCBUUa*r7KiEK;EsRdBHMJ6jrRm8S zn|Gyuw@AE2-xLT00>SS@<<3r;5D3kI9R)R_4Q8kU(15oBZLio=LPsB~>N7l9sV6%D2nkfH}`6sYUPU73L1d+2K!$lin?ZnnlkYSw|_ z<6h#CE@5b_g(PiX#p3@Sq0lDb88wI(#yvqVoey$JD!bP#Waa)`q_)p6fA(Mk0CZ0? zf6dc?fi2TtXT4>pGhLgxobhk(!FSB3%y}r7v?Z_b&e6M=xpWd8%&8ns4xn31Fvg?H znX_OAIX9X%)~q|Tu48Asdm2mMx(8Fjd%XJDIaK$~VCm#&N_Wj<;uk5H-E1{Ya4RAl znPe52a5Q)qmGEvqf+=tOiIz?HK~gsF!4o42DeF6&R{N$hHND`fJsgy$|3t#+0#p%Q z>D@((m*o5f6cF8yen>bLDcJ1|~cFxh)uqYRDwHTEt0kIi2LVDkCM! zke2Cu9S~HlCM7NSRY1#j;HaUp3bLRaxT_6BC?GEb%IbC6l!M>_yXoD%p0S1u9!$0r#ZV0ne;&<1K6cmR0>GLql4l1(sl-jTKQy>rs1cLg)t;ii_XxkqGQ@~iO!4+oDbG=r>&~~7xaK0_~ zgnm)r7Y(Sg|JfPMqZSMI{s5y$Mok#yC0vGvF?Hf)HbPv16#6&&W9)zu3s zR9ZJKt>PfdmwZ9-koJ5sI-H1hL8y*bV6B54r+*?L`w2!xb*G(QDlNN*ftDvbFh1c&%HPFh~_ zBs?YtLvkU?@J=*Wi7y|4Kp+s@8r+MASV0PfAwji2L8sQv6|h0E(9^GadzBeRj)s9= zuJo=`b8(^o@P<}C&@LFTKw@$IJ6>_TE9WlWXoRgIW&1%&P;<|#3;1aAFxt8%L@6}B zXuy?rUCHdhfq()}ANB8#!Pu?Xu~TJqCaTq59_}6UT6VTv5;mX5q3O)<2Zu692i8+t zI8szMab_*fqsY{7iCMWSl^@m~q)xYA;Y(B-nfVs*ZV$~*#rKp?nPxEDW=!LEQPU%=oRs-$XVDC11O0w`+AMI}7;Fl4NS)hFOU z59s6i$Xw-2>pHApcIr9oWsv5|olX0=`Y>k;L2glhPMZ>YvU{_Z8|xIA(qoHx>)m7? zAN44Mx(sJRTr6SJX0hqgF1Z_-`|m>-9RLnY1*P>@5&D|TCY5;ldLdVoV|KV>=fH4e z14o~FlWq?`0F)EI=@g}PxJX-R4*IUfm24O>Yc?}17( zi6>ql5C{afJ?=&BTm!nYh7?q2zze_xl@6$MrCXbMUv*Fg^tc;vz_zsNd3rXhOBC9< zys*Ji0{MUx0DbR<-mY49`MFSErD!e5_pM_0uz;aSzIi4;NiH)F@DSTqAy?_Qpe@ z!Z*aZ3dLGwaW=m)i2Eje#`4dXvSRLEh`xGR<#p`j@+3CJyOQaJd)by!``R-0@?7*6 zOR+Xey*nHE2}_u>CaF$y<|MB`!cFmff6jrrQA>8qSPf|$KXjN=`ZDa|yGI}p2n4r? zTFv}X)!19<16=|jqZt0N25c9d+q+*Q!WPuh8JWvU2Xm-PFu253&4n3NrOqf`STwxW z2kcpJG80M+T(PqC>>-$c#-(Y3;HjSA>qhR>qjgeLIj^>%!GZ^5l=jg$7 z)X`c%#&EF=i}4pu7lJNq1e2$Az#0`pqTtTpx$@fK-RK&&o}^zb zH@CxzL0<#}qTjv?eeG6K;Hb<;-{}8}%tFqt1$>~H!aF0|*Q(RXQVaSIHu2S_6R1_~ z8&TVt$T@Qcy%8vo2@CH@sF-&r5C{Z;Ssl-;aBOjHK-Rj714WIIJ0b`^gFp>dk~l$Dot-9R6Vg$BJC4u`-cW zpYI^G)SEWZk&vH-KDQi4Wg_d>{!G5rkLZ{Ha*`{4d*aGOju-<8QOX#OuVdYra;NX+ z40h~HrwS-d`jLcei+gs;Q0^Pj4(#XHd@#RO5hJ;(0R08S^;5tNDvr!#ZuyiCH)htZ z9E-z_Z}&%tw&YRSnE#1P(h%G#6b%yIS{;63(w#sc5D0G7kn`O+S68~5Sre9$Y_1bST#QCmeI5D4yIZu`WL zEw4QZtDCs6yLH+fpQso@fx^_R>Zs{WaF~r`HAP_u;3y+${l8eCd5Z~s!*A@pe;@w7 ztQQKE3dM_ySo@rGW%OAf5C{Z<+i~>2-6MC~=8mGIQUo$;Lwd@2%=RYUEmZ!j_+q}e jS=T^B?gRpX;LhfM1)OK`ui|<_00000NkvXXu0mjfbI9Fu literal 0 HcmV?d00001 diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 07ba4f2a..277cf4c6 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -9,6 +9,33 @@ in some way (e.g. shell prompts and tmux themes). The project is currently in beta, and most of the functionality in the old vimscript project is already implemented. +Screenshots +----------- + +**Mode-dependent highlighting** + +* .. image:: _static/img/pl-mode-normal.png + :alt: Normal mode +* .. image:: _static/img/pl-mode-insert.png + :alt: Insert mode +* .. image:: _static/img/pl-mode-visual.png + :alt: Visual mode +* .. image:: _static/img/pl-mode-replace.png + :alt: Replace mode + +**Automatic truncation of segments in small windows** + +* .. image:: _static/img/pl-truncate1.png + :alt: Truncation illustration +* .. image:: _static/img/pl-truncate2.png + :alt: Truncation illustration +* .. image:: _static/img/pl-truncate3.png + :alt: Truncation illustration + +The font in the screenshots is `Pragmata Pro`_ by Fabrizio Schiavi. + +.. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm + Feature highlights ------------------ From 75070451439a0f49a505467f97fcf304b6cc8927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 16:05:55 +0100 Subject: [PATCH 0116/1472] Add screenshots to README --- README.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.rst b/README.rst index dd61a3eb..c5404f76 100644 --- a/README.rst +++ b/README.rst @@ -11,3 +11,26 @@ project is already implemented. Check out the `documentation `_ for more information and installation instructions. + +Screenshots +----------- + +**Mode-dependent highlighting** + +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-normal.png + :alt: Normal mode +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-insert.png + :alt: Insert mode +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-visual.png + :alt: Visual mode +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-replace.png + :alt: Replace mode + +**Automatic truncation of segments in small windows** + +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate1.png + :alt: Truncation illustration +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate2.png + :alt: Truncation illustration +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate3.png + :alt: Truncation illustration From def9091662a2e3c74af840ad6fc23602ad3b8101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 17:18:37 +0100 Subject: [PATCH 0117/1472] Update installation instructions Closes #13. --- docs/source/overview.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 495be83b..b881c9e5 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -19,14 +19,21 @@ Installation ------------ Powerline is intended to be installed as a system-wide Python package that -can be easily included in other projects. +can be easily included in other projects. It can be installed by running the +following command as root:: + + pip install https://github.com/Lokaltog/powerline/tarball/develop + +Or, if you use ``easy_install``:: + + easy_install https://github.com/Lokaltog/powerline/tarball/develop + +.. note:: Make sure that you install the package for Python 2. For distros + like Arch Linux you'll have to run ``pip2`` instead of ``pip``. Powerline is available `on the AUR `_ for Arch Linux users. -.. note:: This project is currently unavailable on the PyPI due to a naming - conflict with an unrelated project. - Usage ----- From 7c1d48a2fd4a671964495072e2e3248eef10f3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 22:05:28 +0100 Subject: [PATCH 0118/1472] Improve font patcher For some reason the font patcher doesn't work well unless the target font has an em size of 2048 while the font is being patched. This patch also improves font patching in general, due to the more correct calculation of font metrics. Refs #14. --- powerline/fontpatcher/fontpatcher.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/powerline/fontpatcher/fontpatcher.py b/powerline/fontpatcher/fontpatcher.py index 6ff32237..b5ae9dd4 100755 --- a/powerline/fontpatcher/fontpatcher.py +++ b/powerline/fontpatcher/fontpatcher.py @@ -37,6 +37,9 @@ class FontPatcher(object): for target_font in self.target_fonts: source_font = self.source_font + target_font_em_original = target_font.em + target_font.em = 2048 + target_font.encoding = 'ISO10646' # Rename font @@ -74,8 +77,7 @@ class FontPatcher(object): # Find source and target midpoints for translating x_diff = target_bb[0] - source_bb[0] - y_diff = (target_bb[3] + target_bb[1]) - (source_bb[3] + source_bb[1]) - + y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) @@ -97,6 +99,8 @@ class FontPatcher(object): # Transform the glyph target_font.transform(transform) + target_font.em = target_font_em_original + # Generate patched font target_font.generate('{0}.otf'.format(target_font.fullname)) From e5f5df8a1274e66758012db48e87fb48a1029980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 22:28:27 +0100 Subject: [PATCH 0119/1472] Correctly handle glyph widths when patching fonts --- powerline/fontpatcher/fontpatcher.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/powerline/fontpatcher/fontpatcher.py b/powerline/fontpatcher/fontpatcher.py index b5ae9dd4..643263c9 100755 --- a/powerline/fontpatcher/fontpatcher.py +++ b/powerline/fontpatcher/fontpatcher.py @@ -52,6 +52,7 @@ class FontPatcher(object): source_bb = source_font['block'].boundingBox() target_bb = [0, 0, 0, 0] + target_font_width = 0 # Find the biggest char width and height in the Latin-1 extended range and the box drawing range # This isn't ideal, but it works fairly well - some fonts may need tuning after patching @@ -61,6 +62,9 @@ class FontPatcher(object): except TypeError: continue + if not target_font_width: + target_font_width = target_font[cp].width + if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: @@ -99,6 +103,9 @@ class FontPatcher(object): # Transform the glyph target_font.transform(transform) + # Reset the font's glyph width so it's still considered monospaced + target_font[source_glyph.unicode].width = target_font_width + target_font.em = target_font_em_original # Generate patched font From 366fefdfd3f225085c2cb160451b4b3754c850ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 18 Dec 2012 22:28:43 +0100 Subject: [PATCH 0120/1472] Update fontpatcher symbol font metrics --- powerline/fontpatcher/fontpatcher-symbols.sfd | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/powerline/fontpatcher/fontpatcher-symbols.sfd b/powerline/fontpatcher/fontpatcher-symbols.sfd index 8102ff7f..c65c6490 100644 --- a/powerline/fontpatcher/fontpatcher-symbols.sfd +++ b/powerline/fontpatcher/fontpatcher-symbols.sfd @@ -9,8 +9,8 @@ Version: 001.000 ItalicAngle: 0 UnderlinePosition: -100 UnderlineWidth: 50 -Ascent: 800 -Descent: 200 +Ascent: 1638 +Descent: 410 LayerCount: 2 Layer: 0 0 "Back" 1 Layer: 1 0 "Fore" 0 @@ -19,7 +19,7 @@ OS2Version: 0 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 1 CreationTime: 1355758773 -ModificationTime: 1355765025 +ModificationTime: 1355865048 OS2TypoAscent: 0 OS2TypoAOffset: 1 OS2TypoDescent: 0 @@ -51,7 +51,6 @@ BeginChars: 65536 8 StartChar: uniE0A0 Encoding: 57504 57504 0 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -87,7 +86,6 @@ EndChar StartChar: uniE0A1 Encoding: 57505 57505 1 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -118,7 +116,6 @@ EndChar StartChar: uniE0A2 Encoding: 57506 57506 2 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -161,7 +158,6 @@ EndChar StartChar: uniE0B0 Encoding: 57520 57520 3 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -176,7 +172,6 @@ EndChar StartChar: uniE0B1 Encoding: 57521 57521 4 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -194,7 +189,6 @@ EndChar StartChar: uniE0B2 Encoding: 57522 57522 5 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -209,7 +203,6 @@ EndChar StartChar: uniE0B3 Encoding: 57523 57523 6 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore @@ -227,7 +220,6 @@ EndChar StartChar: block Encoding: 9608 9608 7 Width: 1060 -VWidth: 2048 Flags: HW LayerCount: 2 Fore From 69f726af1a116dbd0c7af2fc502f0bb615ff2cc9 Mon Sep 17 00:00:00 2001 From: ZyX-I Date: Wed, 19 Dec 2012 08:36:41 +0400 Subject: [PATCH 0121/1472] Purge out the requirement to use sudo There is no need in regenerating font cache in *all* directories. Make documentation suggest to regenerate just `~/.fonts` cache which actually changed. --- docs/source/fontpatching.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index d49b4438..8b53dedf 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -65,7 +65,7 @@ Linux 4. Update your font cache:: - $ sudo fc-cache -vf + $ fc-cache -vf ~/.fonts If you're using vim in a terminal you may need to close all open terminal windows after updating the font cache. From 50fa8a33ca951db7666fc5df0583e4287895104d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 19 Dec 2012 14:46:14 +0100 Subject: [PATCH 0122/1472] Update docs and README --- README.rst | 4 +++ docs/source/configuration.rst | 28 ++++++++++++++++ docs/source/fontpatching.rst | 14 +++++--- docs/source/overview.rst | 34 +++++++++++-------- docs/source/troubleshooting.rst | 59 +++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index c5404f76..2221ef8d 100644 --- a/README.rst +++ b/README.rst @@ -34,3 +34,7 @@ Screenshots :alt: Truncation illustration * .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate3.png :alt: Truncation illustration + +The font in the screenshots is `Pragmata Pro`_ by Fabrizio Schiavi. + +.. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 6e79ebdf..f27415e8 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -1,2 +1,30 @@ Configuration ============= + +Powerline is configured with one main configuration file, and with separate +configuration files for themes and colorschemes. All configuration files are +written in JSON, with the excepton of segment definitions, which are written +in Python. + +Powerline provides default configurations in the following locations: + +`Main configuration`_ + ``powerline/config.json`` +`Color scheme`_ + ``powerline/colorschemes/default.json`` +`Theme`_ + ``powerline/themes/{extension}/default.json`` + +The default configuration files are stored in the main package. User +configuration files are stored in ``$XDG_CONFIG_HOME/powerline`` for Linux +users, and in ``~/.config/powerline`` for OS X users. This usually +corresponds to ``~/.config/powerline`` on both platforms. + +Main configuration +------------------ + +Color scheme +------------ + +Theme +----- diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index 8b53dedf..513a8b13 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -1,12 +1,15 @@ -Font patcher -============ +.. _font-patching: + +Font patching +============= Powerline provides a font patcher for custom glyphs like the segment dividers (arrows), branch symbol, padlock symbol, etc. The font patcher requires FontForge with Python bindings to work. -Powerline stores all special glyphs in the Unicode *Private Use Area* -(``U+E000``-``U+F8FF``). +Check out the `powerline-fonts +`_ repository on GitHub for +patched versions of some popular programming fonts. .. warning:: The code points have changed in this version of Powerline! This means that you either have to patch your font again, or change the glyphs @@ -20,6 +23,9 @@ Powerline stores all special glyphs in the Unicode *Private Use Area* Glyph table ----------- +Powerline stores all special glyphs in the Unicode *Private Use Area* +(``U+E000``-``U+F8FF``). + ========== ===== =========== Code point Glyph Description ========== ===== =========== diff --git a/docs/source/overview.rst b/docs/source/overview.rst index b881c9e5..9acde746 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -18,21 +18,26 @@ Vim version 7.3.661 or newer is recommended for performance reasons. Installation ------------ -Powerline is intended to be installed as a system-wide Python package that -can be easily included in other projects. It can be installed by running the -following command as root:: +Installing with ``pip`` +^^^^^^^^^^^^^^^^^^^^^^^ + +To install Powerline system-wide, run the following command as root:: pip install https://github.com/Lokaltog/powerline/tarball/develop -Or, if you use ``easy_install``:: - - easy_install https://github.com/Lokaltog/powerline/tarball/develop +If you don't have root access or don't want to install Powerline +system-wide, install with ``pip install --user`` instead. .. note:: Make sure that you install the package for Python 2. For distros like Arch Linux you'll have to run ``pip2`` instead of ``pip``. -Powerline is available `on the AUR -`_ for Arch Linux users. +Distribution-specific packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following distribution-specific packages are officially supported, and +they provide an easy way of installing and upgrading Powerline: + +* `Arch Linux (AUR) `_ Usage ----- @@ -40,16 +45,17 @@ Usage Vim usage ^^^^^^^^^ -If Powerline is installed as a system-wide Python package, you can enable -the plugin by adding the following line to your ``vimrc``:: +If Powerline is installed as a Python package, you can enable the vim plugin +by adding the following line to your ``vimrc``:: python from powerline.ext.vim import source_plugin; source_plugin() -If Powerline is installed outside Python's search path (e.g. by having the -git repo in your dotfiles folder) you'll have to source the vim plugin file -with an absolute path to the plugin location. +If Powerline is installed somewhere other than Python's site-packages +directories (e.g. by having the git repo in your dotfiles directory) you'll +have to source the vim plugin file with an absolute path to the plugin +location. Add the following line to your ``vimrc``, where ``{path}`` is the path to -the main Powerline project folder:: +the main Powerline project directory:: source {path}/powerline/ext/vim/powerline.vim diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 4edddddf..c153dc3b 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -1,2 +1,61 @@ Troubleshooting =============== + +I can't see any fancy symbols, what's wrong? + Make sure that you've configured gvim or your terminal emulator to use + a patched font (see :ref:`font-patching`). + + Make sure that vim is compiled with the ``--with-features=big`` flag. + + If you're using rxvt-unicode, make sure that it's compiled with the + ``--enable-unicode3`` flag. + + You need to set your ``LANG`` and ``LC_*`` environment variables to + a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's + documentation for information about setting these variables correctly. + +The fancy symbols look a bit blurry or "off"! + Make sure that you have patched all variants of your font (i.e. both the + regular and the bold font files). + +I'm unable to patch my font, what should I do? + Font patching is only known to work on most Linux and OS X machines. If + you have followed the instructions on :ref:`font-patching` and still + have problems, please submit an issue on GitHub. + + You could also check out the `powerline-fonts + `_ repository on GitHub for + patched versions of some popular programming fonts. + +The colors are weird in the default OS X Terminal app! + The default OS X Terminal app is known to have some issues with the + Powerline colors. Please use another terminal emulator. iTerm2 should + work fine. + + The arrows may have the wrong colors if you have changed the "minimum + contrast" slider in the color tab of your OS X settings. + +I'm using tmux and Powerline looks like crap, what's wrong? + You need to tell tmux that it has 256-color capabilities. Add this to + your ``.tmux.conf`` to solve this issue:: + + set -g default-terminal "screen-256color" + + If you use iTerm2, make sure that you have enabled the setting 'Set + locale variables automatically' in Profiles > Terminal > Environment. + +Vim-specific issues +------------------- + +The statusline has strange characters like ``^B`` in it! + Please add ``set encoding=utf-8`` to your ``vimrc``. + +The statusline has a lot of ``^`` or underline characters in it! + You need to configure the ``fillchars`` setting to disable statusline + fillchars (see ``:h fillchars`` for details). Add this to your + ``vimrc`` to solve this issue:: + + set fillchars+=stl:\ ,stlnc:\ + +The statusline is hidden/only appears in split windows! + Make sure that you have ``set laststatus=2`` in your ``vimrc``. From 9cc1233307471acf26dcdf8685c571f8da450368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 20 Dec 2012 15:47:01 +0100 Subject: [PATCH 0123/1472] Add configuration docs --- docs/source/configuration.rst | 228 ++++++++++++++++++++++++++++++++-- 1 file changed, 221 insertions(+), 7 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index f27415e8..4b55e83f 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -3,16 +3,16 @@ Configuration Powerline is configured with one main configuration file, and with separate configuration files for themes and colorschemes. All configuration files are -written in JSON, with the excepton of segment definitions, which are written -in Python. +written in JSON, with the exception of segment definitions, which are +written in Python. Powerline provides default configurations in the following locations: `Main configuration`_ ``powerline/config.json`` -`Color scheme`_ +`Colorschemes`_ ``powerline/colorschemes/default.json`` -`Theme`_ +`Themes`_ ``powerline/themes/{extension}/default.json`` The default configuration files are stored in the main package. User @@ -20,11 +20,225 @@ configuration files are stored in ``$XDG_CONFIG_HOME/powerline`` for Linux users, and in ``~/.config/powerline`` for OS X users. This usually corresponds to ``~/.config/powerline`` on both platforms. +The easiest way of creating your own version of any configuration file is to +copy the configuration file from the main package to the corresponding path +in your user-specific config directory and make your changes to the new +file. Example: + +.. code-block:: sh + + $ cp /path/to/powerline/colorschemes/default.json \ + ~/.config/powerline/colorschemes/mycolorscheme.json + + $ vim ~/.config/powerline/colorschemes/mycolorscheme.json + +.. note:: If you're creating a custom colorscheme or theme, remember to + rename it and update your main configuration to use the new + colorscheme/theme! + Main configuration ------------------ -Color scheme +:Location: ``powerline/config.json`` + +The main configuration file defines some common options that applies to all +extensions, as well as some extension-specific options like themes and +colorschemes. + +Common configuration +^^^^^^^^^^^^^^^^^^^^ + +``dividers`` + Defines the dividers used in all Powerline extensions. This option + should usually only be changed if you don't have a patched font, or if + you use a font patched with the legacy font patcher. + + The ``hard`` dividers are used to divide segments with different + background colors, while the ``soft`` dividers are used to divide + segments with the same background color. + +Extension-specific configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``colorscheme`` + Defines the colorscheme used for this extension. + +``theme`` + Defines the theme used for this extension. + +``local_themes`` + Defines themes used when certain conditions are met, e.g. for + buffer-specific statuslines in vim. Requires a custom matcher and theme. + +Colorschemes ------------ -Theme ------ +:Location: ``powerline/colorschemes/{name}.json`` + +``name`` + Name of the colorscheme. + +``colors`` + .. _config-colorscheme-colors: + + Color definitions, consisting of a dict where the key is the name of the + color, and the value is one of the following: + + * A cterm color index. + * A list of two integers, where the first integer is a cterm color + index, and the second is an RGB/hex color. This is useful for + colorschemes that use colors that aren't available in color terminals. + +``groups`` + .. _config-colorscheme-groups: + + Segment highlighting groups, consisting of a dict where the key is the + name of the highlighting group (usually the function name for function + segments), and the value is a dict that defines the foreground color, + background color and optional attributes: + + ``fg`` + Foreground color. Must be defined in :ref:`colors + `. + + ``bg`` + Background color. Must be defined in :ref:`colors + `. + + ``attr`` + Optional list of attributes. Valid values are one or more of + ``bold``, ``italic`` and ``underline``. Note that some attributes + may be unavailable in some applications or terminal emulators. + +``mode_translations`` + Mode-specific highlighting for extensions that support it (e.g. the vim + extension). It's an easy way of changing a color in a specific mode. + Consists of a dict where the key is the mode and the value is a dict + with the following options: + + ``colors`` + A dict where the key is the color to be translated in this mode, and + the value is the new color. Both the key and the value must be + defined in :ref:`colors `. + + ``groups`` + Segment highlighting groups for this mode. Same syntax as the main + :ref:`groups ` option. + +Themes +------ + +:Location: ``powerline/themes/{extension}/{name}.json`` + +``name`` + Name of the theme. + +``segments`` + A dict with a ``left`` and a ``right`` list, consisting of segment + dicts. Each segment has the following options: + + ``type`` + The segment type. Can be one of ``function`` (default), ``string`` + or ``filler``: + + ``function`` + The segment contents is the return value of the function defined + in the :ref:`name option `. + + ``string`` + A static string segment where the contents is defined in the + :ref:`contents option `, and the + highlighting group is defined in the :ref:`highlight option + `. + + ``filler`` + If the statusline is rendered with a specific width, remaining + whitespace is distributed among filler segments. The + highlighting group is defined in the :ref:`highlight option + `. + + ``module`` + .. _config-themes-seg-module: + + Function module, only required for function segments. Defaults to + ``core``. + + ``name`` + .. _config-themes-seg-name: + + Function name, only required for function segments. + + ``highlight`` + .. _config-themes-seg-highlight: + + Highlighting group for this segment. Consists of a prioritized list + of highlighting groups, where the first highlighting group that is + available in the colorscheme is used. + + ``before`` + A string which will be prepended to the segment contents. + + ``after`` + A string which will be appended to the segment contents. + + ``contents`` + .. _config-themes-seg-contents: + + Segment contents, only required for ``string`` segments. + + ``args`` + A dict of arguments to be passed to a ``function`` segment. + + ``ljust`` + If set, the segment will be left justified to the width specified by + this option. + + ``rjust`` + If set, the segment will be right justified to the width specified + by this option. + + ``priority`` + Optional segment priority. Segments with priority ``-1`` (the + default priority) will always be included, regardless of the width + of the prompt/statusline. + + If the priority is ``0`` or more, the segment may be removed if the + prompt/statusline width is too small for all the segments to be + rendered. A lower number means that the segment has a higher + priority. + + Segments are removed according to their priority, with low priority + segments being removed first. + + ``draw_divider`` + Whether to draw a divider between this and the adjacent segment. The + adjacent segment is to the *right* for segments on the *left* side, + and vice versa. + + ``exclude_modes`` + A list of modes where this segment will be excluded: The segment is + included in all modes, *except* for the modes in this list. + + ``include_modes`` + A list of modes where this segment will be included: The segment is + *not* included in any modes, *except* for the modes in this list. + +Segments +-------- + +Segments are written in Python, and the default segments provided with +Powerline are located in ``powerline/ext/{extension}/segments/{module}.py``. +User-defined segments can be defined in the corresponding path in the user's +config directory. + +Segments are regular Python functions, and they may accept arguments. All +arguments should have a default value which will be used for themes that +don't provide an ``args`` dict. + +A segment function must return one of the following values: + +* ``None``, which will remove the segment from the prompt/statusline. +* A string, which will be the segment contents. +* A dict consisting of a ``contents`` string, and a ``highlight`` list. This + is useful for providing a particular highlighting group depending on the + segment contents. From 9f60daaf7825b73f7134c04d639e0c401b79eb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 20 Dec 2012 16:19:20 +0100 Subject: [PATCH 0124/1472] Use :file: for file references in the docs --- docs/source/configuration.rst | 25 +++++++++++++------------ docs/source/fontpatching.rst | 10 +++++----- docs/source/introduction.rst | 2 +- docs/source/troubleshooting.rst | 8 ++++---- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 4b55e83f..fbc1b9fc 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -9,16 +9,16 @@ written in Python. Powerline provides default configurations in the following locations: `Main configuration`_ - ``powerline/config.json`` + :file:`powerline/config.json` `Colorschemes`_ - ``powerline/colorschemes/default.json`` + :file:`powerline/colorschemes/default.json` `Themes`_ - ``powerline/themes/{extension}/default.json`` + :file:`powerline/themes/{extension}/default.json` The default configuration files are stored in the main package. User -configuration files are stored in ``$XDG_CONFIG_HOME/powerline`` for Linux -users, and in ``~/.config/powerline`` for OS X users. This usually -corresponds to ``~/.config/powerline`` on both platforms. +configuration files are stored in :file:`$XDG_CONFIG_HOME/powerline` for +Linux users, and in :file:`~/.config/powerline` for OS X users. This usually +corresponds to :file:`~/.config/powerline` on both platforms. The easiest way of creating your own version of any configuration file is to copy the configuration file from the main package to the corresponding path @@ -39,7 +39,7 @@ file. Example: Main configuration ------------------ -:Location: ``powerline/config.json`` +:Location: :file:`powerline/config.json` The main configuration file defines some common options that applies to all extensions, as well as some extension-specific options like themes and @@ -73,7 +73,7 @@ Extension-specific configuration Colorschemes ------------ -:Location: ``powerline/colorschemes/{name}.json`` +:Location: :file:`powerline/colorschemes/{name}.json` ``name`` Name of the colorscheme. @@ -128,7 +128,7 @@ Colorschemes Themes ------ -:Location: ``powerline/themes/{extension}/{name}.json`` +:Location: :file:`powerline/themes/{extension}/{name}.json` ``name`` Name of the theme. @@ -227,9 +227,10 @@ Segments -------- Segments are written in Python, and the default segments provided with -Powerline are located in ``powerline/ext/{extension}/segments/{module}.py``. -User-defined segments can be defined in the corresponding path in the user's -config directory. +Powerline are located in +:file:`powerline/ext/{extension}/segments/{module}.py`. User-defined +segments can be defined in the corresponding path in the user's config +directory. Segments are regular Python functions, and they may accept arguments. All arguments should have a default value which will be used for themes that diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index 513a8b13..17df92dd 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -41,8 +41,8 @@ Code point Glyph Description Usage ----- -The font patcher is located at ``powerline/fontpatcher/fontpatcher.py``. It -requires Python 2.7 and FontForge compiled with Python bindings to work. +The font patcher is located at :file:`powerline/fontpatcher/fontpatcher.py`. +It requires Python 2.7 and FontForge compiled with Python bindings to work. Patched fonts are renamed by default (" for Powerline" is added to the font name) so they don't conflict with existing fonts. Use the ``--no-rename`` @@ -65,7 +65,7 @@ Linux $ /path/to/fontpatcher.py MyFontFile.ttf -3. Copy the font file into ``~/.fonts`` (or another X font directory):: +3. Copy the font file into :file:`~/.fonts` (or another X font directory):: $ cp "MyFontFile for Powerline.otf" ~/.fonts @@ -76,7 +76,7 @@ Linux If you're using vim in a terminal you may need to close all open terminal windows after updating the font cache. -5. **Gvim users:** Update the GUI font in your ``vimrc`` file:: +5. **Gvim users:** Update the GUI font in your :file:`vimrc` file:: set guifont=MyFont\ for\ Powerline @@ -124,7 +124,7 @@ OS X 5. Install the font by double-clicking the font file in Finder and click "Install this font" from the preview window. -6. **Gvim users:** Update the GUI font in your ``vimrc`` file:: +6. **Gvim users:** Update the GUI font in your :file:`vimrc` file:: set guifont=MyFont\ for\ Powerline diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 277cf4c6..a0d4c16a 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -61,4 +61,4 @@ Feature highlights are now written in JSON, with a much cleaner syntax that's easier to learn and work with. Themes and colorschemes are also much more configurable, and it's easy to write your own and store them in your home config - directory (usually ``~/.config/powerline``). + directory. diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index c153dc3b..ad9865f9 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -37,7 +37,7 @@ The colors are weird in the default OS X Terminal app! I'm using tmux and Powerline looks like crap, what's wrong? You need to tell tmux that it has 256-color capabilities. Add this to - your ``.tmux.conf`` to solve this issue:: + your :file:`.tmux.conf` to solve this issue:: set -g default-terminal "screen-256color" @@ -48,14 +48,14 @@ Vim-specific issues ------------------- The statusline has strange characters like ``^B`` in it! - Please add ``set encoding=utf-8`` to your ``vimrc``. + Please add ``set encoding=utf-8`` to your :file:`vimrc`. The statusline has a lot of ``^`` or underline characters in it! You need to configure the ``fillchars`` setting to disable statusline fillchars (see ``:h fillchars`` for details). Add this to your - ``vimrc`` to solve this issue:: + :file:`vimrc` to solve this issue:: set fillchars+=stl:\ ,stlnc:\ The statusline is hidden/only appears in split windows! - Make sure that you have ``set laststatus=2`` in your ``vimrc``. + Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. From d87b35d0942cc3a837d302a3be292471c38779d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 21 Dec 2012 11:20:19 +0100 Subject: [PATCH 0125/1472] Fallback to line_percent highlighting for gradient segment --- powerline/ext/vim/segments/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index 5fcdb4c2..34d4a3c9 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -153,7 +153,7 @@ def line_percent(gradient=False): return { 'contents': percentage, - 'highlight': 'line_percent_gradient' + str(int(5 * percentage // 100) + 1), + 'highlight': ['line_percent_gradient' + str(int(5 * percentage // 100) + 1), 'line_percent'], } From f423ba11c856f908f0729c7a1ece8841e707a0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 21 Dec 2012 11:19:53 +0100 Subject: [PATCH 0126/1472] Add information about Python support --- docs/source/overview.rst | 6 ++++++ powerline/ext/vim/powerline.vim | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 9acde746..be7f2a30 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -13,6 +13,12 @@ The vim plugin requires a vim version with Python 2.7 support compiled in. You can check if your vim supports Python 2 by running ``vim --version | grep +python``. +If your vim version doesn't have support for Python 2, you'll have to +compile it with the ``--enable-pythoninterp`` flag (this also requires the +Python headers to be installed on your system). Please consult your +distribution's documentation for details on how to compile and install +packages. + Vim version 7.3.661 or newer is recommended for performance reasons. Installation diff --git a/powerline/ext/vim/powerline.vim b/powerline/ext/vim/powerline.vim index d60ca7e6..875ac243 100644 --- a/powerline/ext/vim/powerline.vim +++ b/powerline/ext/vim/powerline.vim @@ -1,3 +1,8 @@ +if ! has('python') + echoe 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' + finish +endif + python import sys, vim, os python sys.path.append(vim.eval('expand(":h:h:h:h")')) python import uuid From 2be52ed039cd15fd9ee9d2515b87d80bcae769a1 Mon Sep 17 00:00:00 2001 From: ZyX-I Date: Fri, 21 Dec 2012 23:03:50 +0400 Subject: [PATCH 0127/1472] Purge out :echoe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `:echoe` is unstable: unless it is already in `:try`…`:catch` block you never know for sure whether or not it will break execution. Thus the only valid use for `:echoe` is try echoe 'Msg' endtry : rough equivalent to `:throw` (throws, but with different message). As this is probably not the thing you want to do I changed it to use `:echomsg`. --- powerline/ext/vim/powerline.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/ext/vim/powerline.vim b/powerline/ext/vim/powerline.vim index 875ac243..0d4e0b02 100644 --- a/powerline/ext/vim/powerline.vim +++ b/powerline/ext/vim/powerline.vim @@ -1,5 +1,7 @@ if ! has('python') - echoe 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' + echohl ErrorMsg + echomsg 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' + echohl None finish endif From 7a7c971bd11ed658b459ac84cea34453b2db7ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 2 Jan 2013 08:38:28 +0100 Subject: [PATCH 0128/1472] Move plugin loading with :source into separate file This should resolve the issue with duplicate paths in sys.path by only updating sys.path if the plugin is loaded using :source instead of :python. sys.path is now updated in source_plugin.vim, which checks if the user has Python installed, and updates sys.path before sourcing the actual plugin file. Refs #17. Refs #19. Refs #21. --- docs/source/overview.rst | 2 +- powerline/ext/vim/__init__.py | 3 ++- powerline/ext/vim/powerline.vim | 11 +---------- powerline/ext/vim/source_plugin.vim | 11 +++++++++++ 4 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 powerline/ext/vim/source_plugin.vim diff --git a/docs/source/overview.rst b/docs/source/overview.rst index be7f2a30..4bc57d9c 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -64,4 +64,4 @@ location. Add the following line to your ``vimrc``, where ``{path}`` is the path to the main Powerline project directory:: - source {path}/powerline/ext/vim/powerline.vim + source {path}/powerline/ext/vim/source_plugin.vim diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py index 189d565d..d79377a6 100644 --- a/powerline/ext/vim/__init__.py +++ b/powerline/ext/vim/__init__.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- + def source_plugin(): import os import vim - vim.command('source ' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim')) + vim.command('source ' + vim.eval('fnameescape("' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim') + '")')) diff --git a/powerline/ext/vim/powerline.vim b/powerline/ext/vim/powerline.vim index 0d4e0b02..aabdf3f0 100644 --- a/powerline/ext/vim/powerline.vim +++ b/powerline/ext/vim/powerline.vim @@ -1,13 +1,4 @@ -if ! has('python') - echohl ErrorMsg - echomsg 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' - echohl None - finish -endif - -python import sys, vim, os -python sys.path.append(vim.eval('expand(":h:h:h:h")')) -python import uuid +python import uuid, vim python from powerline.core import Powerline python pl = Powerline('vim') diff --git a/powerline/ext/vim/source_plugin.vim b/powerline/ext/vim/source_plugin.vim new file mode 100644 index 00000000..87e8b12c --- /dev/null +++ b/powerline/ext/vim/source_plugin.vim @@ -0,0 +1,11 @@ +if ! has('python') + echohl ErrorMsg + echomsg 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' + echohl None + finish +endif + +python import sys, vim +python sys.path.append(vim.eval('expand(":h:h:h:h")')) + +exec 'source '. fnameescape(expand(':h') . '/powerline.vim') From ee0acef49cae20129efa466fefe55c7e803cbad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 2 Jan 2013 09:09:05 +0100 Subject: [PATCH 0129/1472] Fix issues with the Arch Linux PKGBUILD --- package/PKGBUILD | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package/PKGBUILD b/package/PKGBUILD index 859a0005..a550a8da 100644 --- a/package/PKGBUILD +++ b/package/PKGBUILD @@ -1,17 +1,17 @@ # Maintainer: Kim Silkebækken pkgname=powerline-git -pkgver=20121217 +pkgver=20130102 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('CC BY-SA 3.0') arch=('any') -depends=('vim' 'python2>=2.7') -makedepends=('git') +depends=('python2>=2.7') +makedepends=('git' 'python2-distribute') source=() -_gitroot="git://github.com/Lokaltog/powerline.git" +_gitroot="https://github.com/Lokaltog/powerline.git" _gitname="powerline" _gitbranch="develop" @@ -21,17 +21,17 @@ build() { msg "Connecting to GitHub..." if [ -d ${srcdir}/${_gitname} ]; then - cd ${_gitname} && git pull origin ${_gitbranch} + cd ${_gitname} + git pull origin ${_gitbranch} msg "The local files are updated." else git clone ${_gitroot} + cd ${_gitname} git checkout ${_gitbranch} fi msg "Git checkout done or server timeout." - cd ${srcdir}/${_gitname} - python2 setup.py build || return 1 python2 setup.py install --root=${pkgdir} || return 1 } From 7fb3df28b9fc9222e44ae48d8b2fd3c2a6b9fbfa Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 3 Jan 2013 00:21:56 +0400 Subject: [PATCH 0130/1472] Change code to use vim_get_func('fnameescape') Previous version had problems with paths containing backslashes and/or double quotes. --- powerline/ext/vim/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py index d79377a6..72c1be90 100644 --- a/powerline/ext/vim/__init__.py +++ b/powerline/ext/vim/__init__.py @@ -4,5 +4,8 @@ def source_plugin(): import os import vim + from bindings import vim_get_func - vim.command('source ' + vim.eval('fnameescape("' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim') + '")')) + fnameescape = vim_get_func('fnameescape') + + vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim'))) From bfc29749c15d8d7b37b4d9980d93035c58d450e5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 3 Jan 2013 00:34:33 +0400 Subject: [PATCH 0131/1472] Use better name for powerline variable in global scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is completely possible that “pl” will be claimed by some other plugin as well. There is no local scope for each .vim file in vim-python interface. --- powerline/ext/vim/powerline.vim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/ext/vim/powerline.vim b/powerline/ext/vim/powerline.vim index aabdf3f0..1ead0418 100644 --- a/powerline/ext/vim/powerline.vim +++ b/powerline/ext/vim/powerline.vim @@ -1,18 +1,18 @@ -python import uuid, vim +python import uuid python from powerline.core import Powerline -python pl = Powerline('vim') +python powerline = Powerline('vim') if exists('*pyeval') let s:pyeval = function('pyeval') else - python import json + python import json, vim function! s:pyeval(e) python vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) endfunction endif function! Powerline(winnr, current) - return s:pyeval('pl.renderer.render('. a:winnr .', '. a:current .')') + return s:pyeval('powerline.renderer.render('. a:winnr .', '. a:current .')') endfunction function! s:UpdateWindows() From f67a00454899ff428029f4dce1fb84deecefdaad Mon Sep 17 00:00:00 2001 From: ZyX-I Date: Thu, 3 Jan 2013 00:39:38 +0400 Subject: [PATCH 0132/1472] Use shorter and more readable form of :source --- powerline/ext/vim/source_plugin.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/ext/vim/source_plugin.vim b/powerline/ext/vim/source_plugin.vim index 87e8b12c..84be659e 100644 --- a/powerline/ext/vim/source_plugin.vim +++ b/powerline/ext/vim/source_plugin.vim @@ -8,4 +8,4 @@ endif python import sys, vim python sys.path.append(vim.eval('expand(":h:h:h:h")')) -exec 'source '. fnameescape(expand(':h') . '/powerline.vim') +source :h/powerline.vim From ca1225b864bfd95ec01de26d90d7f3b5c803bf07 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 3 Jan 2013 01:22:53 +0400 Subject: [PATCH 0133/1472] Add virtcol parameter for col_current segment The virtual column number is also default. I doubt anybody here ever wanted to see byte offset. When it comes to a questions like alignment, forced text width and so on only virtual columns matter. It would be good to optionally take concealed characters into account, but vim does not provide a way to do so (well, except recent screencol() added solely for testing which would be hard, but not impossible, to adapt to such needs). Closes #25. --- powerline/ext/vim/segments/core.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index 34d4a3c9..ef1df96b 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -8,6 +8,7 @@ from powerline.lib.memoize import memoize vim_funcs = { 'col': vim_get_func('col', rettype=int), + 'virtcol': vim_get_func('virtcol', rettype=int), 'expand': vim_get_func('expand'), 'line': vim_get_func('line', rettype=int), 'mode': vim_get_func('mode'), @@ -163,7 +164,10 @@ def line_current(): return vim_funcs['line']('.') -def col_current(): +def col_current(virtcol=True): '''Return the current cursor column. + + If the optional argument is True then returns visual column with concealed + characters ignored (default), else returns byte offset. ''' - return vim_funcs['col']('.') + return vim_funcs['virtcol' if virtcol else 'col']('.') From 102e749093f44ec41ef0eba2fd9f7d5b94b9092a Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 3 Jan 2013 03:29:38 +0400 Subject: [PATCH 0134/1472] Add file_vcs_status segment, using powerline.lib.vcs instead of fugitive Using pure-python implementation allows to use the same code in the terminal segments (not implemented currently). It also should be more effective. Git module is able to use pygit2 instead of command-line git which is much faster. Mercurial module assumes that mercurial is always accessible as a python module. Code for repository (not file) status is untested. It exists solely for terminal segments. Closes #26. --- powerline/colorschemes/default.json | 1 + powerline/ext/vim/segments/core.py | 39 ++++---- powerline/lib/vcs/__init__.py | 27 ++++++ powerline/lib/vcs/git.py | 132 ++++++++++++++++++++++++++++ powerline/lib/vcs/mercurial.py | 50 +++++++++++ powerline/themes/vim/default.json | 5 ++ 6 files changed, 234 insertions(+), 20 deletions(-) create mode 100644 powerline/lib/vcs/__init__.py create mode 100644 powerline/lib/vcs/git.py create mode 100644 powerline/lib/vcs/mercurial.py diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index a199a09f..134ad076 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -58,6 +58,7 @@ "modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attr": ["bold"] }, "paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] }, "readonly_indicator": { "fg": "brightestred", "bg": "gray4" }, + "file_vcs_status": { "fg": "brightestred", "bg": "gray4" }, "branch": { "fg": "gray9", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index ef1df96b..59300330 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -5,6 +5,7 @@ import vim from powerline.ext.vim.bindings import vim_get_func from powerline.lib.memoize import memoize +from powerline.lib.vcs import guess vim_funcs = { 'col': vim_get_func('col', rettype=int), @@ -12,9 +13,6 @@ vim_funcs = { 'expand': vim_get_func('expand'), 'line': vim_get_func('line', rettype=int), 'mode': vim_get_func('mode'), - 'vcs': { - 'fugitive': vim_get_func('fugitive#head'), - }, } vim_modes = { @@ -77,23 +75,6 @@ def readonly_indicator(text=u''): return text if int(vim.eval('&readonly')) else None -@memoize(2) -def branch(): - '''Return VCS branch. - - TODO: Expand this function to handle several VCS plugins. - ''' - branch = None - try: - branch = vim_funcs['vcs']['fugitive'](5) - except vim.error: - vim_funcs['vcs']['fugitive'] = None - except TypeError: - pass - - return branch if branch else None - - def file_directory(): '''Return file directory (head component of the file path). ''' @@ -171,3 +152,21 @@ def col_current(virtcol=True): characters ignored (default), else returns byte offset. ''' return vim_funcs['virtcol' if virtcol else 'col']('.') + + +@memoize(2) +def branch(): + repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) + if repo: + return repo.branch() + return None + + +# TODO Drop cache on BufWrite event +@memoize(2) +def file_vcs_status(): + if vim.current.buffer.name and not vim.eval('&buftype'): + repo = guess(os.path.abspath(vim.current.buffer.name)) + if repo: + return repo.status(os.path.relpath(vim.current.buffer.name, repo.directory)) + return None diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py new file mode 100644 index 00000000..16786fe7 --- /dev/null +++ b/powerline/lib/vcs/__init__.py @@ -0,0 +1,27 @@ +import importlib +import os +from powerline.lib.memoize import memoize + + +def generate_directories(path): + yield path + while True: + old_path = path + path = os.path.dirname(path) + if path == old_path: + break + yield path + + +@memoize(100) +def guess(path): + for directory in generate_directories(path): + for vcs, vcs_dir in (('git', '.git'), ('mercurial', '.hg')): + if os.path.isdir(os.path.join(directory, vcs_dir)): + try: + if vcs not in globals(): + globals()[vcs] = importlib.import_module('powerline.lib.vcs.' + vcs) + return globals()[vcs].Repository(directory) + except: + pass + return None diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py new file mode 100644 index 00000000..1ccb17fe --- /dev/null +++ b/powerline/lib/vcs/git.py @@ -0,0 +1,132 @@ +try: + import pygit2 as git + + class Repository(object): + __slots__ = ('repo', 'directory') + + def __init__(self, directory): + self.directory = directory + self.repo = git.Repository(directory) + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :First column: working directory status (D: dirty / space) + :Second column: index status (I: index dirty / space) + :Third column: presense of untracked files (U: untracked files / space) + :None: repository clean + + With file argument: returns status of this file. Output is + equivalent to the first two columns of "git status --porcelain" + (except for merge statuses as they are not supported by libgit2). + ''' + if path: + status = self.repo.status_file(path) + + if status == git.GIT_STATUS_CURRENT: + return None + else: + if status & git.GIT_STATUS_WT_NEW: + return '??' + if status & git.GIT_STATUS_IGNORED: + return '!!' + + if status & git.GIT_STATUS_INDEX_NEW: + index_status = 'A' + elif status & git.GIT_STATUS_INDEX_DELETED: + index_status = 'D' + elif status & git.GIT_STATUS_INDEX_MODIFIED: + index_status = 'M' + else: + index_status = ' ' + + if status & git.GIT_STATUS_WT_DELETED: + wt_status = 'D' + elif status & git.GIT_STATUS_WT_MODIFIED: + wt_status = 'M' + else: + wt_status = ' ' + + return index_status + wt_status + else: + wt_column = ' ' + index_column = ' ' + untracked_column = ' ' + for status in self.repo.status(): + if status & (git.GIT_STATUS_WT_DELETED + | git.GIT_STATUS_WT_MODIFIED): + wt_column = 'D' + elif status & (git.GIT_STATUS_INDEX_NEW + | git.GIT_STATUS_INDEX_MODIFIED + | git.GIT_STATUS_INDEX_DELETED): + index_column = 'I' + elif status & git.GIT_STATUS_WT_NEW: + untracked_column = 'U' + return wt_column + index_column + untracked_column + + def branch(self): + try: + ref = self.repo.lookup_reference('HEAD') + except KeyError: + return None + + try: + target = ref.target + except ValueError: + return '[DETACHED HEAD]' + + if target.startswith('refs/heads/'): + return target[11:] + else: + return '[DETACHED HEAD]' +except ImportError: + from subprocess import Popen, PIPE + + def readlines(cmd, cwd): + p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) + p.stderr.close() + for line in p.stdout: + yield line[:-1] + + class Repository(object): + __slots__ = ('directory',) + + def __init__(self, directory): + self.directory = directory + + def _gitcmd(self, *args): + return readlines(('git',) + args, self.directory) + + def status(self, path=None): + if path: + try: + return self._gitcmd('status', '--porcelain', '--', path).next()[:2] + except StopIteration: + try: + self._gitcmd('ls-files', '--ignored', '--exclude-standard', '--others', '--', path).next() + return '!!' + except StopIteration: + return None + else: + wt_column = ' ' + index_column = ' ' + untracked_column = ' ' + for line in self._gitcmd('status', '--porcelain'): + if line[0] == '?': + untracked_column = 'U' + elif line[0] == '!': + pass + elif line[0] != ' ': + index_column = 'I' + elif line[1] != ' ': + wt_column = 'D' + r = wt_column + index_column + untracked_column + return None if r == ' ' else r + + def branch(self): + for line in self._gitcmd('branch', '-l'): + if line[0] == '*': + return line[2:] + return None diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py new file mode 100644 index 00000000..80b676d9 --- /dev/null +++ b/powerline/lib/vcs/mercurial.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import +from mercurial import hg, ui, match + + +class Repository(object): + __slots__ = ('directory', 'ui') + + statuses = 'MARDUI' + repo_statuses = (1, 1, 1, 1, 2) + repo_statuses_str = (None, 'D ', ' U', 'DU') + + def __init__(self, directory): + self.directory = directory + self.ui = ui.ui() + + def _repo(self): + # Cannot create this object once and use always: when repository updates + # functions emit invalid results + return hg.repository(self.ui, self.directory) + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :"D?": dirty (tracked modified files: added, removed, deleted, modified), + :"?U": untracked-dirty (added, but not tracked files) + :None: clean (status is empty) + + With file argument: returns status of this file: "M"odified, "A"dded, + "R"emoved, "D"eleted (removed from filesystem, but still tracked), + "U"nknown, "I"gnored, (None)Clean. + ''' + repo = self._repo() + + if path: + m = match.match(None, None, [path], exact=True) + statuses = repo.status(match=m, unknown=True, ignored=True) + for status, paths in zip(self.statuses, statuses): + if paths: + return status + return None + else: + resulting_status = 0 + for status, paths in zip(self.repo_statuses, repo.status(unknown=True)): + resulting_status |= status + return self.repo_statuses_str[resulting_status] + + def branch(self): + return self._repo().dirstate.branch() diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index c9ce2ca0..32747eb7 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -31,6 +31,11 @@ "name": "file_name", "draw_divider": false }, + { + "name": "file_vcs_status", + "draw_divider": false, + "before": " " + }, { "name": "modified_indicator", "args": { "text": "+" }, From 3addf44a6dd7f51417734540de857b4b9b4abad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 4 Jan 2013 15:02:40 +0100 Subject: [PATCH 0135/1472] Highlight VCS flags differently "M" flag is highlighted in yellow, "A" flag is highlighted in green, other flags are highlighted in red. This also corresponds with the highlighting in the output of `git status`. Whitespace is also trimmed from the flags as the columns don't matter much in vim statuslines, and empty columns only introduce whitespace that looks like a bug. --- powerline/colorschemes/default.json | 4 +++- powerline/ext/vim/segments/core.py | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index 134ad076..dfc4bf10 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -58,7 +58,6 @@ "modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attr": ["bold"] }, "paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] }, "readonly_indicator": { "fg": "brightestred", "bg": "gray4" }, - "file_vcs_status": { "fg": "brightestred", "bg": "gray4" }, "branch": { "fg": "gray9", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, @@ -67,6 +66,9 @@ "file_format": { "fg": "gray8", "bg": "gray2" }, "file_encoding": { "fg": "gray8", "bg": "gray2" }, "file_type": { "fg": "gray8", "bg": "gray2" }, + "file_vcs_status": { "fg": "brightestred", "bg": "gray4" }, + "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4" }, + "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, "line_percent": { "fg": "gray9", "bg": "gray4" }, "line_percent_gradient1": { "fg": "gradient1", "bg": "gray4" }, "line_percent_gradient2": { "fg": "gradient2", "bg": "gray4" }, diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index 59300330..9709ca31 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -168,5 +168,12 @@ def file_vcs_status(): if vim.current.buffer.name and not vim.eval('&buftype'): repo = guess(os.path.abspath(vim.current.buffer.name)) if repo: - return repo.status(os.path.relpath(vim.current.buffer.name, repo.directory)) + status = repo.status(os.path.relpath(vim.current.buffer.name, repo.directory)) + if not status: + return None + status = status.strip() + return { + 'contents': status, + 'highlight': ['file_vcs_status_' + status, 'file_vcs_status'], + } return None From 5b43960e0e87dd8310e1fecfe47b8ae102bce7be Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 4 Jan 2013 22:18:36 +0400 Subject: [PATCH 0136/1472] Recreate git.Repository() on each call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It appears that pygit2 has similar to mercurial’s issue with statuses (they are not updated) and needs to be regenerated on each call as well. Implies to index statuses only though, that is why I used to think pygit2 does not have this problem. --- powerline/lib/vcs/git.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 1ccb17fe..c5c9bdfd 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -2,11 +2,13 @@ try: import pygit2 as git class Repository(object): - __slots__ = ('repo', 'directory') + __slots__ = ('directory') def __init__(self, directory): self.directory = directory - self.repo = git.Repository(directory) + + def _repo(self): + return git.Repository(self.directory) def status(self, path=None): '''Return status of repository or file. @@ -23,7 +25,7 @@ try: (except for merge statuses as they are not supported by libgit2). ''' if path: - status = self.repo.status_file(path) + status = self._repo().status_file(path) if status == git.GIT_STATUS_CURRENT: return None @@ -54,7 +56,7 @@ try: wt_column = ' ' index_column = ' ' untracked_column = ' ' - for status in self.repo.status(): + for status in self._repo().status(): if status & (git.GIT_STATUS_WT_DELETED | git.GIT_STATUS_WT_MODIFIED): wt_column = 'D' @@ -68,7 +70,7 @@ try: def branch(self): try: - ref = self.repo.lookup_reference('HEAD') + ref = self._repo().lookup_reference('HEAD') except KeyError: return None From 332dc45bf948dcd870711b31e3324bd41bffeb36 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Jan 2013 17:35:06 +0400 Subject: [PATCH 0137/1472] Add additional_key to memoization function Closes #29. --- powerline/ext/vim/segments/core.py | 2 +- powerline/lib/memoize.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index 9709ca31..0dc538a2 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -163,7 +163,7 @@ def branch(): # TODO Drop cache on BufWrite event -@memoize(2) +@memoize(2, additional_key=lambda: vim.current.buffer.number) def file_vcs_status(): if vim.current.buffer.name and not vim.eval('&buftype'): repo = guess(os.path.abspath(vim.current.buffer.name)) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 05f71181..a683c25f 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -11,8 +11,9 @@ class memoize(object): _caches = {} _timeouts = {} - def __init__(self, timeout): + def __init__(self, timeout, additional_key=None): self.timeout = timeout + self.additional_key = additional_key def collect(self): '''Clear cache of results which have timed out. @@ -31,7 +32,10 @@ class memoize(object): def func(*args, **kwargs): kw = kwargs.items() kw.sort() - key = (args, tuple(kw)) + if self.additional_key: + key = (args, tuple(kw), self.additional_key()) + else: + key = (args, tuple(kw)) try: v = self.cache[key] if (time.time() - v[1]) > self.timeout: From 424da8b573b1fac05387149944abe7fa6b8dec4a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Jan 2013 18:14:17 +0400 Subject: [PATCH 0138/1472] =?UTF-8?q?Don=E2=80=99t=20raise=20an=20exceptio?= =?UTF-8?q?n=20when=20encountered=20untracked=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerline/lib/vcs/git.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index c5c9bdfd..8c3ab9c2 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -25,7 +25,10 @@ try: (except for merge statuses as they are not supported by libgit2). ''' if path: - status = self._repo().status_file(path) + try: + status = self._repo().status_file(path) + except KeyError, ValueError: + return None if status == git.GIT_STATUS_CURRENT: return None From 536120aab88a42cf5847bda00d6727e0f66438b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 8 Jan 2013 15:00:05 +0100 Subject: [PATCH 0139/1472] Re-add note about PyPI which got removed from the docs Refs #34. --- docs/source/overview.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 4bc57d9c..fe172ed8 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -37,6 +37,9 @@ system-wide, install with ``pip install --user`` instead. .. note:: Make sure that you install the package for Python 2. For distros like Arch Linux you'll have to run ``pip2`` instead of ``pip``. +.. note:: This project is currently unavailable on the PyPI due to a naming + conflict with an unrelated project. + Distribution-specific packages ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From a26d5e5319c5a2fb8f5e35e82a2c542344e3abf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 13:17:57 +0100 Subject: [PATCH 0140/1472] Add troubleshooting info for Windows gVim + git users Closes #36. --- docs/source/troubleshooting.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index ad9865f9..a9a85c6c 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -59,3 +59,7 @@ The statusline has a lot of ``^`` or underline characters in it! The statusline is hidden/only appears in split windows! Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. + +I'm using gVim for Windows, and ``cmd`` windows keep popping up when working in git repos! + Either install ``libgit2`` and ``pygit2``, or disable the VCS segment in + your user configuration to resolve this issue. From 44fb24dee67ef9970174fed24e853e75169b45d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 14:29:01 +0100 Subject: [PATCH 0141/1472] Fix padding issue with single-sided statuslines --- powerline/renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 0dc062a7..770a85b1 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -89,7 +89,7 @@ class Renderer(object): 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 '' + outer_padding = ' ' if index == 0 or (index == segments_len - 1 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' divider = theme.get_divider(segment['side'], divider_type) From 2ceec25713a077f2a06785a09980ee5a01109a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 14:29:44 +0100 Subject: [PATCH 0142/1472] Update terminal segments and default theme --- powerline/ext/terminal/segments/core.py | 45 ++++++++++++++++++++++--- powerline/themes/terminal/default.json | 16 ++++++++- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/powerline/ext/terminal/segments/core.py b/powerline/ext/terminal/segments/core.py index c42c5faf..8f86b6df 100644 --- a/powerline/ext/terminal/segments/core.py +++ b/powerline/ext/terminal/segments/core.py @@ -1,11 +1,48 @@ # -*- coding: utf-8 -*- import os +import re +import socket + +from powerline.lib.vcs import guess -def user_name(): - user_name = os.environ.get('USER') +def hostname(): + if not os.environ.get('SSH_CLIENT'): + return None + return socket.gethostname() + + +def user(): + user = os.environ.get('USER') + euid = os.geteuid() + return { - 'contents': user_name, - 'highlight': 'user_name' if user_name != 'root' else ['user_name_root', 'user_name'], + 'contents': user, + 'highlight': 'user' if euid != 0 else ['superuser', 'user'], } + + +def branch(): + repo = guess(os.path.abspath(os.getcwd())) + if repo: + return repo.branch() + return None + + +def cwd(dir_shorten_len=None, dir_limit_depth=None): + cwd = os.getcwdu() + home = os.environ.get('HOME') + if home: + cwd = re.sub('^' + re.escape(home), '~', cwd, 1) + cwd_split = cwd.split(os.sep) + cwd_split_len = len(cwd_split) + + if cwd_split_len > dir_limit_depth + 1: + del(cwd_split[0:-dir_limit_depth]) + cwd_split.insert(0, u'…') + + cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] + cwd = os.path.join(*cwd) + + return cwd diff --git a/powerline/themes/terminal/default.json b/powerline/themes/terminal/default.json index 05013239..069fa858 100644 --- a/powerline/themes/terminal/default.json +++ b/powerline/themes/terminal/default.json @@ -3,7 +3,21 @@ "segments": { "left": [ { - "name": "user_name" + "name": "hostname", + "before": " " + }, + { + "name": "user" + }, + { + "name": "branch", + "before": " " + }, + { + "name": "cwd", + "args": { + "dir_limit_depth": 3 + } } ] } From d0d35cac345b842bce36dfd9531f859988e82d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 14:40:06 +0100 Subject: [PATCH 0143/1472] Move terminal prompt script to extension directory --- examples/terminal/pl.py | 13 ------------- powerline/ext/terminal/powerline_prompt.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-) delete mode 100755 examples/terminal/pl.py create mode 100755 powerline/ext/terminal/powerline_prompt.py diff --git a/examples/terminal/pl.py b/examples/terminal/pl.py deleted file mode 100755 index e4386260..00000000 --- a/examples/terminal/pl.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -'''Powerline terminal prompt example. -''' - -import os -import sys -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) - -from powerline.core import Powerline - -pl = Powerline('terminal') -print(pl.renderer.render('n')) diff --git a/powerline/ext/terminal/powerline_prompt.py b/powerline/ext/terminal/powerline_prompt.py new file mode 100755 index 00000000..9221d299 --- /dev/null +++ b/powerline/ext/terminal/powerline_prompt.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +'''Powerline terminal prompt example. +''' +try: + from powerline.core import Powerline +except ImportError: + import os + import sys + sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) + + from powerline.core import Powerline + +pl = Powerline('terminal') +print(pl.renderer.render(None).encode('utf-8')) From 4c2dd90e9ddc5b44b17f2d66f99a42afbd020e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 14:40:27 +0100 Subject: [PATCH 0144/1472] Create terminal/prompt color scheme --- powerline/colorschemes/default.json | 5 +- powerline/colorschemes/terminal_default.json | 63 ++++++++++++++++++++ powerline/config.json | 2 +- 3 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 powerline/colorschemes/terminal_default.json diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index dfc4bf10..f261df00 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -78,10 +78,7 @@ "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" }, - - "user_name": { "fg": "brightcyan", "bg": "darkblue", "attr": ["bold"] }, - "user_name_root": { "fg": "white", "bg": "brightred", "attr": ["bold"] } + "col_current": { "fg": "gray6", "bg": "gray10" } }, "mode_translations": { "nc": { diff --git a/powerline/colorschemes/terminal_default.json b/powerline/colorschemes/terminal_default.json new file mode 100644 index 00000000..4ef0f0a7 --- /dev/null +++ b/powerline/colorschemes/terminal_default.json @@ -0,0 +1,63 @@ +{ + "name": "terminal_default", + "colors": { + "black": 16, + "white": 231, + + "darkestgreen": 22, + "darkgreen": 28, + "mediumgreen": 70, + "brightgreen": 148, + + "darkestcyan": 23, + "mediumcyan": 117, + "brightcyan": 159, + + "darkestblue": 24, + "darkblue": 31, + + "darkestred": 52, + "darkred": 88, + "mediumred": 124, + "brightred": 160, + "brightestred": 196, + + "darkestpurple": 55, + "mediumpurple": 98, + "brightpurple": 189, + + "darkorange": 94, + "mediumorange": 166, + "brightorange": 208, + "brightestorange": 214, + + "brightyellow": 220, + + "gray0": 233, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 252, + + "gradient1": 190, + "gradient2": 184, + "gradient3": 178, + "gradient4": 172, + "gradient5": 166, + "gradient6": 160 + }, + "groups": { + "user": { "fg": "brightcyan", "bg": "darkblue", "attr": ["bold"] }, + "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, + "branch": { "fg": "gray9", "bg": "gray4" }, + "cwd": { "fg": "gray10", "bg": "gray4" }, + "hostname": { "fg": "brightyellow", "bg": "mediumorange" } + }, + "mode_translations": { } +} diff --git a/powerline/config.json b/powerline/config.json index 70c85d46..dfae53ff 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -13,7 +13,7 @@ }, "ext": { "terminal": { - "colorscheme": "default", + "colorscheme": "terminal_default", "theme": "default" }, "tmux": { From 8d584d059ddb054a4d106ac957f6a73868ed3ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 16:09:50 +0100 Subject: [PATCH 0145/1472] Move segments.core packages out of subdirectories --- powerline/ext/terminal/{segments/core.py => segments.py} | 0 powerline/ext/terminal/segments/__init__.py | 0 powerline/ext/tmux/{segments/core.py => segments.py} | 0 powerline/ext/tmux/segments/__init__.py | 0 powerline/ext/vim/{segments/core.py => segments.py} | 0 powerline/ext/vim/segments/__init__.py | 0 powerline/segments.py | 2 +- 7 files changed, 1 insertion(+), 1 deletion(-) rename powerline/ext/terminal/{segments/core.py => segments.py} (100%) delete mode 100644 powerline/ext/terminal/segments/__init__.py rename powerline/ext/tmux/{segments/core.py => segments.py} (100%) delete mode 100644 powerline/ext/tmux/segments/__init__.py rename powerline/ext/vim/{segments/core.py => segments.py} (100%) delete mode 100644 powerline/ext/vim/segments/__init__.py diff --git a/powerline/ext/terminal/segments/core.py b/powerline/ext/terminal/segments.py similarity index 100% rename from powerline/ext/terminal/segments/core.py rename to powerline/ext/terminal/segments.py diff --git a/powerline/ext/terminal/segments/__init__.py b/powerline/ext/terminal/segments/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/powerline/ext/tmux/segments/core.py b/powerline/ext/tmux/segments.py similarity index 100% rename from powerline/ext/tmux/segments/core.py rename to powerline/ext/tmux/segments.py diff --git a/powerline/ext/tmux/segments/__init__.py b/powerline/ext/tmux/segments/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments.py similarity index 100% rename from powerline/ext/vim/segments/core.py rename to powerline/ext/vim/segments.py diff --git a/powerline/ext/vim/segments/__init__.py b/powerline/ext/vim/segments/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/powerline/segments.py b/powerline/segments.py index 750a564c..76d0d513 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -13,7 +13,7 @@ class Segments(object): def get_function(self, segment): oldpath = sys.path sys.path = self.path + sys.path - segment_module = str(segment.get('module', 'powerline.ext.{0}.segments.core'.format(self.ext))) + segment_module = str(segment.get('module', 'powerline.ext.{0}.segments'.format(self.ext))) try: return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) From bba272e0bddd4761fd0cd54eee84f4101e5df1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 9 Jan 2013 16:16:27 +0100 Subject: [PATCH 0146/1472] Update theme/colorscheme name properties --- powerline/colorschemes/default.json | 2 +- powerline/colorschemes/terminal_default.json | 2 +- powerline/themes/terminal/default.json | 1 - powerline/themes/tmux/default.json | 1 - powerline/themes/vim/default.json | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index f261df00..0bdba512 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -1,5 +1,5 @@ { - "name": "default", + "name": "Default color scheme", "colors": { "black": 16, "white": 231, diff --git a/powerline/colorschemes/terminal_default.json b/powerline/colorschemes/terminal_default.json index 4ef0f0a7..f0436ae0 100644 --- a/powerline/colorschemes/terminal_default.json +++ b/powerline/colorschemes/terminal_default.json @@ -1,5 +1,5 @@ { - "name": "terminal_default", + "name": "Default color scheme for terminal prompts", "colors": { "black": 16, "white": 231, diff --git a/powerline/themes/terminal/default.json b/powerline/themes/terminal/default.json index 069fa858..c5e2879b 100644 --- a/powerline/themes/terminal/default.json +++ b/powerline/themes/terminal/default.json @@ -1,5 +1,4 @@ { - "name": "default", "segments": { "left": [ { diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index 05013239..4f368ee5 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -1,5 +1,4 @@ { - "name": "default", "segments": { "left": [ { diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index 32747eb7..4aa488f7 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -1,5 +1,4 @@ { - "name": "default", "segments": { "left": [ { From 7eb3bfde9c8682ade84550fffe37171a507121eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 11 Jan 2013 12:18:17 +0100 Subject: [PATCH 0147/1472] Update memoize class and add support for persistent cache --- powerline/lib/memoize.py | 76 ++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index a683c25f..77ef2d2b 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,48 +1,56 @@ # -*- coding: utf-8 -*- +import cPickle as pickle +import functools +import os +import tempfile import time class memoize(object): - '''Memoization decorator with timout. - - http://code.activestate.com/recipes/325905-memoize-decorator-with-timeout/ + '''Memoization decorator with timeout. ''' - _caches = {} - _timeouts = {} + _cache = {} - def __init__(self, timeout, additional_key=None): + def __init__(self, timeout, additional_key=None, persistent=False, persistent_file=None): self.timeout = timeout self.additional_key = additional_key + self.persistent = persistent + self.persistent_file = persistent_file or os.path.join(tempfile.gettempdir(), 'powerline-cache') - def collect(self): - '''Clear cache of results which have timed out. - ''' - for func in self._caches: - cache = {} - for key in self._caches[func]: - if (time.time() - self._caches[func][key][1]) < self._timeouts[func]: - cache[key] = self._caches[func][key] - self._caches[func] = cache - - def __call__(self, f): - self.cache = self._caches[f] = {} - self._timeouts[f] = self.timeout - - def func(*args, **kwargs): - kw = kwargs.items() - kw.sort() + def __call__(self, func): + @functools.wraps(func) + def decorated_function(*args, **kwargs): if self.additional_key: - key = (args, tuple(kw), self.additional_key()) + key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) else: - key = (args, tuple(kw)) - try: - v = self.cache[key] - if (time.time() - v[1]) > self.timeout: - raise KeyError - except KeyError: - v = self.cache[key] = f(*args, **kwargs), time.time() - return v[0] - func.func_name = f.func_name + key = (func.__name__, args, tuple(kwargs.items())) - return func + if self.persistent: + try: + with open(self.persistent_file, 'rb') as fileobj: + self._cache = pickle.load(fileobj) + except (IOError, EOFError): + pass + + cached = self._cache.get(key, None) + + if cached is None or time.time() - cached['time'] > self.timeout: + cached = self._cache[key] = { + 'result': func(*args, **kwargs), + 'time': time.time(), + } + + if self.persistent: + try: + with open(self.persistent_file, 'wb') as fileobj: + pickle.dump(self._cache, fileobj) + except IOError: + # Unable to write to file + pass + except TypeError: + # Unable to pickle function result + pass + + return cached['result'] + return decorated_function From 7e214a65b0c78a6a8de30de2a4b44d648b2fa27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 11 Jan 2013 13:37:22 +0100 Subject: [PATCH 0148/1472] Create tmux segments Refs #44. --- powerline/ext/tmux/segments.py | 92 ++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index c42c5faf..9f952628 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -2,10 +2,94 @@ import os +from powerline.lib.memoize import memoize -def user_name(): - user_name = os.environ.get('USER') +# Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes +weather_conditions_codes = { + u'〇': [25, 34], + u'⚑': [24], + u'☔': [5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47], + u'☁': range(26, 30) + [44], + u'❅': [7] + range(13, 18) + [41, 42, 43, 46], + u'☈': range(0, 4) + range(37, 39), + u'〰': range(19, 23), + u'☼': [32, 36], + u'☾': [31, 33], +} + + +def date(format='%Y-%m-%d'): + from datetime import datetime + return datetime.now().strftime(format) + + +@memoize(600, persistent=True) +def external_ip(query_url='http://icanhazip.com/'): + import urllib2 + try: + return urllib2.urlopen(query_url).read().strip() + except urllib2.HTTPError: + return + + +def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}'): + from multiprocessing import cpu_count + averages = os.getloadavg() + normalized = averages[1] / cpu_count() + if normalized < 1: + gradient = 'system_load_good' + elif normalized < 2: + gradient = 'system_load_bad' + else: + gradient = 'system_load_ugly' return { - 'contents': user_name, - 'highlight': 'user_name' if user_name != 'root' else ['user_name_root', 'user_name'], + 'contents': format.format(avg=averages), + 'highlight': [gradient, 'system_load'] } + + +def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): + # TODO: make this work with operating systems without /proc/uptime + try: + with open('/proc/uptime', 'r') as f: + seconds = int(float(f.readline().split()[0])) + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + + return format.format(days=int(days), hours=hours, minutes=minutes) + except IOError: + pass + + +@memoize(600, persistent=True) +def forecast(unit='c', location_query=None): + import json + import urllib + import urllib2 + + if not location_query: + try: + location = json.loads(urllib2.urlopen('http://freegeoip.net/json/' + external_ip()).read()) + location_query = ','.join([location['city'], location['region_name'], location['country_name']]) + except ValueError: + return None + + query_data = { + 'q': + 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' + 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit), + 'format': 'json' + } + url = 'http://query.yahooapis.com/v1/public/yql?' + urllib.urlencode(query_data) + response = json.loads(urllib2.urlopen(url).read()) + + condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] + condition_code = int(condition['code']) + icon = u'〇' + + for icon, codes in weather_conditions_codes.items(): + if condition_code in codes: + break + + return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) From 6d3dea1f0befdaceacee2476be77ef3061c13d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 15 Jan 2013 08:49:40 +0100 Subject: [PATCH 0149/1472] Cleanup Python files to comply better with PEP 8 Removed excessive newlines and comments, and fixed whitespace issues. --- powerline/colorscheme.py | 27 +++++------------ powerline/core.py | 11 ++----- powerline/ext/terminal/powerline_prompt.py | 4 +-- powerline/ext/terminal/renderer.py | 7 +---- powerline/ext/terminal/segments.py | 6 +--- powerline/ext/tmux/renderer.py | 10 ++----- powerline/ext/tmux/segments.py | 2 +- powerline/ext/vim/__init__.py | 2 -- powerline/ext/vim/bindings.py | 6 ++-- powerline/ext/vim/renderer.py | 16 ++-------- powerline/ext/vim/segments.py | 34 +++++++--------------- powerline/fontpatcher/fontpatcher.py | 9 ------ powerline/lib/memoize.py | 10 ++----- powerline/lib/vcs/git.py | 2 +- powerline/lib/vcs/mercurial.py | 1 - powerline/matchers.py | 2 -- powerline/renderer.py | 8 ----- powerline/segments.py | 6 +--- powerline/theme.py | 11 ++----- setup.py | 18 +++++------- 20 files changed, 43 insertions(+), 149 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index db4488d2..e03d738d 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -5,12 +5,9 @@ class Colorscheme(object): DEFAULT_MODE_KEY = '__default__' def __init__(self, colorscheme): - '''Initialize a colorscheme. - ''' + '''Initialize a colorscheme.''' self.colors = {} - self.modes_groups = { - self.DEFAULT_MODE_KEY: {} - } + 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(): @@ -22,36 +19,33 @@ class Colorscheme(object): # 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.get('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.get('colors', {}).get(group_props['fg'], group_props['fg'])], 'bg': self.colors[translations.get('colors', {}).get(group_props['bg'], group_props['bg'])], 'attr': group_attr_flag, - } + } def get_group_highlighting(self, group): - '''Return highlighting information for all modes of a highlighting group. - ''' + '''Return highlighting information for all modes of a highlighting group.''' group_highlighting = {} for mode, mode_group in self.modes_groups.items(): try: @@ -74,7 +68,6 @@ 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: @@ -82,14 +75,11 @@ class Colorscheme(object): 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): - '''Convert an attribute array to a renderer flag. - ''' + '''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 @@ -97,7 +87,6 @@ class Colorscheme(object): attr_flag |= Renderer.ATTR_ITALIC if 'underline' in attributes: attr_flag |= Renderer.ATTR_UNDERLINE - return attr_flag cterm_to_hex = { @@ -141,4 +130,4 @@ cterm_to_hex = { 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, -} + } diff --git a/powerline/core.py b/powerline/core.py index 85591053..f7835f3f 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -13,11 +13,9 @@ from matchers import Matchers class Powerline(object): def __init__(self, ext): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) - config_path = os.path.join(config_home, 'powerline') plugin_path = os.path.realpath(os.path.dirname(__file__)) self.search_paths = [config_path, plugin_path] - sys.path[:0] = self.search_paths # Load main config file @@ -31,19 +29,15 @@ class Powerline(object): # Load and initialize extension theme theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) - path = [os.path.expanduser(path) for path in self.config.get('paths', [])] - get_segment = Segments(ext, path, colorscheme).get get_matcher = Matchers(ext, path).get - theme_kwargs = { 'ext': ext, 'colorscheme': colorscheme, 'common_config': self.config, - 'get_segment': get_segment - } - + 'get_segment': get_segment, + } local_themes = {} for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): key = get_matcher(key) @@ -65,5 +59,4 @@ class Powerline(object): 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/terminal/powerline_prompt.py b/powerline/ext/terminal/powerline_prompt.py index 9221d299..fde8d414 100755 --- a/powerline/ext/terminal/powerline_prompt.py +++ b/powerline/ext/terminal/powerline_prompt.py @@ -1,14 +1,12 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -'''Powerline terminal prompt example. -''' +'''Powerline terminal prompt.''' try: from powerline.core import Powerline except ImportError: import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) - from powerline.core import Powerline pl = Powerline('terminal') diff --git a/powerline/ext/terminal/renderer.py b/powerline/ext/terminal/renderer.py index 0c5c5ebe..f946d400 100644 --- a/powerline/ext/terminal/renderer.py +++ b/powerline/ext/terminal/renderer.py @@ -4,8 +4,7 @@ from powerline.renderer import Renderer class TerminalRenderer(Renderer): - '''Powerline terminal segment renderer. - ''' + '''Powerline terminal segment renderer.''' def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -14,24 +13,20 @@ class TerminalRenderer(Renderer): is a valid color or attribute, it's added to the ANSI escape code. ''' ansi = [] - if fg is not None: if fg[0] is False: ansi += [39] else: ansi += [38, 5, fg[0]] - if bg is not None: if bg[0] is False: ansi += [49] else: ansi += [48, 5, bg[0]] - if attr is not None: if attr is False: ansi += [22] else: if attr & Renderer.ATTR_BOLD: ansi += [1] - return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) diff --git a/powerline/ext/terminal/segments.py b/powerline/ext/terminal/segments.py index 8f86b6df..3b84e15d 100644 --- a/powerline/ext/terminal/segments.py +++ b/powerline/ext/terminal/segments.py @@ -16,11 +16,10 @@ def hostname(): def user(): user = os.environ.get('USER') euid = os.geteuid() - return { 'contents': user, 'highlight': 'user' if euid != 0 else ['superuser', 'user'], - } + } def branch(): @@ -37,12 +36,9 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) cwd_split_len = len(cwd_split) - if cwd_split_len > dir_limit_depth + 1: del(cwd_split[0:-dir_limit_depth]) cwd_split.insert(0, u'…') - cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] cwd = os.path.join(*cwd) - return cwd diff --git a/powerline/ext/tmux/renderer.py b/powerline/ext/tmux/renderer.py index eb020b2a..55ebbdfd 100644 --- a/powerline/ext/tmux/renderer.py +++ b/powerline/ext/tmux/renderer.py @@ -4,25 +4,20 @@ from powerline.renderer import Renderer class TmuxRenderer(Renderer): - '''Powerline tmux segment renderer. - ''' + '''Powerline tmux segment renderer.''' def hl(self, fg=None, bg=None, attr=None): - '''Highlight a segment. - ''' + '''Highlight a segment.''' tmux_attr = [] - if fg is not None: if fg[0] is False: tmux_attr += ['fg=default'] else: tmux_attr += ['fg=colour' + str(fg[0])] - if bg is not None: if bg[0] is False: tmux_attr += ['bg=default'] else: tmux_attr += ['bg=colour' + str(bg[0])] - if attr is not None: if attr is False: tmux_attr += ['nobold', 'noitalics', 'nounderscore'] @@ -39,5 +34,4 @@ class TmuxRenderer(Renderer): tmux_attr += ['underscore'] else: tmux_attr += ['nounderscore'] - return '#[' + ','.join(tmux_attr) + ']' diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index c42c5faf..4cff5558 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -8,4 +8,4 @@ def user_name(): return { 'contents': user_name, 'highlight': 'user_name' if user_name != 'root' else ['user_name_root', 'user_name'], - } + } diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py index 72c1be90..a81ac37f 100644 --- a/powerline/ext/vim/__init__.py +++ b/powerline/ext/vim/__init__.py @@ -5,7 +5,5 @@ def source_plugin(): import os import vim from bindings import vim_get_func - fnameescape = vim_get_func('fnameescape') - vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim'))) diff --git a/powerline/ext/vim/bindings.py b/powerline/ext/vim/bindings.py index 69d1db9b..6608372e 100644 --- a/powerline/ext/vim/bindings.py +++ b/powerline/ext/vim/bindings.py @@ -6,13 +6,11 @@ try: _vim_globals = vim.bindeval('g:') def vim_set_global_var(var, val): - '''Set a global var in vim using bindeval(). - ''' + '''Set a global var in vim using bindeval().''' _vim_globals[var] = val def vim_get_func(f, rettype=None): - '''Return a vim function binding. - ''' + '''Return a vim function binding.''' try: return vim.bindeval('function("' + f + '")') except vim.error: diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index f3a16255..7cc5f1bd 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -12,8 +12,7 @@ vim_setwinvar = vim_get_func('setwinvar') class VimRenderer(Renderer): - '''Powerline vim segment renderer. - ''' + '''Powerline vim segment renderer.''' PERCENT_PLACEHOLDER = u'' def __init__(self, *args, **kwargs): @@ -30,7 +29,6 @@ class VimRenderer(Renderer): ''' window_id = vim_getwinvar(winnr, 'window_id') winwidth = vim_winwidth(winnr) - if current: mode = vim_mode() theme = self.get_theme() @@ -39,10 +37,8 @@ class VimRenderer(Renderer): else: mode = 'nc' theme, segments = self.window_cache.get(window_id, (None, None)) - statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') - return statusline def hl(self, fg=None, bg=None, attr=None): @@ -64,16 +60,13 @@ class VimRenderer(Renderer): 'guibg': 'NONE', 'attr': ['NONE'], 'name': '', - } - + } if fg is not None and fg is not False: hl_group['ctermfg'] = fg[0] hl_group['guifg'] = fg[1] - if bg is not None and bg is not False: hl_group['ctermbg'] = bg[0] hl_group['guibg'] = bg[1] - if attr: hl_group['attr'] = [] if attr & self.ATTR_BOLD: @@ -82,17 +75,13 @@ class VimRenderer(Renderer): hl_group['attr'].append('italic') if attr & self.ATTR_UNDERLINE: hl_group['attr'].append('underline') - hl_group['name'] = 'Pl_' + \ str(hl_group['ctermfg']) + '_' + \ str(hl_group['guifg']) + '_' + \ str(hl_group['ctermbg']) + '_' + \ str(hl_group['guibg']) + '_' + \ ''.join(hl_group['attr']) - 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'], @@ -101,5 +90,4 @@ class VimRenderer(Renderer): 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'] + '#' diff --git a/powerline/ext/vim/segments.py b/powerline/ext/vim/segments.py index 0dc538a2..c0f2fc5e 100644 --- a/powerline/ext/vim/segments.py +++ b/powerline/ext/vim/segments.py @@ -47,10 +47,8 @@ def mode(override=None): mode = mode({ 'n': 'NORM' }) ''' mode = vim_funcs['mode']() - if not override: return vim_modes[mode] - try: return override[mode] except IndexError: @@ -58,44 +56,36 @@ def mode(override=None): def modified_indicator(text=u'+'): - '''Return a file modified indicator. - ''' + '''Return a file modified indicator.''' return text if int(vim.eval('&modified')) else None def paste_indicator(text='PASTE'): - '''Return a paste mode indicator. - ''' + '''Return a paste mode indicator.''' return text if int(vim.eval('&paste')) else None def readonly_indicator(text=u''): - '''Return a read-only indicator. - ''' + '''Return a read-only indicator.''' return text if int(vim.eval('&readonly')) else None def file_directory(): - '''Return file directory (head component of the file path). - ''' + '''Return file directory (head component of the file path).''' file_directory = vim_funcs['expand']('%:~:.:h') return file_directory + os.sep if file_directory else None def file_name(display_no_file=False, no_file_text='[No file]'): - '''Return file name (tail component of the file path). - ''' + '''Return file name (tail component of the file path).''' file_name = vim_funcs['expand']('%:~:.:t') - if not file_name and not display_no_file: return None - if not file_name: return { 'contents': no_file_text, 'highlight': ['file_name_no_file', 'file_name'], - } - + } return file_name @@ -124,24 +114,20 @@ def file_type(): def line_percent(gradient=False): - '''Return the cursor position in the file as a percentage. - ''' + '''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) - if not gradient: return percentage - return { 'contents': percentage, 'highlight': ['line_percent_gradient' + str(int(5 * percentage // 100) + 1), 'line_percent'], - } + } def line_current(): - '''Return the current cursor line. - ''' + '''Return the current cursor line.''' return vim_funcs['line']('.') @@ -175,5 +161,5 @@ def file_vcs_status(): return { 'contents': status, 'highlight': ['file_vcs_status_' + status, 'file_vcs_status'], - } + } return None diff --git a/powerline/fontpatcher/fontpatcher.py b/powerline/fontpatcher/fontpatcher.py index 643263c9..b0869843 100755 --- a/powerline/fontpatcher/fontpatcher.py +++ b/powerline/fontpatcher/fontpatcher.py @@ -9,21 +9,17 @@ try: import psMat except ImportError: sys.stderr.write('The required FontForge modules could not be loaded.\n\n') - if sys.version_info.major > 2: sys.stderr.write('FontForge only supports Python 2. Please run this script with the Python 2 executable - e.g. "python2 {0}"\n'.format(sys.argv[0])) else: sys.stderr.write('You need FontForge with Python bindings for this script to work.\n') - sys.exit(1) # Handle command-line arguments parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') - parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) - args = parser.parse_args() @@ -36,10 +32,8 @@ class FontPatcher(object): def patch(self): for target_font in self.target_fonts: source_font = self.source_font - target_font_em_original = target_font.em target_font.em = 2048 - target_font.encoding = 'ISO10646' # Rename font @@ -61,10 +55,8 @@ class FontPatcher(object): bbox = target_font[cp].boundingBox() except TypeError: continue - if not target_font_width: target_font_width = target_font[cp].width - if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: @@ -83,7 +75,6 @@ class FontPatcher(object): x_diff = target_bb[0] - source_bb[0] y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) - transform = psMat.compose(scale, translate) # Create new glyphs from symbol font diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 77ef2d2b..0bee855d 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -8,8 +8,7 @@ import time class memoize(object): - '''Memoization decorator with timeout. - ''' + '''Memoization decorator with timeout.''' _cache = {} def __init__(self, timeout, additional_key=None, persistent=False, persistent_file=None): @@ -25,22 +24,18 @@ class memoize(object): key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) else: key = (func.__name__, args, tuple(kwargs.items())) - if self.persistent: try: with open(self.persistent_file, 'rb') as fileobj: self._cache = pickle.load(fileobj) except (IOError, EOFError): pass - cached = self._cache.get(key, None) - if cached is None or time.time() - cached['time'] > self.timeout: cached = self._cache[key] = { 'result': func(*args, **kwargs), 'time': time.time(), - } - + } if self.persistent: try: with open(self.persistent_file, 'wb') as fileobj: @@ -51,6 +46,5 @@ class memoize(object): except TypeError: # Unable to pickle function result pass - return cached['result'] return decorated_function diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 8c3ab9c2..28607dc9 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -27,7 +27,7 @@ try: if path: try: status = self._repo().status_file(path) - except KeyError, ValueError: + except (KeyError, ValueError): return None if status == git.GIT_STATUS_CURRENT: diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index 80b676d9..63b18dc6 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -32,7 +32,6 @@ class Repository(object): "U"nknown, "I"gnored, (None)Clean. ''' repo = self._repo() - if path: m = match.match(None, None, [path], exact=True) statuses = repo.status(match=m, unknown=True, ignored=True) diff --git a/powerline/matchers.py b/powerline/matchers.py index 6d16cb0d..c5f82e19 100644 --- a/powerline/matchers.py +++ b/powerline/matchers.py @@ -14,10 +14,8 @@ class Matchers(object): if not separator: match_module = 'powerline.ext.{0}.matchers'.format(self.ext) match_function = match_name - oldpath = sys.path sys.path = self.path + sys.path - try: return getattr(import_module(match_module), match_function) finally: diff --git a/powerline/renderer.py b/powerline/renderer.py index 770a85b1..77f98a10 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -39,16 +39,13 @@ class Renderer(object): # Handle excluded/included segments for the current mode segments = [segment for segment in segments\ if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] - rendered_highlighted = self._render_segments(mode, theme, segments) - if not width: # No width specified, so we don't need to crop or pad anything return rendered_highlighted # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] - while self._total_len(segments) > width and len(segments_priority): segments.remove(segments_priority[0]) segments_priority.pop(0) @@ -65,7 +62,6 @@ class Renderer(object): segment['contents'] = segments_fillers_contents # Add remainder whitespace to the first filler segment segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder - return self._render_segments(mode, theme, segments) def _render_segments(self, mode, theme, segments, render_highlighted=True): @@ -87,19 +83,16 @@ class Renderer(object): prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT 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 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' divider = 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': @@ -124,7 +117,6 @@ class Renderer(object): rendered_highlighted += segment_hl + segment['contents'] + outer_padding else: raise ValueError('Unknown segment type') - return rendered_highlighted def _total_len(self, segments): diff --git a/powerline/segments.py b/powerline/segments.py index 76d0d513..e2be730c 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -14,7 +14,6 @@ class Segments(object): oldpath = sys.path sys.path = self.path + sys.path segment_module = str(segment.get('module', 'powerline.ext.{0}.segments'.format(self.ext))) - try: return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) finally: @@ -30,14 +29,11 @@ class Segments(object): def get(self, segment, side): segment_type = segment.get('type', 'function') - try: contents, contents_func, key = getattr(self, 'get_{0}'.format(segment_type))(segment) except AttributeError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) - highlighting_group = segment.get('highlight', segment.get('name')) - return { 'key': key, 'type': segment_type, @@ -54,4 +50,4 @@ class Segments(object): 'side': side, 'exclude_modes': segment.get('exclude_modes', []), 'include_modes': segment.get('include_modes', []), - } + } diff --git a/powerline/theme.py b/powerline/theme.py index 73d126a4..7875b0fe 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -8,18 +8,15 @@ class Theme(object): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] - self.EMPTY_SEGMENT = { 'contents': None, 'highlight': {self.colorscheme.DEFAULT_MODE_KEY: {'fg': (False, False), 'bg': (False, False), 'attr': 0}} - } - + } for side in ['left', 'right']: self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) def get_divider(self, side='left', type='soft'): - '''Return segment divider. - ''' + '''Return segment divider.''' return self.dividers[side][type] def get_segments(self): @@ -31,10 +28,8 @@ class Theme(object): for segment in self.segments: if segment['type'] == 'function': contents = segment['contents_func'](**segment['args']) - if contents is None: continue - try: segment['highlight'] = self.colorscheme.get_group_highlighting(contents['highlight']) segment['contents'] = contents['contents'] @@ -44,11 +39,9 @@ class Theme(object): pass else: continue - segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ .ljust(segment['ljust'])\ .rjust(segment['rjust']) - # We need to yield a copy of the segment, or else mode-dependent # segment contents can't be cached correctly e.g. when caching # non-current window contents for vim statuslines diff --git a/setup.py b/setup.py index 35375bdf..587b2132 100755 --- a/setup.py +++ b/setup.py @@ -11,13 +11,6 @@ try: except IOError: README = '' -install_requires = [] - -docs_extras = [ - 'Sphinx', - 'docutils', -] - setup(name='Powerline', version='beta', description='The ultimate statusline/prompt utility.', @@ -31,8 +24,11 @@ setup(name='Powerline', include_package_data=True, zip_safe=False, test_suite='powerline', - install_requires=install_requires, + install_requires=[], extras_require={ - 'docs': docs_extras, - }, -) + 'docs': [ + 'Sphinx', + 'docutils', + ], + }, + ) From f827420ea16c3eba00fae3785bdece522a127825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 08:01:15 +0100 Subject: [PATCH 0150/1472] Rename default colorscheme --- powerline/colorschemes/{default.json => vim_default.json} | 0 powerline/config.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename powerline/colorschemes/{default.json => vim_default.json} (100%) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/vim_default.json similarity index 100% rename from powerline/colorschemes/default.json rename to powerline/colorschemes/vim_default.json diff --git a/powerline/config.json b/powerline/config.json index dfae53ff..e181034c 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -21,7 +21,7 @@ "theme": "default" }, "vim": { - "colorscheme": "default", + "colorscheme": "vim_default", "theme": "default", "local_themes": { "help": "help" From f6050fc08f05446da1987692908e2fd708d7c74d Mon Sep 17 00:00:00 2001 From: Liam Curry Date: Thu, 10 Jan 2013 13:55:18 -0500 Subject: [PATCH 0151/1472] Add right/left options to tmux script --- examples/tmux/pl.py | 16 +++++++++++++--- examples/tmux/tmux.conf | 5 ++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/tmux/pl.py b/examples/tmux/pl.py index 526d0e3b..d816f76d 100755 --- a/examples/tmux/pl.py +++ b/examples/tmux/pl.py @@ -4,12 +4,22 @@ Run with `tmux -f tmux.conf`. ''' - +import argparse import os import sys + sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from powerline.core import Powerline -pl = Powerline('tmux') -print(pl.renderer.render('n').encode('utf-8')) +parser = argparse.ArgumentParser(description='powerline outputter') +parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) +parser.add_argument('--ext', default='tmux') + +if __name__ == '__main__': + args = parser.parse_args() + pl = Powerline(args.ext) + segments = pl.renderer.get_theme().get_segments() + if args.side != 'all': + segments = [s for s in segments if s['side'] == args.side] + print(pl.renderer.render('n', segments=segments).encode('utf-8')) diff --git a/examples/tmux/tmux.conf b/examples/tmux/tmux.conf index c0c1aec9..fd4d656b 100644 --- a/examples/tmux/tmux.conf +++ b/examples/tmux/tmux.conf @@ -2,4 +2,7 @@ set-option -g status on set-option -g status-interval 2 set-option -g status-utf8 on set-option -g status-left-length 100 -set-option -g status-left "#(./pl.py)" +set-option -g status-left "#(./pl.py left)" +set-option -g status-right-length 100 +set-option -g status-right "#(./pl.py right)" +set-option -g status-justify "centre" From 5f0ec038e4059a0f8c85401f91094fedf80b80c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 08:21:36 +0100 Subject: [PATCH 0152/1472] Create neutral tmux colorscheme --- examples/tmux/tmux.conf | 2 ++ powerline/colorschemes/tmux_default.json | 29 ++++++++++++++++++++++++ powerline/config.json | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 powerline/colorschemes/tmux_default.json diff --git a/examples/tmux/tmux.conf b/examples/tmux/tmux.conf index fd4d656b..78ebb3fa 100644 --- a/examples/tmux/tmux.conf +++ b/examples/tmux/tmux.conf @@ -6,3 +6,5 @@ set-option -g status-left "#(./pl.py left)" set-option -g status-right-length 100 set-option -g status-right "#(./pl.py right)" set-option -g status-justify "centre" +set-option -g status-bg "colour235" +set-option -g status-fg "colour249" diff --git a/powerline/colorschemes/tmux_default.json b/powerline/colorschemes/tmux_default.json new file mode 100644 index 00000000..20ea2ce0 --- /dev/null +++ b/powerline/colorschemes/tmux_default.json @@ -0,0 +1,29 @@ +{ + "name": "Default color scheme for terminal prompts", + "colors": { + "black": 16, + "white": 231, + + "darkestblue": 24, + "darkblue": 31, + "mediumblue": 38, + "brightblue": 117, + "brightestblue": 153, + + "green": 148, + "yellow": 220, + "red": 202 + }, + "groups": { + "user": { "fg": "white", "bg": "mediumblue", "attr": ["bold"] }, + "date": { "fg": "white", "bg": "mediumblue" }, + "forecast": { "fg": "brightestblue", "bg": "darkblue" }, + "external_ip": { "fg": "brightestblue", "bg": "darkblue" }, + "system_load": { "fg": "brightblue", "bg": "darkestblue" }, + "system_load_good": { "fg": "green", "bg": "darkestblue" }, + "system_load_bad": { "fg": "yellow", "bg": "darkestblue" }, + "system_load_ugly": { "fg": "red", "bg": "darkestblue" }, + "uptime": { "fg": "brightblue", "bg": "darkestblue" } + }, + "mode_translations": { } +} diff --git a/powerline/config.json b/powerline/config.json index e181034c..306139bf 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -17,7 +17,7 @@ "theme": "default" }, "tmux": { - "colorscheme": "default", + "colorscheme": "tmux_default", "theme": "default" }, "vim": { From 52c2ba5c9fc3eada390f99454b59ad6569923933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 08:21:52 +0100 Subject: [PATCH 0153/1472] Enable tmux segments --- powerline/themes/tmux/default.json | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index 4f368ee5..f5cb4bad 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -2,7 +2,35 @@ "segments": { "left": [ { - "name": "user_name" + "module": "powerline.ext.terminal.segments", + "name": "user" + }, + { + "name": "external_ip", + "before": "ⓦ " + }, + { + "name": "uptime", + "before": "⇑ " + } + ], + "right": [ + { + "name": "system_load" + }, + { + "name": "forecast" + }, + { + "name": "date", + "args": {"format": "%a"} + }, + { + "name": "date" + }, + { + "name": "date", + "args": {"format": "%H:%M"} } ] } From 07771d77731ed818d35e1a7e223356f1d48fb148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 08:31:41 +0100 Subject: [PATCH 0154/1472] Add Liam Curry to contributors list Refs #44. --- docs/source/license-credits.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index e045e6ef..24eba2ac 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -15,6 +15,7 @@ Credits :Author: `Kim Silkebækken `_ :Main contributors: * `ZyX-I `_ + * `Liam Curry `_ The glyphs in the font patcher are created by Fabrizio Schiavi, creator of the excellent coding font `Pragmata Pro`_. From b7ff63ccc0e7d38cee21197e4f51ba5932f8a0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 08:36:39 +0100 Subject: [PATCH 0155/1472] Separate colorschemes by extension Refs #45. --- docs/source/configuration.rst | 2 +- .../{terminal_default.json => terminal/default.json} | 0 .../colorschemes/{tmux_default.json => tmux/default.json} | 0 .../colorschemes/{vim_default.json => vim/default.json} | 0 powerline/config.json | 6 +++--- powerline/core.py | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename powerline/colorschemes/{terminal_default.json => terminal/default.json} (100%) rename powerline/colorschemes/{tmux_default.json => tmux/default.json} (100%) rename powerline/colorschemes/{vim_default.json => vim/default.json} (100%) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index fbc1b9fc..17cd543e 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -73,7 +73,7 @@ Extension-specific configuration Colorschemes ------------ -:Location: :file:`powerline/colorschemes/{name}.json` +:Location: :file:`powerline/colorschemes/{extension}/{name}.json` ``name`` Name of the colorscheme. diff --git a/powerline/colorschemes/terminal_default.json b/powerline/colorschemes/terminal/default.json similarity index 100% rename from powerline/colorschemes/terminal_default.json rename to powerline/colorschemes/terminal/default.json diff --git a/powerline/colorschemes/tmux_default.json b/powerline/colorschemes/tmux/default.json similarity index 100% rename from powerline/colorschemes/tmux_default.json rename to powerline/colorschemes/tmux/default.json diff --git a/powerline/colorschemes/vim_default.json b/powerline/colorschemes/vim/default.json similarity index 100% rename from powerline/colorschemes/vim_default.json rename to powerline/colorschemes/vim/default.json diff --git a/powerline/config.json b/powerline/config.json index 306139bf..70c85d46 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -13,15 +13,15 @@ }, "ext": { "terminal": { - "colorscheme": "terminal_default", + "colorscheme": "default", "theme": "default" }, "tmux": { - "colorscheme": "tmux_default", + "colorscheme": "default", "theme": "default" }, "vim": { - "colorscheme": "vim_default", + "colorscheme": "default", "theme": "default", "local_themes": { "help": "help" diff --git a/powerline/core.py b/powerline/core.py index f7835f3f..755a62fe 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -24,7 +24,7 @@ class Powerline(object): self.config_ext = config['ext'][ext] # Load and initialize colorscheme - colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config_ext['colorscheme'])) + colorscheme_config = self._load_json_config(os.path.join('colorschemes', ext, self.config_ext['colorscheme'])) colorscheme = Colorscheme(colorscheme_config) # Load and initialize extension theme From 884bbd3f29454898032754c4b532c58518365eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 08:47:49 +0100 Subject: [PATCH 0156/1472] Make mode_translations optional in colorschemes --- powerline/colorscheme.py | 2 +- powerline/colorschemes/terminal/default.json | 3 +-- powerline/colorschemes/tmux/default.json | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index e03d738d..89330603 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -26,7 +26,7 @@ class Colorscheme(object): } # Create mode-specific highlighting for this group - for mode, translations in colorscheme['mode_translations'].items(): + for mode, translations in colorscheme.get('mode_translations', {}).items(): if not mode in self.modes_groups: self.modes_groups[mode] = {} if group_name in translations.get('groups', {}): diff --git a/powerline/colorschemes/terminal/default.json b/powerline/colorschemes/terminal/default.json index f0436ae0..5d4ccc2e 100644 --- a/powerline/colorschemes/terminal/default.json +++ b/powerline/colorschemes/terminal/default.json @@ -58,6 +58,5 @@ "branch": { "fg": "gray9", "bg": "gray4" }, "cwd": { "fg": "gray10", "bg": "gray4" }, "hostname": { "fg": "brightyellow", "bg": "mediumorange" } - }, - "mode_translations": { } + } } diff --git a/powerline/colorschemes/tmux/default.json b/powerline/colorschemes/tmux/default.json index 20ea2ce0..42bb6281 100644 --- a/powerline/colorschemes/tmux/default.json +++ b/powerline/colorschemes/tmux/default.json @@ -24,6 +24,5 @@ "system_load_bad": { "fg": "yellow", "bg": "darkestblue" }, "system_load_ugly": { "fg": "red", "bg": "darkestblue" }, "uptime": { "fg": "brightblue", "bg": "darkestblue" } - }, - "mode_translations": { } + } } From bf23ae286eb73483099aa4e40d20e73b9d02474f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 09:00:19 +0100 Subject: [PATCH 0157/1472] Move tmux example into extension directory --- .../ext/tmux/powerline_status.py | 19 +++++++++---------- {examples => powerline/ext}/tmux/tmux.conf | 4 ++-- 2 files changed, 11 insertions(+), 12 deletions(-) rename examples/tmux/pl.py => powerline/ext/tmux/powerline_status.py (59%) rename {examples => powerline/ext}/tmux/tmux.conf (69%) diff --git a/examples/tmux/pl.py b/powerline/ext/tmux/powerline_status.py similarity index 59% rename from examples/tmux/pl.py rename to powerline/ext/tmux/powerline_status.py index d816f76d..687a1cbd 100755 --- a/examples/tmux/pl.py +++ b/powerline/ext/tmux/powerline_status.py @@ -1,18 +1,17 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -'''Powerline tmux statusline example. - -Run with `tmux -f tmux.conf`. -''' +'''Powerline tmux statusline.''' import argparse -import os -import sys -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +try: + from powerline.core import Powerline +except ImportError: + import os + import sys + sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) + from powerline.core import Powerline -from powerline.core import Powerline - -parser = argparse.ArgumentParser(description='powerline outputter') +parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) parser.add_argument('--ext', default='tmux') diff --git a/examples/tmux/tmux.conf b/powerline/ext/tmux/tmux.conf similarity index 69% rename from examples/tmux/tmux.conf rename to powerline/ext/tmux/tmux.conf index 78ebb3fa..8267536d 100644 --- a/examples/tmux/tmux.conf +++ b/powerline/ext/tmux/tmux.conf @@ -2,9 +2,9 @@ set-option -g status on set-option -g status-interval 2 set-option -g status-utf8 on set-option -g status-left-length 100 -set-option -g status-left "#(./pl.py left)" +set-option -g status-left "#(./powerline_status.py left)" set-option -g status-right-length 100 -set-option -g status-right "#(./pl.py right)" +set-option -g status-right "#(./powerline_status.py right)" set-option -g status-justify "centre" set-option -g status-bg "colour235" set-option -g status-fg "colour249" From b91009404f0ad4b5450259a70cfa480cc1d8e6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 09:04:26 +0100 Subject: [PATCH 0158/1472] Use midline ellipsis for dir shortening --- powerline/ext/terminal/segments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/ext/terminal/segments.py b/powerline/ext/terminal/segments.py index 3b84e15d..81ffedc5 100644 --- a/powerline/ext/terminal/segments.py +++ b/powerline/ext/terminal/segments.py @@ -38,7 +38,7 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): cwd_split_len = len(cwd_split) if cwd_split_len > dir_limit_depth + 1: del(cwd_split[0:-dir_limit_depth]) - cwd_split.insert(0, u'…') + cwd_split.insert(0, u'⋯') cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] cwd = os.path.join(*cwd) return cwd From ecf9e7eea779e30ab55e4b43f0536bbe3bc05f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 16 Jan 2013 09:39:20 +0100 Subject: [PATCH 0159/1472] Update terminal renderer output depending on shell Bash and zsh have different ways of escaping colors in the prompt, this update checks the $SHELL environment variable and uses the correct escape sequence for the current shell. A known issue with this method is that $SHELL doesn't get updated when switching shells (i.e. if you're running /bin/bash when you're in a zsh shell) so in rare cases rendering errors may occur. A workaround which is much slower is to parse the output of `ps -p $$` which always returns the current shell. --- powerline/ext/terminal/renderer.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/powerline/ext/terminal/renderer.py b/powerline/ext/terminal/renderer.py index f946d400..3c975de3 100644 --- a/powerline/ext/terminal/renderer.py +++ b/powerline/ext/terminal/renderer.py @@ -1,10 +1,23 @@ # -*- coding: utf-8 -*- +import os + from powerline.renderer import Renderer class TerminalRenderer(Renderer): '''Powerline terminal segment renderer.''' + _color_templates = { + 'default': '[{code}m', + 'bash': '\[[{code}m\]', + 'zsh': '%{{[{code}m%}}', + } + + def __init__(self, *args, **kwargs): + super(TerminalRenderer, self).__init__(*args, **kwargs) + shell = os.path.basename(os.environ.get('SHELL', 'default')) + self.color_template = self._color_templates[shell] + def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -29,4 +42,4 @@ class TerminalRenderer(Renderer): else: if attr & Renderer.ATTR_BOLD: ansi += [1] - return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + return self.color_template.format(code=';'.join(str(attr) for attr in ansi)) From 128c012553ccdd3a4e2e29799c60247f5c41d27f Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 16 Jan 2013 18:37:00 +0400 Subject: [PATCH 0160/1472] Add foreign local themes support Refs #3. --- powerline/core.py | 22 ++++++++++++++++++++-- powerline/renderer.py | 5 +++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 755a62fe..79514d0b 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -31,7 +31,7 @@ class Powerline(object): theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) path = [os.path.expanduser(path) for path in self.config.get('paths', [])] get_segment = Segments(ext, path, colorscheme).get - get_matcher = Matchers(ext, path).get + self.get_matcher = Matchers(ext, path).get theme_kwargs = { 'ext': ext, 'colorscheme': colorscheme, @@ -40,7 +40,7 @@ class Powerline(object): } local_themes = {} for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): - key = get_matcher(key) + key = self.get_matcher(key) local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} # Load and initialize extension renderer @@ -49,6 +49,24 @@ class Powerline(object): Renderer = getattr(importlib.import_module(renderer_module_name), renderer_class_name) self.renderer = Renderer(theme_config, local_themes, theme_kwargs) + def add_local_theme(self, key, config): + '''Add local themes at runtime (e.g. during vim session). + + Accepts key as first argument (same as keys in config.json: + ext/*/local_themes) and configuration dictionary as the second (has + format identical to themes/*/*.json) + + Returns True if theme was added successfully and False if theme with + the same matcher already exists + ''' + key = self.get_matcher(key) + try: + self.renderer.add_local_theme(key, {'config': config}) + except KeyError: + return False + else: + return True + def _load_theme_config(self, ext, name): return self._load_json_config(os.path.join('themes', ext, name)) diff --git a/powerline/renderer.py b/powerline/renderer.py index 77f98a10..fdaf544f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -14,6 +14,11 @@ class Renderer(object): self.local_themes = local_themes self.theme_kwargs = theme_kwargs + def add_local_theme(self, matcher, theme): + if matcher in self.local_themes: + raise KeyError('There is already a local theme with given matcher') + self.local_themes[matcher] = theme + def get_theme(self): for matcher in self.local_themes.iterkeys(): if matcher(): From 4fc6bd756af9a07befcde37cb96c9adc825002d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 08:29:04 +0100 Subject: [PATCH 0161/1472] Update README --- README.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 2221ef8d..4166d38e 100644 --- a/README.rst +++ b/README.rst @@ -5,16 +5,20 @@ Powerline :Source: https://github.com/Lokaltog/powerline :Version: beta -This is the next version of Powerline, implemented in Python. The project is -currently in beta, and most of the functionality in the old vimscript -project is already implemented. +This is the upcoming version of Powerline, implemented in Python. The +project is currently in a stable beta and almost ready for release. -Check out the `documentation `_ for -more information and installation instructions. +* Consult the `documentation `_ for + more information and installation instructions. +* Check out `powerline-fonts `_ + for pre-patched versions of popular coding fonts. Screenshots ----------- +Vim statusline +^^^^^^^^^^^^^^ + **Mode-dependent highlighting** * .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-normal.png @@ -35,6 +39,8 @@ Screenshots * .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate3.png :alt: Truncation illustration +---- + The font in the screenshots is `Pragmata Pro`_ by Fabrizio Schiavi. .. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm From fd94c7c0badbd60f52c98fc4fead05321c99e48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 09:25:56 +0100 Subject: [PATCH 0162/1472] Install tmux/prompt files as scripts --- powerline/ext/tmux/tmux.conf | 4 ++-- .../terminal/powerline_prompt.py => scripts/powerline-prompt | 2 +- .../ext/tmux/powerline_status.py => scripts/powerline-tmux | 2 +- setup.py | 4 ++++ 4 files changed, 8 insertions(+), 4 deletions(-) rename powerline/ext/terminal/powerline_prompt.py => scripts/powerline-prompt (71%) rename powerline/ext/tmux/powerline_status.py => scripts/powerline-tmux (86%) diff --git a/powerline/ext/tmux/tmux.conf b/powerline/ext/tmux/tmux.conf index 8267536d..13cbe37a 100644 --- a/powerline/ext/tmux/tmux.conf +++ b/powerline/ext/tmux/tmux.conf @@ -2,9 +2,9 @@ set-option -g status on set-option -g status-interval 2 set-option -g status-utf8 on set-option -g status-left-length 100 -set-option -g status-left "#(./powerline_status.py left)" +set-option -g status-left "#(powerline-tmux left)" set-option -g status-right-length 100 -set-option -g status-right "#(./powerline_status.py right)" +set-option -g status-right "#(powerline-tmux right)" set-option -g status-justify "centre" set-option -g status-bg "colour235" set-option -g status-fg "colour249" diff --git a/powerline/ext/terminal/powerline_prompt.py b/scripts/powerline-prompt similarity index 71% rename from powerline/ext/terminal/powerline_prompt.py rename to scripts/powerline-prompt index fde8d414..ab281b05 100755 --- a/powerline/ext/terminal/powerline_prompt.py +++ b/scripts/powerline-prompt @@ -6,7 +6,7 @@ try: except ImportError: import os import sys - sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from powerline.core import Powerline pl = Powerline('terminal') diff --git a/powerline/ext/tmux/powerline_status.py b/scripts/powerline-tmux similarity index 86% rename from powerline/ext/tmux/powerline_status.py rename to scripts/powerline-tmux index 687a1cbd..13f5e8c9 100755 --- a/powerline/ext/tmux/powerline_status.py +++ b/scripts/powerline-tmux @@ -8,7 +8,7 @@ try: except ImportError: import os import sys - sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from powerline.core import Powerline parser = argparse.ArgumentParser(description=__doc__) diff --git a/setup.py b/setup.py index 587b2132..3cbf7805 100755 --- a/setup.py +++ b/setup.py @@ -19,6 +19,10 @@ setup(name='Powerline', author='Kim Silkebækken', author_email='kim.silkebaekken+vim@gmail.com', url='https://github.com/Lokaltog/powerline', + scripts=[ + 'scripts/powerline-prompt', + 'scripts/powerline-tmux', + ], keywords='', packages=find_packages(), include_package_data=True, From a091d04a8c94b2130d18a92f454a27bedb179be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 09:42:15 +0100 Subject: [PATCH 0163/1472] Update terminal rendering for bash --- powerline/ext/terminal/renderer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/powerline/ext/terminal/renderer.py b/powerline/ext/terminal/renderer.py index 3c975de3..023a73c8 100644 --- a/powerline/ext/terminal/renderer.py +++ b/powerline/ext/terminal/renderer.py @@ -9,14 +9,13 @@ class TerminalRenderer(Renderer): '''Powerline terminal segment renderer.''' _color_templates = { 'default': '[{code}m', - 'bash': '\[[{code}m\]', 'zsh': '%{{[{code}m%}}', } def __init__(self, *args, **kwargs): super(TerminalRenderer, self).__init__(*args, **kwargs) - shell = os.path.basename(os.environ.get('SHELL', 'default')) - self.color_template = self._color_templates[shell] + shell = os.path.basename(os.environ.get('SHELL')) + self.color_template = self._color_templates.get(shell, self._color_templates['default']) def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment. From 6bf501df51586b3e8a8cc723aeff496757c8001f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 09:42:39 +0100 Subject: [PATCH 0164/1472] Add usage instructions for terminal/tmux --- docs/source/overview.rst | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index fe172ed8..67973109 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -51,8 +51,8 @@ they provide an easy way of installing and upgrading Powerline: Usage ----- -Vim usage -^^^^^^^^^ +Vim statusline +^^^^^^^^^^^^^^ If Powerline is installed as a Python package, you can enable the vim plugin by adding the following line to your ``vimrc``:: @@ -68,3 +68,35 @@ Add the following line to your ``vimrc``, where ``{path}`` is the path to the main Powerline project directory:: source {path}/powerline/ext/vim/source_plugin.vim + +Terminal prompt +^^^^^^^^^^^^^^^ + +Add the following to your ``.bashrc``/``.zshrc``:: + + export PS1=`powerline-prompt` + +If Powerline is installed somewhere other than Python's site-packages you'll +have to specify the full path to the script:: + + export PS1=`/path/to/powerline/scripts/powerline-prompt` + +Tmux statusline +^^^^^^^^^^^^^^^ + +Add the following to your ``tmux.conf``:: + + set-option -g status on + set-option -g status-interval 2 + set-option -g status-utf8 on + set-option -g status-left-length 100 + set-option -g status-left "#(powerline-tmux left)" + set-option -g status-right-length 100 + set-option -g status-right "#(powerline-tmux right)" + set-option -g status-justify "centre" + set-option -g status-bg "colour235" + set-option -g status-fg "colour249" + +If Powerline is installed somewhere other than Python's site-packages you'll +have to specify the full path to the script in the ``status-left`` and +``status-right`` options. From 7d613baec2433e1466754d3e80016f3411af9b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 10:01:09 +0100 Subject: [PATCH 0165/1472] Fix font patching instructions for OS X Closes #46. --- docs/source/fontpatching.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index 17df92dd..b91dd8d2 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -106,7 +106,7 @@ OS X $ brew uninstall fontforge $ brew update - $ brew install --use-gcc fontforge + $ brew install fontforge **Note:** You may have to use ``--use-clang`` instead of ``--use-gcc`` when compiling FontForge. @@ -114,7 +114,7 @@ OS X 3. If you don't have FontForge, install it with Homebrew:: $ brew update - $ brew install --use-gcc fontforge + $ brew install fontforge 4. Patch your fonts by passing the ``fontpatcher`` script as a parameter to FontForge:: From f18bf7087dc15b6e87b097afe48dbd0ca01f12b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 10:01:48 +0100 Subject: [PATCH 0166/1472] Add troubleshooting info for iTerm2 question mark issue Closes #27. Refs #48. --- docs/source/troubleshooting.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index a9a85c6c..8af8302b 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -10,6 +10,10 @@ I can't see any fancy symbols, what's wrong? If you're using rxvt-unicode, make sure that it's compiled with the ``--enable-unicode3`` flag. + If you're using iTerm2, please update to `this revision + `_ + or newer. + You need to set your ``LANG`` and ``LC_*`` environment variables to a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's documentation for information about setting these variables correctly. From 6207f90734b352ecffb5ac96b5dc523f1aa0019b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 10:03:00 +0100 Subject: [PATCH 0167/1472] Add troubleshooting info about NameError in MacVim Refs #48. --- docs/source/troubleshooting.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 8af8302b..22ef4340 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -67,3 +67,14 @@ The statusline is hidden/only appears in split windows! I'm using gVim for Windows, and ``cmd`` windows keep popping up when working in git repos! Either install ``libgit2`` and ``pygit2``, or disable the VCS segment in your user configuration to resolve this issue. + +I receive a ``NameError`` when trying to use Powerline with MacVim! + Please install MacVim using this command:: + + brew install macvim --env-std --override-system-vim + + Then install Powerline locally with ``pip install --user``, or by + running these commands in the ``powerline`` directory:: + + ./setup.py build + ./setup.py install --user From 20b17cebe0acf48d447a6bbec1cd436d9e126b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 10:03:29 +0100 Subject: [PATCH 0168/1472] Add troubleshooting info about OSX ImportError Closes #39. --- docs/source/troubleshooting.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 22ef4340..f08ac131 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -78,3 +78,9 @@ I receive a ``NameError`` when trying to use Powerline with MacVim! ./setup.py build ./setup.py install --user + +I receive an ``ImportError`` when trying to use Powerline on OS X! + This is caused by an invalid ``sys.path`` when using system vim and + system Python. Please try to select another Python distribution:: + + sudo port select python python27-apple From 8a5b8719cd78f3bc028dbd7ccf0c346507462ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 10:08:51 +0100 Subject: [PATCH 0169/1472] Escape command in prompt usage instructions --- docs/source/overview.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 67973109..ce16c5e5 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -74,12 +74,12 @@ Terminal prompt Add the following to your ``.bashrc``/``.zshrc``:: - export PS1=`powerline-prompt` + export PS1=\`powerline-prompt\` If Powerline is installed somewhere other than Python's site-packages you'll have to specify the full path to the script:: - export PS1=`/path/to/powerline/scripts/powerline-prompt` + export PS1=\`/path/to/powerline/scripts/powerline-prompt\` Tmux statusline ^^^^^^^^^^^^^^^ From 1a99fbd36a18c3815ee8da2a28f4f7741994f008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 17:06:45 +0100 Subject: [PATCH 0170/1472] Update terminal prompt usage instructions Closes #56. --- docs/source/overview.rst | 41 +++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index ce16c5e5..64bd3f9e 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -51,6 +51,11 @@ they provide an easy way of installing and upgrading Powerline: Usage ----- +.. note:: If Powerline is installed somewhere other than Python's + site-packages directories (e.g. by having the git repo in your dotfiles + directory) you'll have to use the absolute path to the scripts in the + examples below. + Vim statusline ^^^^^^^^^^^^^^ @@ -60,27 +65,37 @@ by adding the following line to your ``vimrc``:: python from powerline.ext.vim import source_plugin; source_plugin() If Powerline is installed somewhere other than Python's site-packages -directories (e.g. by having the git repo in your dotfiles directory) you'll -have to source the vim plugin file with an absolute path to the plugin -location. +directories you'll have to source the vim plugin file with an absolute path +to the plugin location. Add the following line to your ``vimrc``, where ``{path}`` is the path to the main Powerline project directory:: source {path}/powerline/ext/vim/source_plugin.vim -Terminal prompt -^^^^^^^^^^^^^^^ +Terminal prompts +^^^^^^^^^^^^^^^^ -Add the following to your ``.bashrc``/``.zshrc``:: +Bash prompt +*********** +Add the following to your ``.bashrc``:: + + function _update_ps1() { + export PS1="$(powerline-prompt)" + } + + export PROMPT_COMMAND="_update_ps1" + + +Zsh prompt +********** + +Add the following to your ``.zshrc``:: + + setopt prompt_subst export PS1=\`powerline-prompt\` -If Powerline is installed somewhere other than Python's site-packages you'll -have to specify the full path to the script:: - - export PS1=\`/path/to/powerline/scripts/powerline-prompt\` - Tmux statusline ^^^^^^^^^^^^^^^ @@ -96,7 +111,3 @@ Add the following to your ``tmux.conf``:: set-option -g status-justify "centre" set-option -g status-bg "colour235" set-option -g status-fg "colour249" - -If Powerline is installed somewhere other than Python's site-packages you'll -have to specify the full path to the script in the ``status-left`` and -``status-right`` options. From 9ec3a8ef5edb3462e389c9d4b79113d65a3899ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 17:24:58 +0100 Subject: [PATCH 0171/1472] Reset highlighting at end of rendered result Closes #57. --- powerline/renderer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/renderer.py b/powerline/renderer.py index fdaf544f..9abe5b8e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -122,6 +122,7 @@ class Renderer(object): rendered_highlighted += segment_hl + segment['contents'] + outer_padding else: raise ValueError('Unknown segment type') + rendered_highlighted += self.hl() return rendered_highlighted def _total_len(self, segments): From 07f1b7d69737130680f8642156290b60908ee9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 20:01:39 +0100 Subject: [PATCH 0172/1472] Rename weather segment --- powerline/colorschemes/tmux/default.json | 2 +- powerline/ext/tmux/segments.py | 7 +------ powerline/themes/tmux/default.json | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/powerline/colorschemes/tmux/default.json b/powerline/colorschemes/tmux/default.json index 42bb6281..f5709f86 100644 --- a/powerline/colorschemes/tmux/default.json +++ b/powerline/colorschemes/tmux/default.json @@ -17,7 +17,7 @@ "groups": { "user": { "fg": "white", "bg": "mediumblue", "attr": ["bold"] }, "date": { "fg": "white", "bg": "mediumblue" }, - "forecast": { "fg": "brightestblue", "bg": "darkblue" }, + "weather": { "fg": "brightestblue", "bg": "darkblue" }, "external_ip": { "fg": "brightestblue", "bg": "darkblue" }, "system_load": { "fg": "brightblue", "bg": "darkestblue" }, "system_load_good": { "fg": "green", "bg": "darkestblue" }, diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index 9f952628..b66740d3 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -56,14 +56,13 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) - return format.format(days=int(days), hours=hours, minutes=minutes) except IOError: pass @memoize(600, persistent=True) -def forecast(unit='c', location_query=None): +def weather(unit='c', location_query=None): import json import urllib import urllib2 @@ -74,7 +73,6 @@ def forecast(unit='c', location_query=None): location_query = ','.join([location['city'], location['region_name'], location['country_name']]) except ValueError: return None - query_data = { 'q': 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' @@ -83,13 +81,10 @@ def forecast(unit='c', location_query=None): } url = 'http://query.yahooapis.com/v1/public/yql?' + urllib.urlencode(query_data) response = json.loads(urllib2.urlopen(url).read()) - condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) icon = u'〇' - for icon, codes in weather_conditions_codes.items(): if condition_code in codes: break - return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index f5cb4bad..22fc70fc 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -19,7 +19,7 @@ "name": "system_load" }, { - "name": "forecast" + "name": "weather" }, { "name": "date", From 245be99307f90b2bcd58f85255a1ccba68eb5967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 20:02:04 +0100 Subject: [PATCH 0173/1472] Add humanize_bytes function to library --- powerline/lib/humanize_bytes.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 powerline/lib/humanize_bytes.py diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py new file mode 100644 index 00000000..3940e5a8 --- /dev/null +++ b/powerline/lib/humanize_bytes.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from math import log +unit_list = zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2]) + + +def humanize_bytes(num, suffix='B', binary_prefix=False): + '''Return a human friendly byte representation. + + Modified version from http://stackoverflow.com/questions/1094841 + ''' + if num == 0: + return '0 ' + suffix + div = 1000 if binary_prefix else 1024 + exponent = min(int(log(num, div)), len(unit_list) - 1) + quotient = float(num) / div ** exponent + unit, decimals = unit_list[exponent] + if unit and binary_prefix: + unit += 'i' + return '{{quotient:.{decimals}f}} {{unit}}{{suffix}}'\ + .format(decimals=decimals)\ + .format(quotient=quotient, unit=unit, suffix=suffix) From 7eca187d029150781ab136498a41592ab84a5a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 20:02:24 +0100 Subject: [PATCH 0174/1472] Add imports to lib init file --- powerline/ext/tmux/segments.py | 2 +- powerline/ext/vim/segments.py | 2 +- powerline/lib/__init__.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index b66740d3..4d0bdfe1 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -2,7 +2,7 @@ import os -from powerline.lib.memoize import memoize +from powerline.lib import memoize # Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes weather_conditions_codes = { diff --git a/powerline/ext/vim/segments.py b/powerline/ext/vim/segments.py index c0f2fc5e..575fb9d6 100644 --- a/powerline/ext/vim/segments.py +++ b/powerline/ext/vim/segments.py @@ -4,7 +4,7 @@ import os import vim from powerline.ext.vim.bindings import vim_get_func -from powerline.lib.memoize import memoize +from powerline.lib import memoize from powerline.lib.vcs import guess vim_funcs = { diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index e69de29b..8d2de86f 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -0,0 +1,2 @@ +from humanize_bytes import humanize_bytes +from memoize import memoize From 0ea7615167486ec582d22e2aff2ada420c29ca4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 20:05:42 +0100 Subject: [PATCH 0175/1472] Add network load tmux segment --- powerline/colorschemes/tmux/default.json | 1 + powerline/ext/tmux/segments.py | 23 +++++++++++++++++++++++ powerline/themes/tmux/default.json | 3 +++ 3 files changed, 27 insertions(+) diff --git a/powerline/colorschemes/tmux/default.json b/powerline/colorschemes/tmux/default.json index f5709f86..7c3e853a 100644 --- a/powerline/colorschemes/tmux/default.json +++ b/powerline/colorschemes/tmux/default.json @@ -18,6 +18,7 @@ "user": { "fg": "white", "bg": "mediumblue", "attr": ["bold"] }, "date": { "fg": "white", "bg": "mediumblue" }, "weather": { "fg": "brightestblue", "bg": "darkblue" }, + "network_load": { "fg": "brightestblue", "bg": "darkblue" }, "external_ip": { "fg": "brightestblue", "bg": "darkblue" }, "system_load": { "fg": "brightblue", "bg": "darkestblue" }, "system_load_good": { "fg": "green", "bg": "darkestblue" }, diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index 4d0bdfe1..730a4706 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -88,3 +88,26 @@ def weather(unit='c', location_query=None): if condition_code in codes: break return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) + + +def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_prefix=False): + import time + from powerline.lib import humanize_bytes + + def get_bytes(): + try: + with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: + rx = int(file_obj.read()) + with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: + tx = int(file_obj.read()) + return (rx, tx) + except IOError: + return (0, 0) + + b1 = get_bytes() + time.sleep(measure_interval) + b2 = get_bytes() + return u'⬇ {rx_diff} ⬆ {tx_diff}'.format( + rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, binary_prefix), + tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, binary_prefix), + ) diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index 22fc70fc..6a468d23 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -9,6 +9,9 @@ "name": "external_ip", "before": "ⓦ " }, + { + "name": "network_load" + }, { "name": "uptime", "before": "⇑ " From 2fba8f0502781f0d3a7335b6a3cfdfc559e4ddd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 20:10:28 +0100 Subject: [PATCH 0176/1472] Don't explicitly reset tmux attributes --- powerline/ext/tmux/renderer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/ext/tmux/renderer.py b/powerline/ext/tmux/renderer.py index 55ebbdfd..c0112edb 100644 --- a/powerline/ext/tmux/renderer.py +++ b/powerline/ext/tmux/renderer.py @@ -7,6 +7,9 @@ class TmuxRenderer(Renderer): '''Powerline tmux segment renderer.''' def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment.''' + # We don't need to explicitly reset attributes, so skip those calls + if not attr and not bg and not fg: + return '' tmux_attr = [] if fg is not None: if fg[0] is False: From a67ab948e5ea521c8dba930eb2900752cbabc54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 20:08:47 +0100 Subject: [PATCH 0177/1472] Remove example tmux.conf --- powerline/ext/tmux/tmux.conf | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 powerline/ext/tmux/tmux.conf diff --git a/powerline/ext/tmux/tmux.conf b/powerline/ext/tmux/tmux.conf deleted file mode 100644 index 13cbe37a..00000000 --- a/powerline/ext/tmux/tmux.conf +++ /dev/null @@ -1,10 +0,0 @@ -set-option -g status on -set-option -g status-interval 2 -set-option -g status-utf8 on -set-option -g status-left-length 100 -set-option -g status-left "#(powerline-tmux left)" -set-option -g status-right-length 100 -set-option -g status-right "#(powerline-tmux right)" -set-option -g status-justify "centre" -set-option -g status-bg "colour235" -set-option -g status-fg "colour249" From 2cfd01ec73ab7ffc628db98ef11e43285246a572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 17 Jan 2013 21:32:15 +0100 Subject: [PATCH 0178/1472] Add PowerlineSymbols font file On Linux it appears to work perfectly to just have this font file present on the system as it automatically chooses fallback fonts if a glyph isn't present in the current font. --- powerline/fontpatcher/PowerlineSymbols.otf | Bin 0 -> 2264 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 powerline/fontpatcher/PowerlineSymbols.otf diff --git a/powerline/fontpatcher/PowerlineSymbols.otf b/powerline/fontpatcher/PowerlineSymbols.otf new file mode 100644 index 0000000000000000000000000000000000000000..b1582afb39a5f302941859c28ddbe652d3be54d9 GIT binary patch literal 2264 zcmd^BeQZ-z6hF7^YdcoDq3ish=^ii=NL=?ioahjP!__PsVU9@@MCjI6+OdAFOPOSB z637O(phI9E4EN!LED!>sjFFK*OpFR*^bbvpf6QbCLPC@TeNWyq_1yN2=t4rmzuuR7 z&+nXj-uZgxzW3JF)vbkW=m0$ktKDw?=8b+4fExnP{o;1lt?|_iZU!)q0)YM1S1R0) z0}kYOBkx$duDB%Ws4WK|Jn~x_f}Y6K#Vj*`;Te?G`bCdd*;0QV_a<>Z=SP9@X;O;# zF3v)KFuvUo2WI~=^7%o}_6TIvAS6%XVhnkL;^OPK-$1?t;~9&Do8zf3;3u;h2zH#LEXJ~3VRjFH`zks|MbWDl^@6lgKRl)`xhx^mI1!+r} zz08W57)KT|1|3K40*C(h=~x!Y3?)J;qv0HsdIWh^*n6A z7{+mv#he*DWHPYA>HE@Wp;5yGOtP9bLBj^fCA&4uAs*MT5%Dd2qQt;r zW3|X&f*uDotOE;tuVFnVdqcwp;7PrPImCxGY()GetcDnf;DI=xfnVZ}$0KFM#Xgk!nAF+qbB4q?-c^GJ6KEm^O&Az6whme$2=yp$LKjdI zPKdFlKuBEI8mtdDp=1pzc~Q9q)nJY24YVMAP%>qh$iwXOm>>@>SPn(F7GVJdq|4CY z{S35Q8(3HAth^vS%k(zA;PSTDw!1K|)a2rj1} z2q<|3FW5z_k22G)by_v+4wTu!fUejXOP@0A`jM$~sT2UBp{X;e)K9Gi^#!kMccCI4 z1mnLCLt6BITv$bMq&^4s&SSOw&mdqh03l@F?5+WP`w&lg#Sm7i<7VN!~m=EU@b?KL**%gc0UK9?XY@c({!^P0_LHApAvB74a`LP?VB zCkG&l#hnj}pc5uYHF=XnNe4M{DPJy?$JA2eB{gA_E2Qt#3X}C-wZX1*%l&pk*kqNw zirXf&o9L$pPxg)+85;Uj4>oyHng93W=;M~$Xj4|n*{jql zi5d?y*yu}#6;YWheX@d5wdzMoZ|#z5C5v+QXG-NN&MMjRr5@wN$jC&2G;NwpjCvi? zQWL$=SecEOLML^$(KC_pa0Csg8p#7aCz(`*OeAJA(X APXGV_ literal 0 HcmV?d00001 From 56f06f887104ee5be0b8e7d488e4ac0c7d99b284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 10:58:23 +0100 Subject: [PATCH 0179/1472] Update font installation instructions Refs #60. --- docs/source/fontpatching.rst | 3 ++ docs/source/overview.rst | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index b91dd8d2..49062b18 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -7,6 +7,9 @@ Powerline provides a font patcher for custom glyphs like the segment dividers (arrows), branch symbol, padlock symbol, etc. The font patcher requires FontForge with Python bindings to work. +You may be able to avoid patching your coding font to use the custom glyphs! +See :ref:`font-installation` for instructions. + Check out the `powerline-fonts `_ repository on GitHub for patched versions of some popular programming fonts. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 64bd3f9e..eefb919d 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -6,6 +6,11 @@ Requirements Powerline requires Python 2.7 to work. +Powerline uses several special glyphs to get the arrow effect and some +custom symbols for developers. This requires that you either have the symbol +font or a patched font on your system. See `Font installation`_ for more +details. + Vim plugin requirements ^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,6 +53,61 @@ they provide an easy way of installing and upgrading Powerline: * `Arch Linux (AUR) `_ +.. _font-installation: + +Font installation +^^^^^^^^^^^^^^^^^ + +Linux +***** + +If you're running Linux, you may be able to avoid patching your coding font +to get the special glyphs required by Powerline. This works by utilizing +fontconfig's fallback font feature, which replaces missing glyphs in a font +with another font on your system. + +This has been tested and works very well with many different coding fonts, +but some fonts may look terrible, in which case you'll have to use a patched +font (see :ref:`font-patching` for details). + +1. Download the `latest version of PowerlineSymbols + `_. +2. Move :file:`PowerlineSymbols.otf` to :file:`~/.fonts`. +3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. +4. Edit your fontconfig file, located in either :file:`~/.fonts.conf` or + :file:`~/.config/fontconfig/fonts.conf`, depending on your fontconfig + version. If your fontconfig file is empty, add the entire code block + below. If you already have a custom font configuration, only add the + ```` block to your font configuration: + + .. code-block:: xml + + + + + + + monospace + + PowerlineSymbols + YOUR MONOSPACE FONT HERE + + + + + Remember to replace ``YOUR MONOSPACE FONT HERE`` with your preferred + coding font. +5. Configure your terminal emulator or gvim to use the ``monospace`` font. +6. If you still don't see the arrow symbols, please close all instances of + your terminal emulator or gvim. You may also have to restart X for the + changes to take effect. + +OS X and Windows +**************** + +You'll have to use a patched font to use the Powerline symbols. See +:ref:`font-patching` for details on font patching and pre-patched fonts. + Usage ----- From 14c4ba45b3b5cba21f4175cf46337bb3ac160a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 11:03:29 +0100 Subject: [PATCH 0180/1472] Move Arch Linux package into separate directory --- {package => packages/archlinux}/.gitignore | 0 {package => packages/archlinux}/PKGBUILD | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {package => packages/archlinux}/.gitignore (100%) rename {package => packages/archlinux}/PKGBUILD (100%) diff --git a/package/.gitignore b/packages/archlinux/.gitignore similarity index 100% rename from package/.gitignore rename to packages/archlinux/.gitignore diff --git a/package/PKGBUILD b/packages/archlinux/PKGBUILD similarity index 100% rename from package/PKGBUILD rename to packages/archlinux/PKGBUILD From 18813c5f066bb0f5a3229288655097cff7bd10d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 11:19:38 +0100 Subject: [PATCH 0181/1472] Install symbol font in Arch Linux package Refs #60. --- packages/archlinux/.gitignore | 1 + packages/archlinux/PKGBUILD | 6 +++++- packages/archlinux/powerline.install | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 packages/archlinux/powerline.install diff --git a/packages/archlinux/.gitignore b/packages/archlinux/.gitignore index e087cfea..4ef5aeeb 100644 --- a/packages/archlinux/.gitignore +++ b/packages/archlinux/.gitignore @@ -1,3 +1,4 @@ * !.gitignore !PKGBUILD +!*.install diff --git a/packages/archlinux/PKGBUILD b/packages/archlinux/PKGBUILD index a550a8da..2e4dab81 100644 --- a/packages/archlinux/PKGBUILD +++ b/packages/archlinux/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=powerline-git -pkgver=20130102 +pkgver=20130118 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -9,6 +9,7 @@ license=('CC BY-SA 3.0') arch=('any') depends=('python2>=2.7') makedepends=('git' 'python2-distribute') +install='powerline.install' source=() _gitroot="https://github.com/Lokaltog/powerline.git" @@ -34,4 +35,7 @@ build() { python2 setup.py build || return 1 python2 setup.py install --root=${pkgdir} || return 1 + + install -d "${pkgdir}/usr/share/fonts/TTF/" + install -m644 "powerline/fontpatcher/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" } diff --git a/packages/archlinux/powerline.install b/packages/archlinux/powerline.install new file mode 100644 index 00000000..3792f4c5 --- /dev/null +++ b/packages/archlinux/powerline.install @@ -0,0 +1,21 @@ +post_install() { + echo "Updating font cache..." + fc-cache -f + + echo " +IMPORTANT +--------- + +Powerline requires custom glyphs to work properly. A font with these symbols +has been installed, and Powerline may work out of the box for you. If it +doesn't, please see the font installation instructions at +http://lokaltog.github.com/powerline/overview.html#font-installation for more +details (you can skip the first steps since the font has already been +installed on your system). +" +} + +post_remove() { + echo "Updating font cache..." + fc-cache -f +} From c1bf60dbcea9029be7e1eb492b570aaa248acf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 16:31:32 +0100 Subject: [PATCH 0182/1472] Move font patcher stuff out of package directory --- .../fontpatcher => font}/PowerlineSymbols.otf | Bin .../fontpatcher => font}/fontpatcher-symbols.sfd | 0 {powerline/fontpatcher => font}/fontpatcher.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {powerline/fontpatcher => font}/PowerlineSymbols.otf (100%) rename {powerline/fontpatcher => font}/fontpatcher-symbols.sfd (100%) rename {powerline/fontpatcher => font}/fontpatcher.py (100%) diff --git a/powerline/fontpatcher/PowerlineSymbols.otf b/font/PowerlineSymbols.otf similarity index 100% rename from powerline/fontpatcher/PowerlineSymbols.otf rename to font/PowerlineSymbols.otf diff --git a/powerline/fontpatcher/fontpatcher-symbols.sfd b/font/fontpatcher-symbols.sfd similarity index 100% rename from powerline/fontpatcher/fontpatcher-symbols.sfd rename to font/fontpatcher-symbols.sfd diff --git a/powerline/fontpatcher/fontpatcher.py b/font/fontpatcher.py similarity index 100% rename from powerline/fontpatcher/fontpatcher.py rename to font/fontpatcher.py From 162b94bf59a895ddaad3e6a095625d32700a69f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 16:32:13 +0100 Subject: [PATCH 0183/1472] Add fontconfig fallback configuration --- font/10-powerline-symbols.conf | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 font/10-powerline-symbols.conf diff --git a/font/10-powerline-symbols.conf b/font/10-powerline-symbols.conf new file mode 100644 index 00000000..7c23e0b7 --- /dev/null +++ b/font/10-powerline-symbols.conf @@ -0,0 +1,69 @@ + + + + + + monospace + PowerlineSymbols + + + Droid Sans Mono + PowerlineSymbols + + + DejaVu Sans Mono + PowerlineSymbols + + + Envy Code R + PowerlineSymbols + + + Inconsolata + PowerlineSymbols + + + Lucida Console + PowerlineSymbols + + + Monaco + PowerlineSymbols + + + Pragmata + PowerlineSymbols + + + PragmataPro + PowerlineSymbols + + + Menlo + PowerlineSymbols + + + Source Code Pro + PowerlineSymbols + + + Consolas + PowerlineSymbols + + + Anonymous pro + PowerlineSymbols + + + Bitstream Vera Sans Mono + PowerlineSymbols + + + Liberation Mono + PowerlineSymbols + + + Ubuntu Mono + PowerlineSymbols + + From 2ace41857f245d535b250cec72c6f31da4ca1a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 16:40:00 +0100 Subject: [PATCH 0184/1472] Update font installation instructions Refs #60. --- docs/source/overview.rst | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index eefb919d..4d4280c1 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -71,36 +71,18 @@ but some fonts may look terrible, in which case you'll have to use a patched font (see :ref:`font-patching` for details). 1. Download the `latest version of PowerlineSymbols - `_. -2. Move :file:`PowerlineSymbols.otf` to :file:`~/.fonts`. + `_ + and the `latest version of the fontconfig file + `_. +2. Move :file:`PowerlineSymbols.otf` to :file:`~/.fonts/`. 3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. -4. Edit your fontconfig file, located in either :file:`~/.fonts.conf` or - :file:`~/.config/fontconfig/fonts.conf`, depending on your fontconfig - version. If your fontconfig file is empty, add the entire code block - below. If you already have a custom font configuration, only add the - ```` block to your font configuration: - - .. code-block:: xml - - - - - - - monospace - - PowerlineSymbols - YOUR MONOSPACE FONT HERE - - - - - Remember to replace ``YOUR MONOSPACE FONT HERE`` with your preferred - coding font. -5. Configure your terminal emulator or gvim to use the ``monospace`` font. -6. If you still don't see the arrow symbols, please close all instances of - your terminal emulator or gvim. You may also have to restart X for the - changes to take effect. +4. Move :file:`10-powerline-symbols.conf` to either :file:`~/.fonts.conf.d/` + or :file:`~/.config/fontconfig/conf.d/`, depending on your fontconfig + version. +5. If you don't see the arrow symbols, please close all instances of your + terminal emulator or gvim. You may also have to restart X for the changes + to take effect. If you *still* don't see the arrow symbols, please submit + an issue on GitHub. OS X and Windows **************** From 1637a1312fd9bb8aec998a33fb76bab8b8aa28ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 16:52:03 +0100 Subject: [PATCH 0185/1472] Improve font installation in Arch Linux package --- packages/archlinux/PKGBUILD | 9 +++++++-- packages/archlinux/powerline.install | 15 +++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/archlinux/PKGBUILD b/packages/archlinux/PKGBUILD index 2e4dab81..6a807bc2 100644 --- a/packages/archlinux/PKGBUILD +++ b/packages/archlinux/PKGBUILD @@ -36,6 +36,11 @@ build() { python2 setup.py build || return 1 python2 setup.py install --root=${pkgdir} || return 1 - install -d "${pkgdir}/usr/share/fonts/TTF/" - install -m644 "powerline/fontpatcher/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" + install -dm755 "${pkgdir}/usr/share/fonts/TTF/" + install -dm755 "${pkgdir}/etc/fonts/conf.avail" + install -dm755 "${pkgdir}/etc/fonts/conf.d" + + install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" + install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" + ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" } diff --git a/packages/archlinux/powerline.install b/packages/archlinux/powerline.install index 3792f4c5..18a2e2a3 100644 --- a/packages/archlinux/powerline.install +++ b/packages/archlinux/powerline.install @@ -6,12 +6,15 @@ post_install() { IMPORTANT --------- -Powerline requires custom glyphs to work properly. A font with these symbols -has been installed, and Powerline may work out of the box for you. If it -doesn't, please see the font installation instructions at -http://lokaltog.github.com/powerline/overview.html#font-installation for more -details (you can skip the first steps since the font has already been -installed on your system). +Powerline requires custom glyphs to work properly. A font with these glyphs has +been installed along with a fontconfig file which enables the glyphs for many +common coding fonts. + +If Powerline doesn't work out of the box on your system, please submit an issue +on GitHub: https://github.com/Lokaltog/powerline/issues + +Consult the documentation for detailed installation instructions and +troubleshooting information: http://lokaltog.github.com/powerline/ " } From c5b37b3913ae87c5223b74261e44a83589482ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 18 Jan 2013 16:53:32 +0100 Subject: [PATCH 0186/1472] Add troubleshooting info for iTerm2 Closes #65. --- docs/source/troubleshooting.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index f08ac131..41bb9745 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -39,6 +39,9 @@ The colors are weird in the default OS X Terminal app! The arrows may have the wrong colors if you have changed the "minimum contrast" slider in the color tab of your OS X settings. +The colors are weird in iTerm2! + Please disable background transparency to resolve this issue. + I'm using tmux and Powerline looks like crap, what's wrong? You need to tell tmux that it has 256-color capabilities. Add this to your :file:`.tmux.conf` to solve this issue:: From 0acfd8437a2cd88d61b4e21e13a7415d051e7535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 19 Jan 2013 17:35:18 +0100 Subject: [PATCH 0187/1472] Change default IP address lookup URI icanhazip.com has two other domains for fine-tuned IP lookup, ipv4.icanhazip.com will always return the IPv4 address, and ipv6.icanhazip.com will always return the IPv6 address (the latter appears to only work if you actually have an IPv6 address). Details at http://rackerhacker.com/icanhazip-com-faq/. Closes #68. --- powerline/ext/tmux/segments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index 730a4706..ab25b1fc 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -24,7 +24,7 @@ def date(format='%Y-%m-%d'): @memoize(600, persistent=True) -def external_ip(query_url='http://icanhazip.com/'): +def external_ip(query_url='http://ipv4.icanhazip.com/'): import urllib2 try: return urllib2.urlopen(query_url).read().strip() From 06d4b4555f9079f062f8697f3678dcd3c628bee4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jan 2013 17:25:37 +0400 Subject: [PATCH 0188/1472] Make VCS guess() function respect .git files Closes #67. --- powerline/lib/vcs/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 16786fe7..ce3a9d96 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -13,11 +13,14 @@ def generate_directories(path): yield path +vcs_props = (('git', '.git', os.path.exists), + ('mercurial', '.hg', os.path.isdir)) + @memoize(100) def guess(path): for directory in generate_directories(path): - for vcs, vcs_dir in (('git', '.git'), ('mercurial', '.hg')): - if os.path.isdir(os.path.join(directory, vcs_dir)): + for vcs, vcs_dir, check in vcs_props: + if check(os.path.join(directory, vcs_dir)): try: if vcs not in globals(): globals()[vcs] = importlib.import_module('powerline.lib.vcs.' + vcs) From 7d668de1692c1f29f94d293ae7ba22e5bcc837bf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jan 2013 16:50:17 +0400 Subject: [PATCH 0189/1472] Raise TypeError only if it is segment type that has problems It raised TypeError also when module did not have required function --- powerline/segments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/segments.py b/powerline/segments.py index e2be730c..d9c1d878 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -30,9 +30,10 @@ class Segments(object): def get(self, segment, side): segment_type = segment.get('type', 'function') try: - contents, contents_func, key = getattr(self, 'get_{0}'.format(segment_type))(segment) + get_segment_info = getattr(self, 'get_{0}'.format(segment_type)) except AttributeError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) + contents, contents_func, key = get_segment_info(segment) highlighting_group = segment.get('highlight', segment.get('name')) return { 'key': key, From 41da1603396a84a4a3df7fe9a5260fc48ed3e192 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jan 2013 16:24:53 +0400 Subject: [PATCH 0190/1472] Add Gentoo live ebuild Still needed to push this ebuild to some overlay or main tree; otherwise it requires more effort to use. Users need to use eselect fontconfig enable 10-powerline-symbols.conf to actually use the fontconfig file (should be added to the docs). Ebuild itself installs the file, but doesn't enable it. --- packages/gentoo/app-misc/powerline/Manifest | 1 + .../app-misc/powerline/powerline-9999.ebuild | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 packages/gentoo/app-misc/powerline/Manifest create mode 100644 packages/gentoo/app-misc/powerline/powerline-9999.ebuild diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest new file mode 100644 index 00000000..f130e016 --- /dev/null +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -0,0 +1 @@ +EBUILD powerline-9999.ebuild 1443 SHA256 e89bb14e6d1e6e99318bbfbbcda46ed69f5589b3e472a37de30a04a1ba72fb49 SHA512 b1da8c33b1886b697d80f9969417a93a2fcdd4e6601d71c35d18f85bc0cbb161981374c10446dc5a9ccf0fd9a244d8389e294f5d6a97f714b401bce1f03ac844 WHIRLPOOL 2b5706b9113deff373adb384e92d42a697338e75b09bb8bdc6339f6d8aaaa4fb8452dac313d8b7e9e3012acc2a694a9cc0266a4b2a232ab7f1f4616af46e8460 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild new file mode 100644 index 00000000..4dd0a00c --- /dev/null +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -0,0 +1,54 @@ +# Copyright 1999-2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/dev-python/setuptools/setuptools-9999.ebuild,v 1.1 2013/01/11 09:59:31 mgorny Exp $ + +EAPI="5" +PYTHON_COMPAT=( python2_7 ) + +#if LIVE +EGIT_REPO_URI="https://github.com/Lokaltog/${PN}" +EGIT_BRANCH="develop" +inherit git +#endif + +inherit distutils-r1 eutils font +DESCRIPTION="The ultimate statusline/prompt utility." +HOMEPAGE="http://github.com/Lokaltog/powerline" +SRC_URI="" + +LICENSE="CC-Attribution-ShareAlike-3.0" +SLOT="0" +KEYWORDS="~alpha ~amd64 arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" +IUSE="vim doc" + +#if LIVE +SRC_URI= +KEYWORDS= +#endif + +S="${WORKDIR}/${PN}" + +RDEPEND="vim? ( || ( app-editors/vim[python] app-editors/gvim[python] ) )" +DEPEND="doc? ( dev-python/sphinx dev-python/docutils )" + +FONT_SUFFIX="otf" +FONT_S="${S}/font" + +FONT_CONF=( + "${FONT_S}/10-powerline-symbols.conf" +) + +src_compile() { + distutils-r1_src_compile + if use doc ; then + einfo "Generating documentation" + sphinx-build -b html docs/source docs_output + fi +} + +src_install() { + unset DOCS + font_src_install + distutils-r1_src_install + use doc && dohtml -r docs_output/* +} From 1557ff2c0354e01f1b153c54b90585d203ed107f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 19 Jan 2013 16:42:29 +0100 Subject: [PATCH 0191/1472] Cleanup weather condition codes --- powerline/ext/tmux/segments.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/tmux/segments.py index ab25b1fc..2703fb20 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/tmux/segments.py @@ -9,10 +9,10 @@ weather_conditions_codes = { u'〇': [25, 34], u'⚑': [24], u'☔': [5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47], - u'☁': range(26, 30) + [44], - u'❅': [7] + range(13, 18) + [41, 42, 43, 46], - u'☈': range(0, 4) + range(37, 39), - u'〰': range(19, 23), + u'☁': [26, 27, 28, 29, 30, 44], + u'❅': [7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46], + u'☈': [0, 1, 2, 3, 4, 37, 38, 39], + u'〰': [19, 20, 21, 22, 23], u'☼': [32, 36], u'☾': [31, 33], } From 78e54e0c840bb4a26e8c820b2f7327d11e313696 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 16 Jan 2013 22:04:43 +0400 Subject: [PATCH 0192/1472] =?UTF-8?q?Addd=20=E2=80=9Cdefault=5Fmodule?= =?UTF-8?q?=E2=80=9D=20theme=20key=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #3. --- docs/source/configuration.rst | 8 +++++++- powerline/core.py | 2 +- powerline/segments.py | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 17cd543e..5dd7419c 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -133,6 +133,11 @@ Themes ``name`` Name of the theme. +``default_module`` + .. _config-themes-default_module: + + Python module where segments will be looked by default. + ``segments`` A dict with a ``left`` and a ``right`` list, consisting of segment dicts. Each segment has the following options: @@ -161,7 +166,8 @@ Themes .. _config-themes-seg-module: Function module, only required for function segments. Defaults to - ``core``. + ``powerline.ext.{extension}.segments``. Default is overriden by + :ref:`theme option `. ``name`` .. _config-themes-seg-name: diff --git a/powerline/core.py b/powerline/core.py index 79514d0b..dec3c16a 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -30,7 +30,7 @@ class Powerline(object): # Load and initialize extension theme theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) path = [os.path.expanduser(path) for path in self.config.get('paths', [])] - get_segment = Segments(ext, path, colorscheme).get + get_segment = Segments(ext, path, colorscheme, theme_config.get('default_module')).get self.get_matcher = Matchers(ext, path).get theme_kwargs = { 'ext': ext, diff --git a/powerline/segments.py b/powerline/segments.py index d9c1d878..e5245958 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -5,15 +5,15 @@ import sys class Segments(object): - def __init__(self, ext, path, colorscheme): - self.ext = ext + def __init__(self, ext, path, colorscheme, default_module=None): + self.default_module = default_module or 'powerline.ext.{0}.segments'.format(ext) self.path = path self.colorscheme = colorscheme def get_function(self, segment): oldpath = sys.path sys.path = self.path + sys.path - segment_module = str(segment.get('module', 'powerline.ext.{0}.segments'.format(self.ext))) + segment_module = str(segment.get('module', self.default_module)) try: return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) finally: From 65e358dee20ea07286ddb13c56d207952b6b7fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 19 Jan 2013 17:01:01 +0100 Subject: [PATCH 0193/1472] Move terminal and tmux segments into common module This should ideally be renamed to something else since it's not strictly an extension. A better module naming scheme could be something like: powerline.segments.ext.{extension} powerline.segments.common This would move all segments out of their respective extension directory, and might be a bit confusing. The ext directories may also need some changes to make this work properly. --- powerline/ext/common/__init__.py | 0 powerline/ext/{tmux => common}/segments.py | 40 ++++++++++++++++++++ powerline/ext/terminal/segments.py | 44 ---------------------- powerline/themes/terminal/default.json | 1 + powerline/themes/tmux/default.json | 2 +- 5 files changed, 42 insertions(+), 45 deletions(-) create mode 100644 powerline/ext/common/__init__.py rename powerline/ext/{tmux => common}/segments.py (79%) delete mode 100644 powerline/ext/terminal/segments.py diff --git a/powerline/ext/common/__init__.py b/powerline/ext/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/ext/tmux/segments.py b/powerline/ext/common/segments.py similarity index 79% rename from powerline/ext/tmux/segments.py rename to powerline/ext/common/segments.py index 2703fb20..85435b06 100644 --- a/powerline/ext/tmux/segments.py +++ b/powerline/ext/common/segments.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- import os +import re +import socket +from powerline.lib.vcs import guess from powerline.lib import memoize # Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes @@ -18,6 +21,43 @@ weather_conditions_codes = { } +def hostname(): + if not os.environ.get('SSH_CLIENT'): + return None + return socket.gethostname() + + +def user(): + user = os.environ.get('USER') + euid = os.geteuid() + return { + 'contents': user, + 'highlight': 'user' if euid != 0 else ['superuser', 'user'], + } + + +def branch(): + repo = guess(os.path.abspath(os.getcwd())) + if repo: + return repo.branch() + return None + + +def cwd(dir_shorten_len=None, dir_limit_depth=None): + cwd = os.getcwdu() + home = os.environ.get('HOME') + if home: + cwd = re.sub('^' + re.escape(home), '~', cwd, 1) + cwd_split = cwd.split(os.sep) + cwd_split_len = len(cwd_split) + if cwd_split_len > dir_limit_depth + 1: + del(cwd_split[0:-dir_limit_depth]) + cwd_split.insert(0, u'⋯') + cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] + cwd = os.path.join(*cwd) + return cwd + + def date(format='%Y-%m-%d'): from datetime import datetime return datetime.now().strftime(format) diff --git a/powerline/ext/terminal/segments.py b/powerline/ext/terminal/segments.py deleted file mode 100644 index 81ffedc5..00000000 --- a/powerline/ext/terminal/segments.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import re -import socket - -from powerline.lib.vcs import guess - - -def hostname(): - if not os.environ.get('SSH_CLIENT'): - return None - return socket.gethostname() - - -def user(): - user = os.environ.get('USER') - euid = os.geteuid() - return { - 'contents': user, - 'highlight': 'user' if euid != 0 else ['superuser', 'user'], - } - - -def branch(): - repo = guess(os.path.abspath(os.getcwd())) - if repo: - return repo.branch() - return None - - -def cwd(dir_shorten_len=None, dir_limit_depth=None): - cwd = os.getcwdu() - home = os.environ.get('HOME') - if home: - cwd = re.sub('^' + re.escape(home), '~', cwd, 1) - cwd_split = cwd.split(os.sep) - cwd_split_len = len(cwd_split) - if cwd_split_len > dir_limit_depth + 1: - del(cwd_split[0:-dir_limit_depth]) - cwd_split.insert(0, u'⋯') - cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] - cwd = os.path.join(*cwd) - return cwd diff --git a/powerline/themes/terminal/default.json b/powerline/themes/terminal/default.json index c5e2879b..be399c6c 100644 --- a/powerline/themes/terminal/default.json +++ b/powerline/themes/terminal/default.json @@ -1,4 +1,5 @@ { + "default_module": "powerline.ext.common.segments", "segments": { "left": [ { diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index 6a468d23..6197db10 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -1,8 +1,8 @@ { + "default_module": "powerline.ext.common.segments", "segments": { "left": [ { - "module": "powerline.ext.terminal.segments", "name": "user" }, { From dfe5d5d829cd1404cd3ec4627eab6f3707f12d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 19 Jan 2013 18:09:20 +0100 Subject: [PATCH 0194/1472] Restructure extension directories Refs #74. --- powerline/{ext => bindings}/__init__.py | 0 .../{ext/vim/bindings.py => bindings/vim/__init__.py} | 7 +++++++ powerline/{ext => bindings}/vim/powerline.vim | 0 powerline/{ext => bindings}/vim/source_plugin.vim | 0 powerline/core.py | 10 +++++----- powerline/ext/terminal/__init__.py | 1 - powerline/ext/tmux/__init__.py | 1 - powerline/ext/vim/__init__.py | 9 --------- powerline/{matchers.py => matcher.py} | 4 ++-- powerline/{ext/common => matchers}/__init__.py | 0 powerline/{ext/vim/matchers.py => matchers/vim.py} | 4 ++++ powerline/renderers/__init__.py | 0 .../terminal/renderer.py => renderers/terminal.py} | 0 powerline/{ext/tmux/renderer.py => renderers/tmux.py} | 0 powerline/{ext/vim/renderer.py => renderers/vim.py} | 4 +++- powerline/{segments.py => segment.py} | 4 ++-- powerline/segments/__init__.py | 0 .../{ext/common/segments.py => segments/common.py} | 0 powerline/{ext/vim/segments.py => segments/vim.py} | 4 +++- powerline/themes/terminal/default.json | 2 +- powerline/themes/tmux/default.json | 2 +- 21 files changed, 28 insertions(+), 24 deletions(-) rename powerline/{ext => bindings}/__init__.py (100%) rename powerline/{ext/vim/bindings.py => bindings/vim/__init__.py} (81%) rename powerline/{ext => bindings}/vim/powerline.vim (100%) rename powerline/{ext => bindings}/vim/source_plugin.vim (100%) delete mode 100644 powerline/ext/terminal/__init__.py delete mode 100644 powerline/ext/tmux/__init__.py delete mode 100644 powerline/ext/vim/__init__.py rename powerline/{matchers.py => matcher.py} (83%) rename powerline/{ext/common => matchers}/__init__.py (100%) rename powerline/{ext/vim/matchers.py => matchers/vim.py} (53%) create mode 100644 powerline/renderers/__init__.py rename powerline/{ext/terminal/renderer.py => renderers/terminal.py} (100%) rename powerline/{ext/tmux/renderer.py => renderers/tmux.py} (100%) rename powerline/{ext/vim/renderer.py => renderers/vim.py} (97%) rename powerline/{segments.py => segment.py} (93%) create mode 100644 powerline/segments/__init__.py rename powerline/{ext/common/segments.py => segments/common.py} (100%) rename powerline/{ext/vim/segments.py => segments/vim.py} (97%) diff --git a/powerline/ext/__init__.py b/powerline/bindings/__init__.py similarity index 100% rename from powerline/ext/__init__.py rename to powerline/bindings/__init__.py diff --git a/powerline/ext/vim/bindings.py b/powerline/bindings/vim/__init__.py similarity index 81% rename from powerline/ext/vim/bindings.py rename to powerline/bindings/vim/__init__.py index 6608372e..f96060dd 100644 --- a/powerline/ext/vim/bindings.py +++ b/powerline/bindings/vim/__init__.py @@ -2,6 +2,13 @@ import vim + +def source_plugin(): + import os + from powerline.bindings.vim import vim_get_func + fnameescape = vim_get_func('fnameescape') + vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim'))) + try: _vim_globals = vim.bindeval('g:') diff --git a/powerline/ext/vim/powerline.vim b/powerline/bindings/vim/powerline.vim similarity index 100% rename from powerline/ext/vim/powerline.vim rename to powerline/bindings/vim/powerline.vim diff --git a/powerline/ext/vim/source_plugin.vim b/powerline/bindings/vim/source_plugin.vim similarity index 100% rename from powerline/ext/vim/source_plugin.vim rename to powerline/bindings/vim/source_plugin.vim diff --git a/powerline/core.py b/powerline/core.py index dec3c16a..844e4c73 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -6,8 +6,8 @@ import os import sys from colorscheme import Colorscheme -from segments import Segments -from matchers import Matchers +from segment import Segment +from matcher import Matcher class Powerline(object): @@ -30,8 +30,8 @@ class Powerline(object): # Load and initialize extension theme theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) path = [os.path.expanduser(path) for path in self.config.get('paths', [])] - get_segment = Segments(ext, path, colorscheme, theme_config.get('default_module')).get - self.get_matcher = Matchers(ext, path).get + get_segment = Segment(ext, path, colorscheme, theme_config.get('default_module')).get + self.get_matcher = Matcher(ext, path).get theme_kwargs = { 'ext': ext, 'colorscheme': colorscheme, @@ -44,7 +44,7 @@ class Powerline(object): local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} # Load and initialize extension renderer - renderer_module_name = 'powerline.ext.{0}.renderer'.format(ext) + renderer_module_name = 'powerline.renderers.{0}'.format(ext) renderer_class_name = '{0}Renderer'.format(ext.capitalize()) Renderer = getattr(importlib.import_module(renderer_module_name), renderer_class_name) self.renderer = Renderer(theme_config, local_themes, theme_kwargs) diff --git a/powerline/ext/terminal/__init__.py b/powerline/ext/terminal/__init__.py deleted file mode 100644 index 5bf82fe6..00000000 --- a/powerline/ext/terminal/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from renderer import TerminalRenderer # NOQA diff --git a/powerline/ext/tmux/__init__.py b/powerline/ext/tmux/__init__.py deleted file mode 100644 index c8e23c53..00000000 --- a/powerline/ext/tmux/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from renderer import TmuxRenderer # NOQA diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py deleted file mode 100644 index a81ac37f..00000000 --- a/powerline/ext/vim/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- - - -def source_plugin(): - import os - import vim - from bindings import vim_get_func - fnameescape = vim_get_func('fnameescape') - vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim'))) diff --git a/powerline/matchers.py b/powerline/matcher.py similarity index 83% rename from powerline/matchers.py rename to powerline/matcher.py index c5f82e19..e61b5615 100644 --- a/powerline/matchers.py +++ b/powerline/matcher.py @@ -4,7 +4,7 @@ from importlib import import_module import sys -class Matchers(object): +class Matcher(object): def __init__(self, ext, path): self.ext = ext self.path = path @@ -12,7 +12,7 @@ class Matchers(object): def get(self, match_name): match_module, separator, match_function = match_name.rpartition('.') if not separator: - match_module = 'powerline.ext.{0}.matchers'.format(self.ext) + match_module = 'powerline.matchers.{0}'.format(self.ext) match_function = match_name oldpath = sys.path sys.path = self.path + sys.path diff --git a/powerline/ext/common/__init__.py b/powerline/matchers/__init__.py similarity index 100% rename from powerline/ext/common/__init__.py rename to powerline/matchers/__init__.py diff --git a/powerline/ext/vim/matchers.py b/powerline/matchers/vim.py similarity index 53% rename from powerline/ext/vim/matchers.py rename to powerline/matchers/vim.py index 4d11c9cd..fbaee480 100644 --- a/powerline/ext/vim/matchers.py +++ b/powerline/matchers/vim.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + import vim diff --git a/powerline/renderers/__init__.py b/powerline/renderers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/ext/terminal/renderer.py b/powerline/renderers/terminal.py similarity index 100% rename from powerline/ext/terminal/renderer.py rename to powerline/renderers/terminal.py diff --git a/powerline/ext/tmux/renderer.py b/powerline/renderers/tmux.py similarity index 100% rename from powerline/ext/tmux/renderer.py rename to powerline/renderers/tmux.py diff --git a/powerline/ext/vim/renderer.py b/powerline/renderers/vim.py similarity index 97% rename from powerline/ext/vim/renderer.py rename to powerline/renderers/vim.py index 7cc5f1bd..93997a21 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/renderers/vim.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -from powerline.ext.vim.bindings import vim_get_func +from __future__ import absolute_import + +from powerline.bindings.vim import vim_get_func from powerline.renderer import Renderer import vim diff --git a/powerline/segments.py b/powerline/segment.py similarity index 93% rename from powerline/segments.py rename to powerline/segment.py index e5245958..c6e50cce 100644 --- a/powerline/segments.py +++ b/powerline/segment.py @@ -4,9 +4,9 @@ from importlib import import_module import sys -class Segments(object): +class Segment(object): def __init__(self, ext, path, colorscheme, default_module=None): - self.default_module = default_module or 'powerline.ext.{0}.segments'.format(ext) + self.default_module = default_module or 'powerline.segments.{0}'.format(ext) self.path = path self.colorscheme = colorscheme diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/ext/common/segments.py b/powerline/segments/common.py similarity index 100% rename from powerline/ext/common/segments.py rename to powerline/segments/common.py diff --git a/powerline/ext/vim/segments.py b/powerline/segments/vim.py similarity index 97% rename from powerline/ext/vim/segments.py rename to powerline/segments/vim.py index 575fb9d6..09f8f068 100644 --- a/powerline/ext/vim/segments.py +++ b/powerline/segments/vim.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + import os import vim -from powerline.ext.vim.bindings import vim_get_func +from powerline.bindings.vim import vim_get_func from powerline.lib import memoize from powerline.lib.vcs import guess diff --git a/powerline/themes/terminal/default.json b/powerline/themes/terminal/default.json index be399c6c..2b1720ad 100644 --- a/powerline/themes/terminal/default.json +++ b/powerline/themes/terminal/default.json @@ -1,5 +1,5 @@ { - "default_module": "powerline.ext.common.segments", + "default_module": "powerline.segments.common", "segments": { "left": [ { diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index 6197db10..77f726ae 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -1,5 +1,5 @@ { - "default_module": "powerline.ext.common.segments", + "default_module": "powerline.segments.common", "segments": { "left": [ { From f108ab04c4c4bea7deca82e4969b4e10cb22f540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:15:12 +0100 Subject: [PATCH 0195/1472] Update pip install URI in the docs --- docs/source/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 4d4280c1..c582b12b 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -34,7 +34,7 @@ Installing with ``pip`` To install Powerline system-wide, run the following command as root:: - pip install https://github.com/Lokaltog/powerline/tarball/develop + pip install git+git://github.com/Lokaltog/powerline If you don't have root access or don't want to install Powerline system-wide, install with ``pip install --user`` instead. From b57174a16cb41ad60abf04dc71c92fb7102c673b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:15:22 +0100 Subject: [PATCH 0196/1472] Remove Ubuntu Mono from fontconfig file Refs #60. --- font/10-powerline-symbols.conf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/font/10-powerline-symbols.conf b/font/10-powerline-symbols.conf index 7c23e0b7..de4cca49 100644 --- a/font/10-powerline-symbols.conf +++ b/font/10-powerline-symbols.conf @@ -62,8 +62,4 @@ Liberation Mono PowerlineSymbols - - Ubuntu Mono - PowerlineSymbols - From 0d097139d62afa05a2af099ea2e83ddd4bb6344d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:16:21 +0100 Subject: [PATCH 0197/1472] Move common segment imports --- powerline/segments/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 85435b06..c8e6e25e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- import os -import re -import socket -from powerline.lib.vcs import guess from powerline.lib import memoize # Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes @@ -22,6 +19,7 @@ weather_conditions_codes = { def hostname(): + import socket if not os.environ.get('SSH_CLIENT'): return None return socket.gethostname() @@ -37,6 +35,7 @@ def user(): def branch(): + from powerline.lib.vcs import guess repo = guess(os.path.abspath(os.getcwd())) if repo: return repo.branch() @@ -44,6 +43,7 @@ def branch(): def cwd(dir_shorten_len=None, dir_limit_depth=None): + import re cwd = os.getcwdu() home = os.environ.get('HOME') if home: From f610527beae377e029a9e99f88738b127655bb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:17:51 +0100 Subject: [PATCH 0198/1472] Move system_load segment code --- powerline/segments/common.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c8e6e25e..5add311d 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -72,22 +72,6 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): return -def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}'): - from multiprocessing import cpu_count - averages = os.getloadavg() - normalized = averages[1] / cpu_count() - if normalized < 1: - gradient = 'system_load_good' - elif normalized < 2: - gradient = 'system_load_bad' - else: - gradient = 'system_load_ugly' - return { - 'contents': format.format(avg=averages), - 'highlight': [gradient, 'system_load'] - } - - def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): # TODO: make this work with operating systems without /proc/uptime try: @@ -130,6 +114,22 @@ def weather(unit='c', location_query=None): return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) +def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}'): + from multiprocessing import cpu_count + averages = os.getloadavg() + normalized = averages[1] / cpu_count() + if normalized < 1: + gradient = 'system_load_good' + elif normalized < 2: + gradient = 'system_load_bad' + else: + gradient = 'system_load_ugly' + return { + 'contents': format.format(avg=averages), + 'highlight': [gradient, 'system_load'] + } + + def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_prefix=False): import time from powerline.lib import humanize_bytes From a8846c0031d4b676377e7864614232d856f22fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:18:07 +0100 Subject: [PATCH 0199/1472] Add cpu_load_percent segment This requires psutil to be installed, should be added to the docs later. --- powerline/colorschemes/tmux/default.json | 1 + powerline/segments/common.py | 9 +++++++++ powerline/themes/tmux/default.json | 3 +++ 3 files changed, 13 insertions(+) diff --git a/powerline/colorschemes/tmux/default.json b/powerline/colorschemes/tmux/default.json index 7c3e853a..9c12fc6e 100644 --- a/powerline/colorschemes/tmux/default.json +++ b/powerline/colorschemes/tmux/default.json @@ -24,6 +24,7 @@ "system_load_good": { "fg": "green", "bg": "darkestblue" }, "system_load_bad": { "fg": "yellow", "bg": "darkestblue" }, "system_load_ugly": { "fg": "red", "bg": "darkestblue" }, + "cpu_load_percent": { "fg": "brightblue", "bg": "darkestblue" }, "uptime": { "fg": "brightblue", "bg": "darkestblue" } } } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5add311d..ffd1a519 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -130,6 +130,15 @@ def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}'): } +def cpu_load_percent(measure_interval=.5): + try: + import psutil + except ImportError: + return None + cpu_percent = int(psutil.cpu_percent(interval=measure_interval)) + return u'{0}%'.format(cpu_percent) + + def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_prefix=False): import time from powerline.lib import humanize_bytes diff --git a/powerline/themes/tmux/default.json b/powerline/themes/tmux/default.json index 77f726ae..25d59de4 100644 --- a/powerline/themes/tmux/default.json +++ b/powerline/themes/tmux/default.json @@ -21,6 +21,9 @@ { "name": "system_load" }, + { + "name": "cpu_load_percent" + }, { "name": "weather" }, From 0586bd059a6b98488b2f7bac7ab6e1cf4ab6356b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:18:55 +0100 Subject: [PATCH 0200/1472] Memoize weather segment for 30 minutes --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ffd1a519..8a2e151e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -85,7 +85,7 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): pass -@memoize(600, persistent=True) +@memoize(1800, persistent=True) def weather(unit='c', location_query=None): import json import urllib From b4fc8ebe915c5126aefa2b7cfc15ca3ead5aa5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:19:16 +0100 Subject: [PATCH 0201/1472] Update network_load segment to work with psutil --- powerline/segments/common.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 8a2e151e..08148bbb 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -145,15 +145,25 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref def get_bytes(): try: - with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: - rx = int(file_obj.read()) - with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: - tx = int(file_obj.read()) - return (rx, tx) - except IOError: - return (0, 0) + import psutil + io_counters = psutil.network_io_counters(pernic=True) + if_io = io_counters.get(interface) + if not if_io: + return None + return (if_io.bytes_recv, if_io.bytes_sent) + except ImportError: + try: + with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: + rx = int(file_obj.read()) + with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: + tx = int(file_obj.read()) + return (rx, tx) + except IOError: + return None b1 = get_bytes() + if b1 is None: + return None time.sleep(measure_interval) b2 = get_bytes() return u'⬇ {rx_diff} ⬆ {tx_diff}'.format( From 033afade95b89eafe2a619993e06438f8805e5ec Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jan 2013 02:15:10 +0400 Subject: [PATCH 0202/1472] Fix highlighting when there are only 2 segments in theme Here are two fixes: - defaultdict makes it not throw KeyErrors. - Replacing (False, False) with False makes it not throw vim.error (no color ctermbg=False) --- powerline/renderers/terminal.py | 4 ++-- powerline/renderers/tmux.py | 4 ++-- powerline/theme.py | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/renderers/terminal.py b/powerline/renderers/terminal.py index 023a73c8..9f3c76de 100644 --- a/powerline/renderers/terminal.py +++ b/powerline/renderers/terminal.py @@ -26,12 +26,12 @@ class TerminalRenderer(Renderer): ''' ansi = [] if fg is not None: - if fg[0] is False: + if fg is False or fg[0] is False: ansi += [39] else: ansi += [38, 5, fg[0]] if bg is not None: - if bg[0] is False: + if bg is False or bg[0] is False: ansi += [49] else: ansi += [48, 5, bg[0]] diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index c0112edb..1b2a93a2 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -12,12 +12,12 @@ class TmuxRenderer(Renderer): return '' tmux_attr = [] if fg is not None: - if fg[0] is False: + if fg is False or fg[0] is False: tmux_attr += ['fg=default'] else: tmux_attr += ['fg=colour' + str(fg[0])] if bg is not None: - if bg[0] is False: + if bg is False or bg[0] is False: tmux_attr += ['bg=default'] else: tmux_attr += ['bg=colour' + str(bg[0])] diff --git a/powerline/theme.py b/powerline/theme.py index 7875b0fe..97811b19 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -2,6 +2,7 @@ import copy +from collections import defaultdict class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config, get_segment): @@ -10,7 +11,7 @@ class Theme(object): self.segments = [] self.EMPTY_SEGMENT = { 'contents': None, - 'highlight': {self.colorscheme.DEFAULT_MODE_KEY: {'fg': (False, False), 'bg': (False, False), 'attr': 0}} + 'highlight': defaultdict(lambda : {'fg': False, 'bg': False, 'attr': 0}) } for side in ['left', 'right']: self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) From 1a7d67f5a76285c1cbd9e199db46562ef2c082a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:30:18 +0100 Subject: [PATCH 0203/1472] =?UTF-8?q?Fix=20=E2=80=9Cdefault=5Fmodule?= =?UTF-8?q?=E2=80=9D=20theme=20key=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts commit 78e54e0c840bb4a26e8c820b2f7327d11e313696 and moves default_module into Segments.__init__. Refs #3. --- powerline/core.py | 7 ++----- powerline/theme.py | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 844e4c73..8df46676 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -6,7 +6,6 @@ import os import sys from colorscheme import Colorscheme -from segment import Segment from matcher import Matcher @@ -29,14 +28,12 @@ class Powerline(object): # Load and initialize extension theme theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) - path = [os.path.expanduser(path) for path in self.config.get('paths', [])] - get_segment = Segment(ext, path, colorscheme, theme_config.get('default_module')).get - self.get_matcher = Matcher(ext, path).get + self.config['paths'] = [os.path.expanduser(path) for path in self.config.get('paths', [])] + self.get_matcher = Matcher(ext, self.config['paths']).get theme_kwargs = { 'ext': ext, 'colorscheme': colorscheme, 'common_config': self.config, - 'get_segment': get_segment, } local_themes = {} for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): diff --git a/powerline/theme.py b/powerline/theme.py index 97811b19..84ea97e2 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -3,9 +3,10 @@ import copy from collections import defaultdict +from segment import Segment class Theme(object): - def __init__(self, ext, colorscheme, theme_config, common_config, get_segment): + def __init__(self, ext, colorscheme, theme_config, common_config): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] @@ -13,6 +14,7 @@ class Theme(object): 'contents': None, 'highlight': defaultdict(lambda : {'fg': False, 'bg': False, 'attr': 0}) } + get_segment = Segment(ext, common_config['paths'], colorscheme, theme_config.get('default_module')).get for side in ['left', 'right']: self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) From 69846cd0cc1594a813071637a91c25ad7817a1f6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jan 2013 02:49:58 +0400 Subject: [PATCH 0204/1472] Move configuration files into separate directory Refs #72. --- powerline/{ => config_files}/colorschemes/terminal/default.json | 0 powerline/{ => config_files}/colorschemes/tmux/default.json | 0 powerline/{ => config_files}/colorschemes/vim/default.json | 0 powerline/{ => config_files}/config.json | 0 powerline/{ => config_files}/themes/terminal/default.json | 0 powerline/{ => config_files}/themes/tmux/default.json | 0 powerline/{ => config_files}/themes/vim/default.json | 0 powerline/{ => config_files}/themes/vim/help.json | 0 powerline/core.py | 2 +- 9 files changed, 1 insertion(+), 1 deletion(-) rename powerline/{ => config_files}/colorschemes/terminal/default.json (100%) rename powerline/{ => config_files}/colorschemes/tmux/default.json (100%) rename powerline/{ => config_files}/colorschemes/vim/default.json (100%) rename powerline/{ => config_files}/config.json (100%) rename powerline/{ => config_files}/themes/terminal/default.json (100%) rename powerline/{ => config_files}/themes/tmux/default.json (100%) rename powerline/{ => config_files}/themes/vim/default.json (100%) rename powerline/{ => config_files}/themes/vim/help.json (100%) diff --git a/powerline/colorschemes/terminal/default.json b/powerline/config_files/colorschemes/terminal/default.json similarity index 100% rename from powerline/colorschemes/terminal/default.json rename to powerline/config_files/colorschemes/terminal/default.json diff --git a/powerline/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json similarity index 100% rename from powerline/colorschemes/tmux/default.json rename to powerline/config_files/colorschemes/tmux/default.json diff --git a/powerline/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json similarity index 100% rename from powerline/colorschemes/vim/default.json rename to powerline/config_files/colorschemes/vim/default.json diff --git a/powerline/config.json b/powerline/config_files/config.json similarity index 100% rename from powerline/config.json rename to powerline/config_files/config.json diff --git a/powerline/themes/terminal/default.json b/powerline/config_files/themes/terminal/default.json similarity index 100% rename from powerline/themes/terminal/default.json rename to powerline/config_files/themes/terminal/default.json diff --git a/powerline/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json similarity index 100% rename from powerline/themes/tmux/default.json rename to powerline/config_files/themes/tmux/default.json diff --git a/powerline/themes/vim/default.json b/powerline/config_files/themes/vim/default.json similarity index 100% rename from powerline/themes/vim/default.json rename to powerline/config_files/themes/vim/default.json diff --git a/powerline/themes/vim/help.json b/powerline/config_files/themes/vim/help.json similarity index 100% rename from powerline/themes/vim/help.json rename to powerline/config_files/themes/vim/help.json diff --git a/powerline/core.py b/powerline/core.py index 8df46676..f6b6c446 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -13,7 +13,7 @@ class Powerline(object): def __init__(self, ext): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) config_path = os.path.join(config_home, 'powerline') - plugin_path = os.path.realpath(os.path.dirname(__file__)) + plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') self.search_paths = [config_path, plugin_path] sys.path[:0] = self.search_paths From f1976c2022dd6befb4983dbffa708f01ed0f3b92 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jan 2013 03:14:31 +0400 Subject: [PATCH 0205/1472] Update docs - Added notes about where common and extension-specific configuration is located - Added common.paths config.json option description - Fixed user-defined segments location found in segments section - Fixed Themes/segments/module reference text --- docs/source/configuration.rst | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 5dd7419c..e6b6e927 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -48,6 +48,9 @@ colorschemes. Common configuration ^^^^^^^^^^^^^^^^^^^^ +Common configuration is a subdictionary that is a value of ``common`` key in +:file:`powerline/config.json` file. + ``dividers`` Defines the dividers used in all Powerline extensions. This option should usually only be changed if you don't have a patched font, or if @@ -57,9 +60,19 @@ Common configuration background colors, while the ``soft`` dividers are used to divide segments with the same background color. +``paths`` + .. _config-common-paths + + Defines additional paths which will be searched for modules when using + :ref:`module segment option `. Paths defined here + have priority when searching for modules. + Extension-specific configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Common configuration is a subdictionary that is a value of ``ext`` key in +:file:`powerline/config.json` file. + ``colorscheme`` Defines the colorscheme used for this extension. @@ -167,7 +180,7 @@ Themes Function module, only required for function segments. Defaults to ``powerline.ext.{extension}.segments``. Default is overriden by - :ref:`theme option `. + :ref:`default_module theme option `. ``name`` .. _config-themes-seg-name: @@ -234,9 +247,9 @@ Segments Segments are written in Python, and the default segments provided with Powerline are located in -:file:`powerline/ext/{extension}/segments/{module}.py`. User-defined -segments can be defined in the corresponding path in the user's config -directory. +:file:`powerline/ext/{extension}/segments/{module}.py`. User-defined segments +can be defined in any module in ``sys.path`` or :ref:`paths common configuration +option `, import is always absolute. Segments are regular Python functions, and they may accept arguments. All arguments should have a default value which will be used for themes that From b7d8495b9c8e0ee0578b9b9c62f9f86b58554c58 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jan 2013 19:24:41 +0400 Subject: [PATCH 0206/1472] Fix problems in mode segment - It was impossible to configure visual block and select block mode strings: JSON strings can contain neither raw control characters nor escape sequences for them - It was impossible to override only some of the strings: missing key generates KeyError exception, not IndexError --- powerline/segments/vim.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 09f8f068..5849a858 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -22,10 +22,10 @@ vim_modes = { 'no': u'N·OPER', 'v': u'VISUAL', 'V': u'V·LINE', - '': u'V·BLCK', + '^V': u'V·BLCK', 's': u'SELECT', 'S': u'S·LINE', - '': u'S·BLCK', + '^S': u'S·BLCK', 'i': u'INSERT', 'R': u'REPLACE', 'Rv': u'V·RPLCE', @@ -38,6 +38,11 @@ vim_modes = { '!': u'SHELL', } +mode_translations = { + chr(ord('V')-0x40): '^V', + chr(ord('S')-0x40): '^S', +} + def mode(override=None): '''Return the current vim mode. @@ -49,11 +54,12 @@ def mode(override=None): mode = mode({ 'n': 'NORM' }) ''' mode = vim_funcs['mode']() + mode = mode_translations.get(mode, mode) if not override: return vim_modes[mode] try: return override[mode] - except IndexError: + except KeyError: return vim_modes[mode] From 84edefbe2e7498937f101fc56794c0a5dc1d74dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:48:56 +0100 Subject: [PATCH 0207/1472] Update docs with new module paths --- docs/source/configuration.rst | 8 ++++---- docs/source/overview.rst | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index e6b6e927..7919256e 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -246,10 +246,10 @@ Segments -------- Segments are written in Python, and the default segments provided with -Powerline are located in -:file:`powerline/ext/{extension}/segments/{module}.py`. User-defined segments -can be defined in any module in ``sys.path`` or :ref:`paths common configuration -option `, import is always absolute. +Powerline are located in :file:`powerline/segments/{extension}.py`. +User-defined segments can be defined in any module in ``sys.path`` or +:ref:`paths common configuration option `, import is +always absolute. Segments are regular Python functions, and they may accept arguments. All arguments should have a default value which will be used for themes that diff --git a/docs/source/overview.rst b/docs/source/overview.rst index c582b12b..98099f29 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -104,7 +104,7 @@ Vim statusline If Powerline is installed as a Python package, you can enable the vim plugin by adding the following line to your ``vimrc``:: - python from powerline.ext.vim import source_plugin; source_plugin() + python from powerline.bindings.vim import source_plugin; source_plugin() If Powerline is installed somewhere other than Python's site-packages directories you'll have to source the vim plugin file with an absolute path @@ -113,7 +113,7 @@ to the plugin location. Add the following line to your ``vimrc``, where ``{path}`` is the path to the main Powerline project directory:: - source {path}/powerline/ext/vim/source_plugin.vim + source {path}/powerline/bindings/vim/source_plugin.vim Terminal prompts ^^^^^^^^^^^^^^^^ From c561922eede2af6818cce23cc21650afd6ef632c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 20 Jan 2013 19:49:06 +0100 Subject: [PATCH 0208/1472] Add info about Gentoo Live ebuild --- docs/source/overview.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 98099f29..6509dfd5 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -52,6 +52,7 @@ The following distribution-specific packages are officially supported, and they provide an easy way of installing and upgrading Powerline: * `Arch Linux (AUR) `_ +* Gentoo Live ebuild (:file:`packages/gentoo/app-misc/powerline/`) .. _font-installation: From 4e97fd95d8b75af792c4691c4a136136f11b8413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Mon, 21 Jan 2013 03:17:04 -0500 Subject: [PATCH 0209/1472] Add Pathogen/Vundle support Closes #79. --- docs/source/overview.rst | 2 +- powerline/bindings/vim/{ => plugin}/source_plugin.vim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename powerline/bindings/vim/{ => plugin}/source_plugin.vim (69%) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 6509dfd5..2d2883e9 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -114,7 +114,7 @@ to the plugin location. Add the following line to your ``vimrc``, where ``{path}`` is the path to the main Powerline project directory:: - source {path}/powerline/bindings/vim/source_plugin.vim + source {path}/powerline/bindings/vim/plugin/source_plugin.vim Terminal prompts ^^^^^^^^^^^^^^^^ diff --git a/powerline/bindings/vim/source_plugin.vim b/powerline/bindings/vim/plugin/source_plugin.vim similarity index 69% rename from powerline/bindings/vim/source_plugin.vim rename to powerline/bindings/vim/plugin/source_plugin.vim index 84be659e..543f9dd8 100644 --- a/powerline/bindings/vim/source_plugin.vim +++ b/powerline/bindings/vim/plugin/source_plugin.vim @@ -6,6 +6,6 @@ if ! has('python') endif python import sys, vim -python sys.path.append(vim.eval('expand(":h:h:h:h")')) +python sys.path.append(vim.eval('expand(":h:h:h:h:h")')) -source :h/powerline.vim +source :h:h/powerline.vim From ccc18e8401baccbacf7ca4d946cbf30bb96f821a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 09:56:43 +0100 Subject: [PATCH 0210/1472] Remove unnecessary import --- powerline/bindings/vim/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index f96060dd..aa2c652f 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -5,7 +5,6 @@ import vim def source_plugin(): import os - from powerline.bindings.vim import vim_get_func fnameescape = vim_get_func('fnameescape') vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim'))) From 7db8aa074f57cc9a73ade3ed4b26ddfbebe1e94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:14:21 +0100 Subject: [PATCH 0211/1472] Update usage docs Refs #79. --- docs/source/overview.rst | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 2d2883e9..bd9a319e 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -102,19 +102,36 @@ Usage Vim statusline ^^^^^^^^^^^^^^ -If Powerline is installed as a Python package, you can enable the vim plugin -by adding the following line to your ``vimrc``:: +Regular installation +******************** - python from powerline.bindings.vim import source_plugin; source_plugin() +**The recommended way of installing Powerline is as a Python package.** +You can then enable the vim plugin by adding the following line to your +``vimrc``: + +.. code-block:: vim + + python from powerline.bindings.vim import source_plugin; source_plugin() If Powerline is installed somewhere other than Python's site-packages -directories you'll have to source the vim plugin file with an absolute path -to the plugin location. +directories you'll either have to use a plugin manager like Vundle, or +source the vim plugin file with an absolute path to the plugin location. Add the following line to your ``vimrc``, where ``{path}`` is the path to -the main Powerline project directory:: +the main Powerline project directory: - source {path}/powerline/bindings/vim/plugin/source_plugin.vim +.. code-block:: vim + + source {path}/powerline/bindings/vim/plugin/source_plugin.vim + +Vundle installation +******************* + +If you're using Vundle you can add the following line to your ``vimrc``: + +.. code-block:: vim + + Bundle 'Lokaltog/powerline', {'rtp': 'powerline/bindings/vim/'} Terminal prompts ^^^^^^^^^^^^^^^^ From 7be62ba56306ea340fb1534974e9c4c2ee7e57a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:16:34 +0100 Subject: [PATCH 0212/1472] Use code-blocks in docs --- docs/source/overview.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index bd9a319e..04ebd6e3 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -139,22 +139,25 @@ Terminal prompts Bash prompt *********** -Add the following to your ``.bashrc``:: +Add the following to your ``.bashrc``: - function _update_ps1() { - export PS1="$(powerline-prompt)" - } +.. code-block:: bash - export PROMPT_COMMAND="_update_ps1" + function _update_ps1() { + export PS1="$(powerline-prompt)" + } + export PROMPT_COMMAND="_update_ps1" Zsh prompt ********** -Add the following to your ``.zshrc``:: +Add the following to your ``.zshrc``: - setopt prompt_subst - export PS1=\`powerline-prompt\` +.. code-block:: bash + + setopt prompt_subst + export PS1=\`powerline-prompt\` Tmux statusline ^^^^^^^^^^^^^^^ From 45769b29075d0b858a18c13face39503cf401298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:31:16 +0100 Subject: [PATCH 0213/1472] Allow overriding the renderer when instantiating Powerline This is useful to allow e.g. the prompt script to use a common shell theme while having different renderers for different shells. --- powerline/core.py | 10 ++++++---- powerline/lib/__init__.py | 9 +++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index f6b6c446..8b2ce088 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -7,10 +7,11 @@ import sys from colorscheme import Colorscheme from matcher import Matcher +from powerline.lib import underscore_to_camelcase class Powerline(object): - def __init__(self, ext): + def __init__(self, ext, renderer_module=None): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) config_path = os.path.join(config_home, 'powerline') plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') @@ -41,9 +42,10 @@ class Powerline(object): local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} # Load and initialize extension renderer - renderer_module_name = 'powerline.renderers.{0}'.format(ext) - renderer_class_name = '{0}Renderer'.format(ext.capitalize()) - Renderer = getattr(importlib.import_module(renderer_module_name), renderer_class_name) + renderer_module_name = renderer_module or ext + renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) + renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) + Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) self.renderer = Renderer(theme_config, local_themes, theme_kwargs) def add_local_theme(self, key, config): diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 8d2de86f..f2f97e2b 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,2 +1,7 @@ -from humanize_bytes import humanize_bytes -from memoize import memoize +from memoize import memoize # NOQA +from humanize_bytes import humanize_bytes # NOQA + + +def underscore_to_camelcase(string): + '''Return a underscore_separated_string as CamelCase.''' + return ''.join(word.capitalize() or '_' for word in string.split('_')) From 0b385a11e0431865729641d3d427034a2b079bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:48:14 +0100 Subject: [PATCH 0214/1472] Rename "terminal" extension to "shell" Refs #84. --- .../{terminal => shell}/default.json | 0 powerline/config_files/config.json | 2 +- .../themes/{terminal => shell}/default.json | 0 powerline/renderers/{terminal.py => shell.py} | 18 +++--------------- 4 files changed, 4 insertions(+), 16 deletions(-) rename powerline/config_files/colorschemes/{terminal => shell}/default.json (100%) rename powerline/config_files/themes/{terminal => shell}/default.json (100%) rename powerline/renderers/{terminal.py => shell.py} (59%) diff --git a/powerline/config_files/colorschemes/terminal/default.json b/powerline/config_files/colorschemes/shell/default.json similarity index 100% rename from powerline/config_files/colorschemes/terminal/default.json rename to powerline/config_files/colorschemes/shell/default.json diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 70c85d46..35b5bb16 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -12,7 +12,7 @@ } }, "ext": { - "terminal": { + "shell": { "colorscheme": "default", "theme": "default" }, diff --git a/powerline/config_files/themes/terminal/default.json b/powerline/config_files/themes/shell/default.json similarity index 100% rename from powerline/config_files/themes/terminal/default.json rename to powerline/config_files/themes/shell/default.json diff --git a/powerline/renderers/terminal.py b/powerline/renderers/shell.py similarity index 59% rename from powerline/renderers/terminal.py rename to powerline/renderers/shell.py index 9f3c76de..e2b63929 100644 --- a/powerline/renderers/terminal.py +++ b/powerline/renderers/shell.py @@ -1,22 +1,10 @@ # -*- coding: utf-8 -*- -import os - from powerline.renderer import Renderer -class TerminalRenderer(Renderer): - '''Powerline terminal segment renderer.''' - _color_templates = { - 'default': '[{code}m', - 'zsh': '%{{[{code}m%}}', - } - - def __init__(self, *args, **kwargs): - super(TerminalRenderer, self).__init__(*args, **kwargs) - shell = os.path.basename(os.environ.get('SHELL')) - self.color_template = self._color_templates.get(shell, self._color_templates['default']) - +class ShellRenderer(Renderer): + '''Powerline shell segment renderer.''' def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -41,4 +29,4 @@ class TerminalRenderer(Renderer): else: if attr & Renderer.ATTR_BOLD: ansi += [1] - return self.color_template.format(code=';'.join(str(attr) for attr in ansi)) + return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) From 2eb07e894e3b10af52fec9c031b51febac5f251e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:49:03 +0100 Subject: [PATCH 0215/1472] Add zsh prompt renderer Zsh can use regular escape sequences in the prompt, but they must be wrapped in %{...%} to work correctly. Refs #84. --- powerline/renderers/zsh_prompt.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 powerline/renderers/zsh_prompt.py diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py new file mode 100644 index 00000000..fe86c1bb --- /dev/null +++ b/powerline/renderers/zsh_prompt.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from powerline.renderers.shell import ShellRenderer + + +class ZshPromptRenderer(ShellRenderer): + '''Powerline zsh prompt segment renderer.''' + def hl(self, fg=None, bg=None, attr=None): + '''Highlight a segment. + + Returns the default ShellRenderer escape sequence with %{...%} wrapped + around it (required in zsh prompts). + ''' + return '%{' + super(ZshPromptRenderer, self).hl(fg, bg, attr) + '%}' From fcb07943ebed0a4f572ad6c62f3c0f3e14f008d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:50:07 +0100 Subject: [PATCH 0216/1472] Allow side, extension and renderer module to be specified in powerline-prompt Refs #84. --- scripts/powerline-prompt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index ab281b05..00322b88 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -1,13 +1,25 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -'''Powerline terminal prompt.''' +'''Powerline prompt script.''' +import argparse + try: from powerline.core import Powerline except ImportError: import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from powerline.core import Powerline + from powerline.core import Powerline # NOQA -pl = Powerline('terminal') -print(pl.renderer.render(None).encode('utf-8')) +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) +parser.add_argument('--renderer_module', default='shell', choices=('shell', 'zsh_prompt')) +parser.add_argument('--ext', default='shell') + +if __name__ == '__main__': + args = parser.parse_args() + pl = Powerline(ext=args.ext, renderer_module=args.renderer_module) + segments = pl.renderer.get_theme().get_segments() + if args.side != 'all': + segments = [s for s in segments if s['side'] == args.side] + print(pl.renderer.render(None, segments=segments).encode('utf-8')) From 25c5a6d978685d83a46e0284e6a5b3101d3af272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 10:53:46 +0100 Subject: [PATCH 0217/1472] Update zsh prompt example in docs --- docs/source/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 04ebd6e3..b059dce5 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -157,7 +157,7 @@ Add the following to your ``.zshrc``: .. code-block:: bash setopt prompt_subst - export PS1=\`powerline-prompt\` + export PS1=\`powerline-prompt --renderer_module=zsh_prompt\` Tmux statusline ^^^^^^^^^^^^^^^ From 4a9ade11b54bfabc1ec1d3765adfa4d451699179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 11:43:34 +0100 Subject: [PATCH 0218/1472] Make uptime tmux segment support psutil psutil is used if available, and the function falls back to reading /proc/uptime if not. According to the psutil docs this should work across more platforms. --- powerline/segments/common.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 08148bbb..eeb01a6c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -73,16 +73,20 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): - # TODO: make this work with operating systems without /proc/uptime try: - with open('/proc/uptime', 'r') as f: - seconds = int(float(f.readline().split()[0])) - minutes, seconds = divmod(seconds, 60) - hours, minutes = divmod(minutes, 60) - days, hours = divmod(hours, 24) - return format.format(days=int(days), hours=hours, minutes=minutes) - except IOError: - pass + import psutil + from datetime import datetime + seconds = (datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).seconds + except ImportError: + try: + with open('/proc/uptime', 'r') as f: + seconds = int(float(f.readline().split()[0])) + except IOError: + return None + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + return format.format(days=int(days), hours=hours, minutes=minutes) @memoize(1800, persistent=True) From 8192d59284a2701bfde43c48079a4f1991b3ddc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 11:46:14 +0100 Subject: [PATCH 0219/1472] Make system load thresholds customizable --- powerline/segments/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index eeb01a6c..e9654dac 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -118,13 +118,13 @@ def weather(unit='c', location_query=None): return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) -def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}'): +def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}', threshold_good=1, threshold_bad=2): from multiprocessing import cpu_count averages = os.getloadavg() normalized = averages[1] / cpu_count() - if normalized < 1: + if normalized < threshold_good: gradient = 'system_load_good' - elif normalized < 2: + elif normalized < threshold_bad: gradient = 'system_load_bad' else: gradient = 'system_load_ugly' From e64ef3c337d73cd9d3b32528c968025b274c9ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 17:30:35 +0100 Subject: [PATCH 0220/1472] Fix zsh prompt example --- docs/source/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index b059dce5..1dca94ac 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -157,7 +157,7 @@ Add the following to your ``.zshrc``: .. code-block:: bash setopt prompt_subst - export PS1=\`powerline-prompt --renderer_module=zsh_prompt\` + export PS1='$(powerline-prompt --renderer_module=zsh_prompt)' Tmux statusline ^^^^^^^^^^^^^^^ From 614c08bee1058e255500f35596c92ed3c8de9861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 17:55:05 +0100 Subject: [PATCH 0221/1472] Convert file name to UTF-8 in vim file name segment Closes #87. --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 5849a858..fba374af 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -86,7 +86,7 @@ def file_directory(): def file_name(display_no_file=False, no_file_text='[No file]'): '''Return file name (tail component of the file path).''' - file_name = vim_funcs['expand']('%:~:.:t') + file_name = vim_funcs['expand']('%:~:.:t').decode('utf-8') if not file_name and not display_no_file: return None if not file_name: From 77f66cbbc9581015dbfb4623b98bb83eb1287b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 20:38:41 +0100 Subject: [PATCH 0222/1472] Add additional dependency info --- docs/source/overview.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 1dca94ac..c04277f6 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -26,6 +26,15 @@ packages. Vim version 7.3.661 or newer is recommended for performance reasons. +Additional dependencies +^^^^^^^^^^^^^^^^^^^^^^^ + +The following Python packages are not required by all segments, but +recommended for optimal performance: + +* ``pygit2`` +* ``psutil`` + Installation ------------ From bee427eb42a7ff2782254c6a2d09a4d351412c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 20:42:57 +0100 Subject: [PATCH 0223/1472] Add Python 3.3 support Support for unicode literals was reintroduced in Python 3.3 which makes supporting both Python 2 and Python 3 much easier, so this will be the minimum supported Python 3 version. Closes #8. --- docs/source/overview.rst | 18 ++++---- .../bindings/vim/plugin/source_plugin.vim | 10 +++-- powerline/bindings/vim/powerline.vim | 16 ++++--- powerline/core.py | 8 ++-- powerline/lib/__init__.py | 4 +- powerline/lib/humanize_bytes.py | 2 +- powerline/lib/memoize.py | 5 ++- powerline/lib/vcs/git.py | 6 +-- powerline/renderer.py | 6 +-- powerline/segments/common.py | 44 ++++++++++++++----- powerline/segments/vim.py | 8 ++-- powerline/theme.py | 10 +++-- scripts/powerline-prompt | 4 +- scripts/powerline-tmux | 6 +-- setup.py | 2 +- 15 files changed, 91 insertions(+), 58 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index c04277f6..c706ef71 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -4,7 +4,7 @@ Overview Requirements ------------ -Powerline requires Python 2.7 to work. +Powerline requires Python 3.3 or Python 2.7 to work. Powerline uses several special glyphs to get the arrow effect and some custom symbols for developers. This requires that you either have the symbol @@ -14,13 +14,14 @@ details. Vim plugin requirements ^^^^^^^^^^^^^^^^^^^^^^^ -The vim plugin requires a vim version with Python 2.7 support compiled in. -You can check if your vim supports Python 2 by running ``vim --version -| grep +python``. +The vim plugin requires a vim version with Python support compiled in. You +can check if your vim supports Python by running ``vim --version | grep ++python``. -If your vim version doesn't have support for Python 2, you'll have to -compile it with the ``--enable-pythoninterp`` flag (this also requires the -Python headers to be installed on your system). Please consult your +If your vim version doesn't have support for Python, you'll have to compile +it with the ``--enable-python3interp`` flag (``--enable-pythoninterp`` if +you want Python 2 support instead). Note that this also requires the related +Python headers to be installed on your system. Please consult your distribution's documentation for details on how to compile and install packages. @@ -48,9 +49,6 @@ To install Powerline system-wide, run the following command as root:: If you don't have root access or don't want to install Powerline system-wide, install with ``pip install --user`` instead. -.. note:: Make sure that you install the package for Python 2. For distros - like Arch Linux you'll have to run ``pip2`` instead of ``pip``. - .. note:: This project is currently unavailable on the PyPI due to a naming conflict with an unrelated project. diff --git a/powerline/bindings/vim/plugin/source_plugin.vim b/powerline/bindings/vim/plugin/source_plugin.vim index 543f9dd8..0d1fe429 100644 --- a/powerline/bindings/vim/plugin/source_plugin.vim +++ b/powerline/bindings/vim/plugin/source_plugin.vim @@ -1,11 +1,13 @@ -if ! has('python') +if ! has('python') && ! has('python3') echohl ErrorMsg - echomsg 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' + echomsg 'You need vim compiled with Python 3.3 or Python 2.7 support for Powerline to work. Please consult the documentation for more details.' echohl None finish endif -python import sys, vim -python sys.path.append(vim.eval('expand(":h:h:h:h:h")')) +let s:pycmd = has('python3') ? 'python3' : 'python' + +exec s:pycmd ' import sys, vim' +exec s:pycmd ' sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' source :h:h/powerline.vim diff --git a/powerline/bindings/vim/powerline.vim b/powerline/bindings/vim/powerline.vim index 1ead0418..502ef837 100644 --- a/powerline/bindings/vim/powerline.vim +++ b/powerline/bindings/vim/powerline.vim @@ -1,13 +1,17 @@ -python import uuid -python from powerline.core import Powerline -python powerline = Powerline('vim') +let s:pycmd = has('python3') ? 'python3' : 'python' -if exists('*pyeval') +exec s:pycmd ' import uuid' +exec s:pycmd ' from powerline.core import Powerline' +exec s:pycmd ' powerline = Powerline("vim")' + +if exists('*py3eval') + let s:pyeval = function('py3eval') +elseif exists('*pyeval') let s:pyeval = function('pyeval') else - python import json, vim + exec s:pycmd ' import json, vim' function! s:pyeval(e) - python vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) + exec s:pycmd ' vim.command("return " + json.dumps(eval(vim.eval("a:e"))))' endfunction endif diff --git a/powerline/core.py b/powerline/core.py index 8b2ce088..ae20f6a1 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -5,8 +5,8 @@ import json import os import sys -from colorscheme import Colorscheme -from matcher import Matcher +from powerline.colorscheme import Colorscheme +from powerline.matcher import Matcher from powerline.lib import underscore_to_camelcase @@ -37,7 +37,7 @@ class Powerline(object): 'common_config': self.config, } local_themes = {} - for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): + for key, local_theme_name in self.config_ext.get('local_themes', {}).items(): key = self.get_matcher(key) local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} @@ -74,6 +74,6 @@ class Powerline(object): 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: + with open(config_file_path, 'r') 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/lib/__init__.py b/powerline/lib/__init__.py index f2f97e2b..903ca183 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,5 +1,5 @@ -from memoize import memoize # NOQA -from humanize_bytes import humanize_bytes # NOQA +from powerline.lib.memoize import memoize # NOQA +from powerline.lib.humanize_bytes import humanize_bytes # NOQA def underscore_to_camelcase(string): diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index 3940e5a8..e9a13479 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from math import log -unit_list = zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2]) +unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2])) def humanize_bytes(num, suffix='B', binary_prefix=False): diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 0bee855d..80552468 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- -import cPickle as pickle +try: + import cPickle as pickle +except ImportError: + import pickle import functools import os import tempfile diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 28607dc9..ab865a4f 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -93,7 +93,7 @@ except ImportError: p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) p.stderr.close() for line in p.stdout: - yield line[:-1] + yield line[:-1].decode('utf-8') class Repository(object): __slots__ = ('directory',) @@ -107,10 +107,10 @@ except ImportError: def status(self, path=None): if path: try: - return self._gitcmd('status', '--porcelain', '--', path).next()[:2] + return next(self._gitcmd('status', '--porcelain', '--', path))[:2] except StopIteration: try: - self._gitcmd('ls-files', '--ignored', '--exclude-standard', '--others', '--', path).next() + next(self._gitcmd('ls-files', '--ignored', '--exclude-standard', '--others', '--', path)) return '!!' except StopIteration: return None diff --git a/powerline/renderer.py b/powerline/renderer.py index 9abe5b8e..24ad1f7a 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from colorscheme import Colorscheme -from theme import Theme +from powerline.colorscheme import Colorscheme +from powerline.theme import Theme class Renderer(object): @@ -20,7 +20,7 @@ class Renderer(object): self.local_themes[matcher] = theme def get_theme(self): - for matcher in self.local_themes.iterkeys(): + for matcher in self.local_themes.keys(): if matcher(): match = self.local_themes[matcher] if 'config' in match: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e9654dac..f22b2d1a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -18,6 +18,31 @@ weather_conditions_codes = { } +def _urllib_read(url): + try: + import urllib.error + import urllib.request + try: + return urllib.request.urlopen(url).read().decode('utf-8') + except urllib.error.HTTPError: + return + except ImportError: + import urllib2 + try: + return urllib2.urlopen(url).read() + except urllib2.HTTPError: + return + + +def _urllib_urlencode(string): + try: + import urllib.parse + return urllib.parse.urlencode(string) + except ImportError: + import urllib + return urllib.urlencode(string) + + def hostname(): import socket if not os.environ.get('SSH_CLIENT'): @@ -44,7 +69,10 @@ def branch(): def cwd(dir_shorten_len=None, dir_limit_depth=None): import re - cwd = os.getcwdu() + try: + cwd = os.getcwdu() + except AttributeError: + cwd = os.getcwd() home = os.environ.get('HOME') if home: cwd = re.sub('^' + re.escape(home), '~', cwd, 1) @@ -65,11 +93,7 @@ def date(format='%Y-%m-%d'): @memoize(600, persistent=True) def external_ip(query_url='http://ipv4.icanhazip.com/'): - import urllib2 - try: - return urllib2.urlopen(query_url).read().strip() - except urllib2.HTTPError: - return + return _urllib_read(query_url).strip() def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): @@ -92,12 +116,10 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): @memoize(1800, persistent=True) def weather(unit='c', location_query=None): import json - import urllib - import urllib2 if not location_query: try: - location = json.loads(urllib2.urlopen('http://freegeoip.net/json/' + external_ip()).read()) + location = json.loads(_urllib_read('http://freegeoip.net/json/' + external_ip())) location_query = ','.join([location['city'], location['region_name'], location['country_name']]) except ValueError: return None @@ -107,8 +129,8 @@ def weather(unit='c', location_query=None): 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit), 'format': 'json' } - url = 'http://query.yahooapis.com/v1/public/yql?' + urllib.urlencode(query_data) - response = json.loads(urllib2.urlopen(url).read()) + url = 'http://query.yahooapis.com/v1/public/yql?' + _urllib_urlencode(query_data) + response = json.loads(_urllib_read(url)) condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) icon = u'〇' diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index fba374af..619813c1 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -39,8 +39,8 @@ vim_modes = { } mode_translations = { - chr(ord('V')-0x40): '^V', - chr(ord('S')-0x40): '^S', + chr(ord('V') - 0x40): '^V', + chr(ord('S') - 0x40): '^S', } @@ -53,7 +53,7 @@ def mode(override=None): mode = mode({ 'n': 'NORM' }) ''' - mode = vim_funcs['mode']() + mode = vim_funcs['mode']().decode('utf-8') mode = mode_translations.get(mode, mode) if not override: return vim_modes[mode] @@ -80,7 +80,7 @@ def readonly_indicator(text=u''): def file_directory(): '''Return file directory (head component of the file path).''' - file_directory = vim_funcs['expand']('%:~:.:h') + file_directory = vim_funcs['expand']('%:~:.:h').decode('utf-8') return file_directory + os.sep if file_directory else None diff --git a/powerline/theme.py b/powerline/theme.py index 84ea97e2..5bca316f 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- +from collections import defaultdict import copy -from collections import defaultdict -from segment import Segment +from .segment import Segment class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config): @@ -42,7 +42,11 @@ class Theme(object): pass else: continue - segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ + try: + contents = unicode(segment['contents']) + except NameError: + contents = str(segment['contents']) + segment['contents'] = (segment['before'] + contents + segment['after'])\ .ljust(segment['ljust'])\ .rjust(segment['rjust']) # We need to yield a copy of the segment, or else mode-dependent diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index 00322b88..d26772e4 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- '''Powerline prompt script.''' import argparse @@ -22,4 +22,4 @@ if __name__ == '__main__': segments = pl.renderer.get_theme().get_segments() if args.side != 'all': segments = [s for s in segments if s['side'] == args.side] - print(pl.renderer.render(None, segments=segments).encode('utf-8')) + print(pl.renderer.render(None, segments=segments)) diff --git a/scripts/powerline-tmux b/scripts/powerline-tmux index 13f5e8c9..4ade5cd1 100755 --- a/scripts/powerline-tmux +++ b/scripts/powerline-tmux @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- '''Powerline tmux statusline.''' import argparse @@ -9,7 +9,7 @@ except ImportError: import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from powerline.core import Powerline + from powerline.core import Powerline # NOQA parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) @@ -21,4 +21,4 @@ if __name__ == '__main__': segments = pl.renderer.get_theme().get_segments() if args.side != 'all': segments = [s for s in segments if s['side'] == args.side] - print(pl.renderer.render('n', segments=segments).encode('utf-8')) + print(pl.renderer.render(None, segments=segments)) diff --git a/setup.py b/setup.py index 3cbf7805..c65bbfa3 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- import os From 85331710bf1740c8598dd65981c2e8b6114ee854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 21:02:30 +0100 Subject: [PATCH 0224/1472] Remove unneeded extras dependency --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index c65bbfa3..15deb0f2 100755 --- a/setup.py +++ b/setup.py @@ -32,7 +32,6 @@ setup(name='Powerline', extras_require={ 'docs': [ 'Sphinx', - 'docutils', ], }, ) From a620a0ef549e31ae2c538beb914101dd36b20248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 21:50:30 +0100 Subject: [PATCH 0225/1472] Fix various Python 3 related issues Powerline now detects the Python version and verifies that the user has Python 3.3 before enabling Python 3 support. To enable Python 3 support, use `python3` to execute source_plugin() in your vimrc instead of `python2`. --- powerline/bindings/vim/__init__.py | 9 ++++++++- .../bindings/vim/plugin/source_plugin.vim | 12 ++++++------ powerline/bindings/vim/powerline.vim | 18 +++++++----------- powerline/theme.py | 14 ++++++++------ 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index aa2c652f..ef46b4ae 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -4,9 +4,16 @@ import vim def source_plugin(): + import sys import os + if sys.version_info[:2] == (3, 3): + vim.command('let g:powerline_pycmd = "python3"') + vim.command('let g:powerline_pyeval = "py3eval"') + else: + vim.command('let g:powerline_pycmd = "python"') + vim.command('let g:powerline_pyeval = "pyeval"') fnameescape = vim_get_func('fnameescape') - vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim'))) + vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim')).decode('utf-8')) try: _vim_globals = vim.bindeval('g:') diff --git a/powerline/bindings/vim/plugin/source_plugin.vim b/powerline/bindings/vim/plugin/source_plugin.vim index 0d1fe429..51c85ea0 100644 --- a/powerline/bindings/vim/plugin/source_plugin.vim +++ b/powerline/bindings/vim/plugin/source_plugin.vim @@ -5,9 +5,9 @@ if ! has('python') && ! has('python3') finish endif -let s:pycmd = has('python3') ? 'python3' : 'python' - -exec s:pycmd ' import sys, vim' -exec s:pycmd ' sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' - -source :h:h/powerline.vim +python <:h:h:h:h:h")')) +from powerline.bindings.vim import source_plugin +source_plugin() +EOF diff --git a/powerline/bindings/vim/powerline.vim b/powerline/bindings/vim/powerline.vim index 502ef837..ade94c7b 100644 --- a/powerline/bindings/vim/powerline.vim +++ b/powerline/bindings/vim/powerline.vim @@ -1,17 +1,13 @@ -let s:pycmd = has('python3') ? 'python3' : 'python' +exec g:powerline_pycmd 'import uuid' +exec g:powerline_pycmd 'from powerline.core import Powerline' +exec g:powerline_pycmd 'powerline = Powerline("vim")' -exec s:pycmd ' import uuid' -exec s:pycmd ' from powerline.core import Powerline' -exec s:pycmd ' powerline = Powerline("vim")' - -if exists('*py3eval') - let s:pyeval = function('py3eval') -elseif exists('*pyeval') - let s:pyeval = function('pyeval') +if exists('*'. g:powerline_pyeval) + let s:pyeval = function(g:powerline_pyeval) else - exec s:pycmd ' import json, vim' + exec g:powerline_pycmd 'import json, vim' function! s:pyeval(e) - exec s:pycmd ' vim.command("return " + json.dumps(eval(vim.eval("a:e"))))' + exec g:powerline_pycmd 'vim.command("return " + json.dumps(eval(vim.eval("a:e"))))' endfunction endif diff --git a/powerline/theme.py b/powerline/theme.py index 5bca316f..7ef45615 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -5,6 +5,12 @@ import copy from .segment import Segment +try: + unicode() +except NameError: + unicode = str + + class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config): self.colorscheme = colorscheme @@ -12,7 +18,7 @@ class Theme(object): self.segments = [] self.EMPTY_SEGMENT = { 'contents': None, - 'highlight': defaultdict(lambda : {'fg': False, 'bg': False, 'attr': 0}) + 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) } get_segment = Segment(ext, common_config['paths'], colorscheme, theme_config.get('default_module')).get for side in ['left', 'right']: @@ -42,11 +48,7 @@ class Theme(object): pass else: continue - try: - contents = unicode(segment['contents']) - except NameError: - contents = str(segment['contents']) - segment['contents'] = (segment['before'] + contents + segment['after'])\ + segment['contents'] = (segment['before'] + unicode(segment['contents']) + segment['after'])\ .ljust(segment['ljust'])\ .rjust(segment['rjust']) # We need to yield a copy of the segment, or else mode-dependent From ced333dd29173bc8fd55beb2bed49737aed37e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 22:03:56 +0100 Subject: [PATCH 0226/1472] Add note about Python 3 support in docs --- docs/source/overview.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index c706ef71..a565969b 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -120,6 +120,10 @@ You can then enable the vim plugin by adding the following line to your python from powerline.bindings.vim import source_plugin; source_plugin() +If you want to enable Python 3 support, substitute the ``python`` command +above with ``python3``. Note that this is somewhat experimental as some +segments don't have support for Python 3 yet. + If Powerline is installed somewhere other than Python's site-packages directories you'll either have to use a plugin manager like Vundle, or source the vim plugin file with an absolute path to the plugin location. From 8355e3d6709fbbdb8fe024cf281431d0c5b82e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 21 Jan 2013 22:09:32 +0100 Subject: [PATCH 0227/1472] Move string decoding of file name/dir vim segments Closes #93. --- powerline/segments/vim.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 619813c1..26d9aded 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -80,13 +80,13 @@ def readonly_indicator(text=u''): def file_directory(): '''Return file directory (head component of the file path).''' - file_directory = vim_funcs['expand']('%:~:.:h').decode('utf-8') - return file_directory + os.sep if file_directory else None + file_directory = vim_funcs['expand']('%:~:.:h') + return file_directory.decode('utf-8') + os.sep if file_directory else None def file_name(display_no_file=False, no_file_text='[No file]'): '''Return file name (tail component of the file path).''' - file_name = vim_funcs['expand']('%:~:.:t').decode('utf-8') + file_name = vim_funcs['expand']('%:~:.:t') if not file_name and not display_no_file: return None if not file_name: @@ -94,7 +94,7 @@ def file_name(display_no_file=False, no_file_text='[No file]'): 'contents': no_file_text, 'highlight': ['file_name_no_file', 'file_name'], } - return file_name + return file_name.decode('utf-8') def file_format(): From 3ee6e55025c293244c9c03c90972da4381175e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 08:04:26 +0100 Subject: [PATCH 0228/1472] Handle Py2/3 Unicode differences when writing to stdout Closes #94. --- scripts/powerline-prompt | 12 ++++++++---- scripts/powerline-tmux | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index d26772e4..50efcfb6 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -2,12 +2,12 @@ # -*- coding: utf-8 -*- '''Powerline prompt script.''' import argparse +import sys try: from powerline.core import Powerline except ImportError: import os - import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from powerline.core import Powerline # NOQA @@ -18,8 +18,12 @@ parser.add_argument('--ext', default='shell') if __name__ == '__main__': args = parser.parse_args() - pl = Powerline(ext=args.ext, renderer_module=args.renderer_module) - segments = pl.renderer.get_theme().get_segments() + powerline = Powerline(ext=args.ext, renderer_module=args.renderer_module) + segments = powerline.renderer.get_theme().get_segments() if args.side != 'all': segments = [s for s in segments if s['side'] == args.side] - print(pl.renderer.render(None, segments=segments)) + rendered = powerline.renderer.render(None, segments=segments) + try: + sys.stdout.write(rendered) + except UnicodeEncodeError: + sys.stdout.write(rendered.encode('utf-8')) diff --git a/scripts/powerline-tmux b/scripts/powerline-tmux index 4ade5cd1..0b9e1377 100755 --- a/scripts/powerline-tmux +++ b/scripts/powerline-tmux @@ -2,12 +2,12 @@ # -*- coding: utf-8 -*- '''Powerline tmux statusline.''' import argparse +import sys try: from powerline.core import Powerline except ImportError: import os - import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from powerline.core import Powerline # NOQA @@ -17,8 +17,12 @@ parser.add_argument('--ext', default='tmux') if __name__ == '__main__': args = parser.parse_args() - pl = Powerline(args.ext) - segments = pl.renderer.get_theme().get_segments() + powerline = Powerline(args.ext) + segments = powerline.renderer.get_theme().get_segments() if args.side != 'all': segments = [s for s in segments if s['side'] == args.side] - print(pl.renderer.render(None, segments=segments)) + rendered = powerline.renderer.render(None, segments=segments) + try: + sys.stdout.write(rendered) + except UnicodeEncodeError: + sys.stdout.write(rendered.encode('utf-8')) From 215981407608dba8ed0eb5eda1829a7a57e0876d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 09:03:34 +0100 Subject: [PATCH 0229/1472] Add side argument in renderer.render() method --- powerline/renderer.py | 4 ++-- powerline/theme.py | 50 +++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 24ad1f7a..f2c2bd8d 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -29,7 +29,7 @@ class Renderer(object): else: return self.theme - def render(self, mode, width=None, theme=None, segments=None): + def render(self, mode, width=None, theme=None, segments=None, side=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -39,7 +39,7 @@ class Renderer(object): reached. ''' theme = theme or self.get_theme() - segments = segments or theme.get_segments() + segments = segments or theme.get_segments(side) # Handle excluded/included segments for the current mode segments = [segment for segment in segments\ diff --git a/powerline/theme.py b/powerline/theme.py index 7ef45615..b0716815 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -15,43 +15,47 @@ class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) - self.segments = [] + self.segments = { + 'left': [], + 'right': [], + } self.EMPTY_SEGMENT = { 'contents': None, 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) } get_segment = Segment(ext, common_config['paths'], colorscheme, theme_config.get('default_module')).get for side in ['left', 'right']: - self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) + self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) def get_divider(self, side='left', type='soft'): '''Return segment divider.''' return self.dividers[side][type] - def get_segments(self): + def get_segments(self, side=None): '''Return all segments. Function segments are called, and all segments get their before/after and ljust/rjust properties applied. ''' - for segment in self.segments: - if segment['type'] == 'function': - contents = segment['contents_func'](**segment['args']) - if contents is None: + for side in [side] if side else ['left', 'right']: + for segment in self.segments[side]: + if segment['type'] == 'function': + contents = segment['contents_func'](**segment['args']) + if contents is None: + continue + try: + segment['highlight'] = self.colorscheme.get_group_highlighting(contents['highlight']) + segment['contents'] = contents['contents'] + except TypeError: + segment['contents'] = contents + elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None): + pass + else: continue - try: - segment['highlight'] = self.colorscheme.get_group_highlighting(contents['highlight']) - segment['contents'] = contents['contents'] - except TypeError: - segment['contents'] = contents - elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None): - pass - else: - continue - segment['contents'] = (segment['before'] + unicode(segment['contents']) + segment['after'])\ - .ljust(segment['ljust'])\ - .rjust(segment['rjust']) - # We need to yield a copy of the segment, or else mode-dependent - # segment contents can't be cached correctly e.g. when caching - # non-current window contents for vim statuslines - yield copy.copy(segment) + segment['contents'] = (segment['before'] + unicode(segment['contents']) + segment['after'])\ + .ljust(segment['ljust'])\ + .rjust(segment['rjust']) + # We need to yield a copy of the segment, or else mode-dependent + # segment contents can't be cached correctly e.g. when caching + # non-current window contents for vim statuslines + yield copy.copy(segment) From 2b0c01482b8bcd509e0cb18b07c33bcee8dc5fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 09:04:57 +0100 Subject: [PATCH 0230/1472] Improve side handling in prompt/tmux scripts --- scripts/powerline-prompt | 7 ++----- scripts/powerline-tmux | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index 50efcfb6..1670d882 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -12,17 +12,14 @@ except ImportError: from powerline.core import Powerline # NOQA parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) +parser.add_argument('side', nargs='?', default=None, choices=('left', 'right')) parser.add_argument('--renderer_module', default='shell', choices=('shell', 'zsh_prompt')) parser.add_argument('--ext', default='shell') if __name__ == '__main__': args = parser.parse_args() powerline = Powerline(ext=args.ext, renderer_module=args.renderer_module) - segments = powerline.renderer.get_theme().get_segments() - if args.side != 'all': - segments = [s for s in segments if s['side'] == args.side] - rendered = powerline.renderer.render(None, segments=segments) + rendered = powerline.renderer.render(None, side=args.side) try: sys.stdout.write(rendered) except UnicodeEncodeError: diff --git a/scripts/powerline-tmux b/scripts/powerline-tmux index 0b9e1377..51e01755 100755 --- a/scripts/powerline-tmux +++ b/scripts/powerline-tmux @@ -12,16 +12,13 @@ except ImportError: from powerline.core import Powerline # NOQA parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) +parser.add_argument('side', nargs='?', default=None, choices=('left', 'right')) parser.add_argument('--ext', default='tmux') if __name__ == '__main__': args = parser.parse_args() powerline = Powerline(args.ext) - segments = powerline.renderer.get_theme().get_segments() - if args.side != 'all': - segments = [s for s in segments if s['side'] == args.side] - rendered = powerline.renderer.render(None, segments=segments) + rendered = powerline.renderer.render(None, side=args.side) try: sys.stdout.write(rendered) except UnicodeEncodeError: From f800872c9e1f28eb3abbee69e86db5fa21e9471b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 09:06:30 +0100 Subject: [PATCH 0231/1472] Default to no mode in renderer.render() method --- powerline/renderer.py | 2 +- scripts/powerline-prompt | 2 +- scripts/powerline-tmux | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index f2c2bd8d..70ce4f1c 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -29,7 +29,7 @@ class Renderer(object): else: return self.theme - def render(self, mode, width=None, theme=None, segments=None, side=None): + def render(self, mode=None, width=None, theme=None, segments=None, side=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index 1670d882..24ccb1da 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -19,7 +19,7 @@ parser.add_argument('--ext', default='shell') if __name__ == '__main__': args = parser.parse_args() powerline = Powerline(ext=args.ext, renderer_module=args.renderer_module) - rendered = powerline.renderer.render(None, side=args.side) + rendered = powerline.renderer.render(side=args.side) try: sys.stdout.write(rendered) except UnicodeEncodeError: diff --git a/scripts/powerline-tmux b/scripts/powerline-tmux index 51e01755..e9dde783 100755 --- a/scripts/powerline-tmux +++ b/scripts/powerline-tmux @@ -18,7 +18,7 @@ parser.add_argument('--ext', default='tmux') if __name__ == '__main__': args = parser.parse_args() powerline = Powerline(args.ext) - rendered = powerline.renderer.render(None, side=args.side) + rendered = powerline.renderer.render(side=args.side) try: sys.stdout.write(rendered) except UnicodeEncodeError: From 973ea572d47641b0440d9e00ad0ede33288c3248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 09:07:22 +0100 Subject: [PATCH 0232/1472] Remove unneeded exclusion from MANIFEST.in --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0e2b40e9..4114082e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1 @@ recursive-include powerline *.json *.vim -exclude powerline/tests/*.xml From 424f9791361ca2898f7774ac9880f2690438b369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 10:58:41 +0100 Subject: [PATCH 0233/1472] Make it possible to return several segment in segment functions This requires a couple of minor changes to custom segments. The segment `highlight` key has been renamed to `highlight_group`, and segment functions must return a list of segments dicts instead of just a dict. Closes #88. --- docs/source/configuration.rst | 20 ++++----- .../config_files/themes/vim/default.json | 7 ++-- powerline/config_files/themes/vim/help.json | 4 +- powerline/renderer.py | 2 +- powerline/segment.py | 5 ++- powerline/segments/common.py | 42 +++++++++++-------- powerline/segments/vim.py | 24 ++++++----- powerline/theme.py | 20 +++++---- 8 files changed, 70 insertions(+), 54 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 7919256e..26fc8b73 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -61,7 +61,7 @@ Common configuration is a subdictionary that is a value of ``common`` key in segments with the same background color. ``paths`` - .. _config-common-paths + .. _config-common-paths: Defines additional paths which will be searched for modules when using :ref:`module segment option `. Paths defined here @@ -166,14 +166,14 @@ Themes ``string`` A static string segment where the contents is defined in the :ref:`contents option `, and the - highlighting group is defined in the :ref:`highlight option - `. + highlighting group is defined in the :ref:`highlight_group + option `. ``filler`` If the statusline is rendered with a specific width, remaining whitespace is distributed among filler segments. The - highlighting group is defined in the :ref:`highlight option - `. + highlighting group is defined in the :ref:`highlight_group + option `. ``module`` .. _config-themes-seg-module: @@ -187,8 +187,8 @@ Themes Function name, only required for function segments. - ``highlight`` - .. _config-themes-seg-highlight: + ``highlight_group`` + .. _config-themes-seg-highlight_group: Highlighting group for this segment. Consists of a prioritized list of highlighting groups, where the first highlighting group that is @@ -259,6 +259,6 @@ A segment function must return one of the following values: * ``None``, which will remove the segment from the prompt/statusline. * A string, which will be the segment contents. -* A dict consisting of a ``contents`` string, and a ``highlight`` list. This - is useful for providing a particular highlighting group depending on the - segment contents. +* A list of dicts consisting of a ``contents`` string, and + a ``highlight_group`` list. This is useful for providing a particular + highlighting group depending on the segment contents. diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 4aa488f7..011b273c 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -32,8 +32,7 @@ }, { "name": "file_vcs_status", - "draw_divider": false, - "before": " " + "draw_divider": false }, { "name": "modified_indicator", @@ -42,7 +41,7 @@ }, { "type": "filler", - "highlight": ["background"] + "highlight_group": ["background"] } ], "right": [ @@ -72,7 +71,7 @@ { "type": "string", "contents": " ", - "highlight": ["line_current_symbol", "line_current"] + "highlight_group": ["line_current_symbol", "line_current"] }, { "name": "line_current", diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index f51a1b87..6c63be03 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -7,7 +7,7 @@ }, { "type": "filler", - "highlight": ["background"] + "highlight_group": ["background"] } ], "right": [ @@ -21,7 +21,7 @@ { "type": "string", "contents": "⭡ ", - "highlight": ["line_current_symbol", "line_current"] + "highlight_group": ["line_current_symbol", "line_current"] }, { "name": "line_current", diff --git a/powerline/renderer.py b/powerline/renderer.py index 70ce4f1c..055b5166 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -89,7 +89,7 @@ class Renderer(object): next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT 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 and segment['side'] == 'right') else '' + outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' divider = theme.get_divider(segment['side'], divider_type) divider_hl = '' diff --git a/powerline/segment.py b/powerline/segment.py index c6e50cce..0c27b816 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -34,11 +34,12 @@ class Segment(object): except AttributeError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) contents, contents_func, key = get_segment_info(segment) - highlighting_group = segment.get('highlight', segment.get('name')) + highlight_group = segment.get('highlight_group', segment.get('name')) return { 'key': key, 'type': segment_type, - 'highlight': self.colorscheme.get_group_highlighting(highlighting_group), + 'highlight_group': highlight_group, + 'highlight': self.colorscheme.get_group_highlighting(highlight_group), 'before': segment.get('before', ''), 'after': segment.get('after', ''), 'contents_func': contents_func, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f22b2d1a..806d9403 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -53,10 +53,10 @@ def hostname(): def user(): user = os.environ.get('USER') euid = os.geteuid() - return { - 'contents': user, - 'highlight': 'user' if euid != 0 else ['superuser', 'user'], - } + return [{ + 'contents': user, + 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], + }] def branch(): @@ -140,20 +140,26 @@ def weather(unit='c', location_query=None): return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) -def system_load(format='{avg[0]:.1f}, {avg[1]:.1f}, {avg[2]:.1f}', threshold_good=1, threshold_bad=2): - from multiprocessing import cpu_count - averages = os.getloadavg() - normalized = averages[1] / cpu_count() - if normalized < threshold_good: - gradient = 'system_load_good' - elif normalized < threshold_bad: - gradient = 'system_load_bad' - else: - gradient = 'system_load_ugly' - return { - 'contents': format.format(avg=averages), - 'highlight': [gradient, 'system_load'] - } +def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): + import multiprocessing + cpu_count = multiprocessing.cpu_count() + ret = [] + for avg in os.getloadavg(): + normalized = avg / cpu_count + if normalized < threshold_good: + hl = 'system_load_good' + elif normalized < threshold_bad: + hl = 'system_load_bad' + else: + hl = 'system_load_ugly' + ret.append({ + 'contents': format.format(avg=avg), + 'highlight_group': [hl, 'system_load'], + 'draw_divider': False, + }) + ret[0]['contents'] += ' ' + ret[1]['contents'] += ' ' + return ret def cpu_load_percent(measure_interval=.5): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 26d9aded..d9177184 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -90,10 +90,10 @@ def file_name(display_no_file=False, no_file_text='[No file]'): if not file_name and not display_no_file: return None if not file_name: - return { + return [{ 'contents': no_file_text, - 'highlight': ['file_name_no_file', 'file_name'], - } + 'highlight_group': ['file_name_no_file', 'file_name'], + }] return file_name.decode('utf-8') @@ -128,10 +128,10 @@ def line_percent(gradient=False): percentage = int(line_current * 100 // line_last) if not gradient: return percentage - return { + return [{ 'contents': percentage, - 'highlight': ['line_percent_gradient' + str(int(5 * percentage // 100) + 1), 'line_percent'], - } + 'highlight_group': ['line_percent_gradient' + str(int(5 * percentage // 100) + 1), 'line_percent'], + }] def line_current(): @@ -166,8 +166,12 @@ def file_vcs_status(): if not status: return None status = status.strip() - return { - 'contents': status, - 'highlight': ['file_vcs_status_' + status, 'file_vcs_status'], - } + ret = [] + for status in status: + ret.append({ + 'contents': status, + 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], + }) + ret[0]['before'] = ' ' + return ret return None diff --git a/powerline/theme.py b/powerline/theme.py index b0716815..d4d14a35 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from collections import defaultdict -import copy +from copy import copy from .segment import Segment @@ -38,24 +38,30 @@ class Theme(object): and ljust/rjust properties applied. ''' for side in [side] if side else ['left', 'right']: + parsed_segments = [] for segment in self.segments[side]: if segment['type'] == 'function': contents = segment['contents_func'](**segment['args']) if contents is None: continue - try: - segment['highlight'] = self.colorscheme.get_group_highlighting(contents['highlight']) - segment['contents'] = contents['contents'] - except TypeError: + if isinstance(contents, list): + for subsegment in contents: + segment_copy = copy(segment) + segment_copy.update(subsegment) + parsed_segments.append(segment_copy) + else: segment['contents'] = contents + parsed_segments.append(segment) elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None): - pass + parsed_segments.append(segment) else: continue + for segment in parsed_segments: + segment['highlight'] = self.colorscheme.get_group_highlighting(segment['highlight_group']) segment['contents'] = (segment['before'] + unicode(segment['contents']) + segment['after'])\ .ljust(segment['ljust'])\ .rjust(segment['rjust']) # We need to yield a copy of the segment, or else mode-dependent # segment contents can't be cached correctly e.g. when caching # non-current window contents for vim statuslines - yield copy.copy(segment) + yield copy(segment) From 3703514ed78ed2a92b1659fe082042b93445ab62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 11:26:33 +0100 Subject: [PATCH 0234/1472] Add option to set soft divider highlighting group Closes #89. --- powerline/renderer.py | 4 +++- powerline/segment.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 055b5166..50490f16 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -96,7 +96,9 @@ class Renderer(object): segment_hl = '' if render_highlighted: - if divider_type == 'hard': + if divider_type == 'soft' and segment['divider_highlight_group'] is not None: + divider_hl = self.hl(segment['divider_highlight'][mode]['fg'], segment['divider_highlight'][mode]['bg'], False) + elif divider_type == 'hard': divider_hl = self.hl(segment['highlight'][mode]['bg'], compare_segment['highlight'][mode]['bg'], False) segment_hl = self.hl(**segment['highlight'][mode]) diff --git a/powerline/segment.py b/powerline/segment.py index 0c27b816..9d634104 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -35,11 +35,14 @@ class Segment(object): raise TypeError('Unknown segment type: {0}'.format(segment_type)) contents, contents_func, key = get_segment_info(segment) highlight_group = segment.get('highlight_group', segment.get('name')) + divider_highlight_group = segment.get('divider_highlight_group') return { 'key': key, 'type': segment_type, 'highlight_group': highlight_group, 'highlight': self.colorscheme.get_group_highlighting(highlight_group), + 'divider_highlight_group': divider_highlight_group, + 'divider_highlight': self.colorscheme.get_group_highlighting(divider_highlight_group) if divider_highlight_group else None, 'before': segment.get('before', ''), 'after': segment.get('after', ''), 'contents_func': contents_func, From 10ed88c576f1e9d680d5db402b299bd9c0c0bba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 11:44:46 +0100 Subject: [PATCH 0235/1472] Update vim soft divider highlighting --- powerline/config_files/colorschemes/vim/default.json | 7 ++++++- powerline/config_files/themes/vim/default.json | 12 ++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 0bdba512..065d7281 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -10,6 +10,7 @@ "brightgreen": 148, "darkestcyan": 23, + "darkcyan": 74, "mediumcyan": 117, "brightcyan": 159, @@ -54,11 +55,13 @@ }, "groups": { "background": { "fg": "white", "bg": "gray2" }, + "background:divider": { "fg": "gray6", "bg": "gray2" }, "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" }, + "branch:divider": { "fg": "gray7", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, "file_name_no_file": { "fg": "gray9", "bg": "gray4", "attr": ["bold"] }, @@ -126,7 +129,9 @@ "gradient6": "mediumcyan" }, "groups": { - "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] } + "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }, + "background:divider": { "fg": "darkcyan", "bg": "darkestblue" }, + "branch:divider": { "fg": "darkcyan", "bg": "darkblue" } } }, "v": { diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 011b273c..dbcfdd15 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -14,7 +14,8 @@ "name": "branch", "exclude_modes": ["nc"], "priority": 60, - "before": " " + "before": " ", + "divider_highlight_group": "branch:divider" }, { "name": "readonly_indicator", @@ -49,17 +50,20 @@ "name": "file_format", "draw_divider": false, "exclude_modes": ["nc"], - "priority": 50 + "priority": 50, + "divider_highlight_group": "background:divider" }, { "name": "file_encoding", "exclude_modes": ["nc"], - "priority": 50 + "priority": 50, + "divider_highlight_group": "background:divider" }, { "name": "file_type", "exclude_modes": ["nc"], - "priority": 50 + "priority": 50, + "divider_highlight_group": "background:divider" }, { "name": "line_percent", From 292d5313e2bf81fe8cb88a0ea769071d5752075a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:17:58 +0100 Subject: [PATCH 0236/1472] Remove expensive functools.wraps from memoize decorator --- powerline/lib/memoize.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 80552468..33be7d7d 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -4,7 +4,6 @@ try: import cPickle as pickle except ImportError: import pickle -import functools import os import tempfile import time @@ -21,7 +20,6 @@ class memoize(object): self.persistent_file = persistent_file or os.path.join(tempfile.gettempdir(), 'powerline-cache') def __call__(self, func): - @functools.wraps(func) def decorated_function(*args, **kwargs): if self.additional_key: key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) From b7e7b1e4f7112271b74137ec4671a925affe727c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:20:26 +0100 Subject: [PATCH 0237/1472] Use absolute import in source_plugin.vim Refs #95. --- powerline/bindings/vim/plugin/source_plugin.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/bindings/vim/plugin/source_plugin.vim b/powerline/bindings/vim/plugin/source_plugin.vim index 51c85ea0..d1edf768 100644 --- a/powerline/bindings/vim/plugin/source_plugin.vim +++ b/powerline/bindings/vim/plugin/source_plugin.vim @@ -6,6 +6,7 @@ if ! has('python') && ! has('python3') endif python <:h:h:h:h:h")')) from powerline.bindings.vim import source_plugin From eb141428c6245c4844d9698907a759a4b1516c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:21:54 +0100 Subject: [PATCH 0238/1472] Return empty string for empty sides --- powerline/renderer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 50490f16..d03b03fe 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -82,7 +82,10 @@ class Renderer(object): ''' rendered_highlighted = u'' segments_len = len(segments) - mode = mode if mode in segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY + try: + mode = mode if mode in segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY + except IndexError: + return '' for index, segment in enumerate(segments): prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT From 30f98f6dc2a9242c626a9cd718555ea1715f7d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:22:31 +0100 Subject: [PATCH 0239/1472] Add exit code/pipe status args in powerline-prompt Refs #90. --- scripts/powerline-prompt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index 24ccb1da..115681c2 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -15,6 +15,8 @@ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('side', nargs='?', default=None, choices=('left', 'right')) parser.add_argument('--renderer_module', default='shell', choices=('shell', 'zsh_prompt')) parser.add_argument('--ext', default='shell') +parser.add_argument('--last_exit_code', default=None, type=int) +parser.add_argument('--last_pipe_status', default=None, type=int) if __name__ == '__main__': args = parser.parse_args() From cfe47adc966010b5a154bedb5e1995c0635ee42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:23:29 +0100 Subject: [PATCH 0240/1472] Move branch segment to right side in shell theme --- powerline/config_files/themes/shell/default.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 2b1720ad..42e7e06c 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -9,16 +9,18 @@ { "name": "user" }, - { - "name": "branch", - "before": " " - }, { "name": "cwd", "args": { "dir_limit_depth": 3 } } + ], + "right": [ + { + "name": "branch", + "before": " " + } ] } } From 487aef3af74bcbd9f3fd84338739e4c4a05187bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:25:01 +0100 Subject: [PATCH 0241/1472] Add zsh prompt script for sourcing in zshrc This script sets a couple of environment variables in tmux if it's running, and provides the last exit code and last pipe status to powerline-prompt. It also traps SIGWINCH and sets the window width as an env variable in tmux. The default prompt has been split in two and the git branch is moved to RPS1. The script must be sourced with an absolute path for now: . /path/to/powerline/bindings/zsh/prompt.zsh Statusline cropping has not been implemented in powerline-tmux yet. Closes #82. Closes #83. Refs #90. --- powerline/bindings/zsh/prompt.zsh | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 powerline/bindings/zsh/prompt.zsh diff --git a/powerline/bindings/zsh/prompt.zsh b/powerline/bindings/zsh/prompt.zsh new file mode 100644 index 00000000..3f3bee9d --- /dev/null +++ b/powerline/bindings/zsh/prompt.zsh @@ -0,0 +1,33 @@ +_powerline_precmd() { + export PS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status=$pipestatus left)" + export RPS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status=$pipestatus right)" + _powerline_tmux_set_pwd +} + +_powerline_tmux_setenv() { + if [[ -n "$TMUX" ]]; then + tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + fi +} + +_powerline_tmux_set_pwd() { + _powerline_tmux_setenv PWD "$PWD" +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "$COLUMNS" +} + +_powerline_install_precmd() { + for f in "${precmd_functions[@]}"; do + if [[ "$f" = "_powerline_precmd" ]]; then + return + fi + done + precmd_functions+=(_powerline_precmd) +} + +trap "_powerline_tmux_set_columns" SIGWINCH +kill -SIGWINCH $$ + +_powerline_install_precmd From 9035ec73f946d6e068157068563b8bb38970f9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:36:40 +0100 Subject: [PATCH 0242/1472] Add left-only shell theme This is useful for bash prompts since bash doesn't support RPS1. This theme must be manually enabled by editing the user config file. Closes #84. --- .../themes/shell/default_leftonly.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 powerline/config_files/themes/shell/default_leftonly.json diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json new file mode 100644 index 00000000..2b1720ad --- /dev/null +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -0,0 +1,24 @@ +{ + "default_module": "powerline.segments.common", + "segments": { + "left": [ + { + "name": "hostname", + "before": " " + }, + { + "name": "user" + }, + { + "name": "branch", + "before": " " + }, + { + "name": "cwd", + "args": { + "dir_limit_depth": 3 + } + } + ] + } +} From a023f4ad10b251de907911e20668685e0b6000a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:45:03 +0100 Subject: [PATCH 0243/1472] Add bash prompt script for sourcing in bashrc This only displays the left part of the prompt. It requires the left-only theme to be able to display all segments. The script must be sourced with an absolute path for now: . /path/to/powerline/bindings/bash/prompt.sh Closes #90. --- powerline/bindings/bash/prompt.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 powerline/bindings/bash/prompt.sh diff --git a/powerline/bindings/bash/prompt.sh b/powerline/bindings/bash/prompt.sh new file mode 100644 index 00000000..c24ca077 --- /dev/null +++ b/powerline/bindings/bash/prompt.sh @@ -0,0 +1,23 @@ +_powerline_prompt_command() { + export PS1="$(powerline-prompt --last_exit_code=$? left)" + _powerline_tmux_set_pwd +} + +_powerline_tmux_setenv() { + if [[ -n "$TMUX" ]]; then + tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + fi +} + +_powerline_tmux_set_pwd() { + _powerline_tmux_setenv PWD "$PWD" +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "$COLUMNS" +} + +trap "_powerline_tmux_set_columns" SIGWINCH +kill -SIGWINCH "$$" + +export PROMPT_COMMAND="_powerline_prompt_command" From c5df2fcb7c3abf521154c5a8a821696bb85e5937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 13:57:33 +0100 Subject: [PATCH 0244/1472] Create awesome looking prompt with divided cwd This is inspired by the look of powerline-shell: https://github.com/milkbikis/powerline-shell --- powerline/config_files/colorschemes/shell/default.json | 8 +++++--- powerline/config_files/themes/shell/default.json | 3 ++- .../config_files/themes/shell/default_leftonly.json | 3 ++- powerline/segments/common.py | 9 +++++++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 5d4ccc2e..7fb4bc64 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -53,10 +53,12 @@ "gradient6": 160 }, "groups": { - "user": { "fg": "brightcyan", "bg": "darkblue", "attr": ["bold"] }, + "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, - "branch": { "fg": "gray9", "bg": "gray4" }, - "cwd": { "fg": "gray10", "bg": "gray4" }, + "branch": { "fg": "gray9", "bg": "gray2" }, + "cwd": { "fg": "gray9", "bg": "gray4" }, + "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, + "cwd:divider": { "fg": "gray7", "bg": "gray4" }, "hostname": { "fg": "brightyellow", "bg": "mediumorange" } } } diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 42e7e06c..24712023 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -13,7 +13,8 @@ "name": "cwd", "args": { "dir_limit_depth": 3 - } + }, + "divider_highlight_group": "cwd:divider" } ], "right": [ diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 2b1720ad..5d7c6fca 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -17,7 +17,8 @@ "name": "cwd", "args": { "dir_limit_depth": 3 - } + }, + "divider_highlight_group": "cwd:divider" } ] } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 806d9403..3aff4532 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -82,8 +82,13 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): del(cwd_split[0:-dir_limit_depth]) cwd_split.insert(0, u'⋯') cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] - cwd = os.path.join(*cwd) - return cwd + ret = [] + for part in cwd: + ret.append({ + 'contents': part, + }) + ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] + return ret def date(format='%Y-%m-%d'): From 35de8a192bb4f603c88aaaededfd64d0a5df9dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 14:09:02 +0100 Subject: [PATCH 0245/1472] Fix common cwd segment for absolute paths --- powerline/segments/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 3aff4532..9a4b770a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -83,7 +83,11 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): cwd_split.insert(0, u'⋯') cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] ret = [] + if not cwd[0]: + cwd[0] = '/' for part in cwd: + if not part: + continue ret.append({ 'contents': part, }) From 504e212f147cc8a82a0e8ada7c40da93535847c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 14:19:04 +0100 Subject: [PATCH 0246/1472] Add common virtualenv segment --- powerline/config_files/colorschemes/shell/default.json | 4 +++- powerline/config_files/themes/shell/default.json | 3 +++ powerline/config_files/themes/shell/default_leftonly.json | 3 +++ powerline/segments/common.py | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 7fb4bc64..6c744f8a 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -1,5 +1,5 @@ { - "name": "Default color scheme for terminal prompts", + "name": "Default color scheme for shell prompts", "colors": { "black": 16, "white": 231, @@ -10,6 +10,7 @@ "brightgreen": 148, "darkestcyan": 23, + "darkcyan": 74, "mediumcyan": 117, "brightcyan": 159, @@ -55,6 +56,7 @@ "groups": { "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, + "virtualenv": { "fg": "white", "bg": "darkcyan" }, "branch": { "fg": "gray9", "bg": "gray2" }, "cwd": { "fg": "gray9", "bg": "gray4" }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 24712023..00d81e28 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -9,6 +9,9 @@ { "name": "user" }, + { + "name": "virtualenv" + }, { "name": "cwd", "args": { diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 5d7c6fca..95d09443 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -9,6 +9,9 @@ { "name": "user" }, + { + "name": "virtualenv" + }, { "name": "branch", "before": " " diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 9a4b770a..8cd43503 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -211,3 +211,7 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, binary_prefix), tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, binary_prefix), ) + + +def virtualenv(): + return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None From 7c19381b24b60e6500e689b427a2bf1718a4f9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 14:23:21 +0100 Subject: [PATCH 0247/1472] Update optional dependency list --- docs/source/overview.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index a565969b..4d8b9331 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -27,13 +27,14 @@ packages. Vim version 7.3.661 or newer is recommended for performance reasons. -Additional dependencies -^^^^^^^^^^^^^^^^^^^^^^^ +Optional dependencies +^^^^^^^^^^^^^^^^^^^^^ The following Python packages are not required by all segments, but recommended for optimal performance: * ``pygit2`` +* ``mercurial`` * ``psutil`` Installation From 96760675ea6f9dd6dae9b068a5848441b6904e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 14:59:07 +0100 Subject: [PATCH 0248/1472] Add "e" symbol before virtualenv prompt segment --- powerline/config_files/themes/shell/default.json | 3 ++- powerline/config_files/themes/shell/default_leftonly.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 00d81e28..135b1908 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -10,7 +10,8 @@ "name": "user" }, { - "name": "virtualenv" + "name": "virtualenv", + "before": "ⓔ " }, { "name": "cwd", diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 95d09443..33e8b7df 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -10,7 +10,8 @@ "name": "user" }, { - "name": "virtualenv" + "name": "virtualenv", + "before": "ⓔ " }, { "name": "branch", From 8f055473acb1ba96374f4a2b430b00136d721e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 16:36:50 +0100 Subject: [PATCH 0249/1472] Update tmux theme and add sourceable config file The tmux theme has been updated and now matches the other themes much better. It may still look a little boring and segment cropping isn't enabled, but it's a good starting point. It colors the weather segment differently based on the current weather and temperature, and some other segment colors have been tweaked. To enable the theme, add the following to your tmux.conf: source '/full/path/to/powerline/bindings/tmux/powerline.conf' Closes #51. --- powerline/bindings/tmux/powerline.conf | 11 ++++ .../colorschemes/tmux/default.json | 53 ++++++++++++++----- .../config_files/themes/shell/default.json | 5 +- .../config_files/themes/tmux/default.json | 44 ++++++++------- powerline/segments/common.py | 21 ++++++-- 5 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 powerline/bindings/tmux/powerline.conf diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf new file mode 100644 index 00000000..03c15d61 --- /dev/null +++ b/powerline/bindings/tmux/powerline.conf @@ -0,0 +1,11 @@ +set -g status on +set -g status-utf8 on +set -g status-interval 2 +set -g status-fg colour231 +set -g status-bg colour234 +set -g status-left-length 20 +set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(powerline-tmux left)' +set -g status-right '#(powerline-tmux right)' +set -g status-right-length 150 +set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[fg=colour249]#W " +set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 9c12fc6e..831747f5 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -10,21 +10,46 @@ "brightblue": 117, "brightestblue": 153, - "green": 148, - "yellow": 220, - "red": 202 + "gray0": 234, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 254, + + "system_load_good": 106, + "system_load_bad": 178, + "system_load_ugly": 202, + + "weather_temp_cold": 67, + "weather_temp_hot": 166, + "weather_condition_cold": 117, + "weather_condition_hot": 228 }, "groups": { - "user": { "fg": "white", "bg": "mediumblue", "attr": ["bold"] }, - "date": { "fg": "white", "bg": "mediumblue" }, - "weather": { "fg": "brightestblue", "bg": "darkblue" }, - "network_load": { "fg": "brightestblue", "bg": "darkblue" }, - "external_ip": { "fg": "brightestblue", "bg": "darkblue" }, - "system_load": { "fg": "brightblue", "bg": "darkestblue" }, - "system_load_good": { "fg": "green", "bg": "darkestblue" }, - "system_load_bad": { "fg": "yellow", "bg": "darkestblue" }, - "system_load_ugly": { "fg": "red", "bg": "darkestblue" }, - "cpu_load_percent": { "fg": "brightblue", "bg": "darkestblue" }, - "uptime": { "fg": "brightblue", "bg": "darkestblue" } + "background:divider": { "fg": "gray5", "bg": "gray0" }, + "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, + "date": { "fg": "gray8", "bg": "gray2" }, + "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, + "time:divider": { "fg": "gray5", "bg": "gray2" }, + "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, + "weather": { "fg": "gray8", "bg": "gray0" }, + "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, + "weather_temp_hot": { "fg": "weather_temp_hot", "bg": "gray0" }, + "weather_condition_☼": { "fg": "weather_condition_hot", "bg": "gray0" }, + "weather_condition_❅": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_condition_☔": { "fg": "weather_condition_cold", "bg": "gray0" }, + "uptime": { "fg": "gray8", "bg": "gray0" }, + "external_ip": { "fg": "gray8", "bg": "gray0" }, + "network_load": { "fg": "gray8", "bg": "gray0" }, + "system_load": { "fg": "gray8", "bg": "gray0" }, + "system_load_good": { "fg": "system_load_good", "bg": "gray0" }, + "system_load_bad": { "fg": "system_load_bad", "bg": "gray0" }, + "system_load_ugly": { "fg": "system_load_ugly", "bg": "gray0" } } } diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 135b1908..877365b6 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -4,7 +4,10 @@ "left": [ { "name": "hostname", - "before": " " + "before": " ", + "args": { + "only_if_ssh": true + } }, { "name": "user" diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 25d59de4..18d818d9 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -1,42 +1,46 @@ { "default_module": "powerline.segments.common", "segments": { - "left": [ + "right": [ { - "name": "user" + "name": "uptime", + "before": "⇑ ", + "priority": 50, + "divider_highlight_group": "background:divider" }, { "name": "external_ip", - "before": "ⓦ " + "before": "ⓦ ", + "priority": 50, + "divider_highlight_group": "background:divider" }, { - "name": "network_load" + "name": "network_load", + "priority": 50, + "divider_highlight_group": "background:divider" }, { - "name": "uptime", - "before": "⇑ " - } - ], - "right": [ - { - "name": "system_load" + "name": "system_load", + "priority": 50, + "divider_highlight_group": "background:divider" }, { - "name": "cpu_load_percent" - }, - { - "name": "weather" - }, - { - "name": "date", - "args": {"format": "%a"} + "name": "weather", + "priority": 50, + "divider_highlight_group": "background:divider" }, { "name": "date" }, { "name": "date", - "args": {"format": "%H:%M"} + "args": {"format": "%H:%M"}, + "before": "⌚ ", + "highlight_group": ["time", "date"], + "divider_highlight_group": "time:divider" + }, + { + "name": "hostname" } ] } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 8cd43503..4808887b 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -43,9 +43,9 @@ def _urllib_urlencode(string): return urllib.urlencode(string) -def hostname(): +def hostname(only_if_ssh=False): import socket - if not os.environ.get('SSH_CLIENT'): + if only_if_ssh and not os.environ.get('SSH_CLIENT'): return None return socket.gethostname() @@ -146,7 +146,17 @@ def weather(unit='c', location_query=None): for icon, codes in weather_conditions_codes.items(): if condition_code in codes: break - return u'{0} {1}°{2}'.format(icon, condition['temp'], unit.upper()) + return [ + { + 'contents': icon + ' ', + 'highlight_group': ['weather_condition_' + icon, 'weather_condition', 'weather'], + }, + { + 'contents': '{0}°{1}'.format(condition['temp'], unit.upper()), + 'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], + 'draw_divider': False, + }, + ] def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): @@ -166,6 +176,7 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): 'highlight_group': [hl, 'system_load'], 'draw_divider': False, }) + ret[0]['draw_divider'] = True ret[0]['contents'] += ' ' ret[1]['contents'] += ' ' return ret @@ -208,8 +219,8 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref time.sleep(measure_interval) b2 = get_bytes() return u'⬇ {rx_diff} ⬆ {tx_diff}'.format( - rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, binary_prefix), - tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, binary_prefix), + rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, binary_prefix).rjust(8), + tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, binary_prefix).rjust(8), ) From f175a6babbaf718f4b568f40edc179f104903eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 16:45:24 +0100 Subject: [PATCH 0250/1472] Fix issue with $pipestatus $pipestatus is a space-separated int list and not a single int. --- powerline/bindings/zsh/prompt.zsh | 4 ++-- scripts/powerline-prompt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/zsh/prompt.zsh b/powerline/bindings/zsh/prompt.zsh index 3f3bee9d..b3bf9c4d 100644 --- a/powerline/bindings/zsh/prompt.zsh +++ b/powerline/bindings/zsh/prompt.zsh @@ -1,6 +1,6 @@ _powerline_precmd() { - export PS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status=$pipestatus left)" - export RPS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status=$pipestatus right)" + export PS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" left)" + export RPS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" right)" _powerline_tmux_set_pwd } diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index 115681c2..6f125628 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -16,7 +16,7 @@ parser.add_argument('side', nargs='?', default=None, choices=('left', 'right')) parser.add_argument('--renderer_module', default='shell', choices=('shell', 'zsh_prompt')) parser.add_argument('--ext', default='shell') parser.add_argument('--last_exit_code', default=None, type=int) -parser.add_argument('--last_pipe_status', default=None, type=int) +parser.add_argument('--last_pipe_status', default=None) if __name__ == '__main__': args = parser.parse_args() From 6bc13b8d60bfbcdf18ee7f05c9cface86dff15e3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 22 Jan 2013 19:41:28 +0400 Subject: [PATCH 0251/1472] Make g:powerline_* variables fixed*, added g:powerline_loaded The latter is required to be able to forbid sourcing powerline.vim * changes are ignored until script is resourced --- powerline/bindings/vim/powerline.vim | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/vim/powerline.vim b/powerline/bindings/vim/powerline.vim index ade94c7b..a180e520 100644 --- a/powerline/bindings/vim/powerline.vim +++ b/powerline/bindings/vim/powerline.vim @@ -1,13 +1,21 @@ -exec g:powerline_pycmd 'import uuid' -exec g:powerline_pycmd 'from powerline.core import Powerline' -exec g:powerline_pycmd 'powerline = Powerline("vim")' +if exists('g:powerline_loaded') + finish +endif +let g:powerline_loaded = 1 -if exists('*'. g:powerline_pyeval) - let s:pyeval = function(g:powerline_pyeval) +let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', 'py'), '\v^(py)%[thon](3?)$', '\1\2', '') +let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') + +exec s:powerline_pycmd 'import uuid' +exec s:powerline_pycmd 'from powerline.core import Powerline' +exec s:powerline_pycmd 'powerline = Powerline("vim")' + +if exists('*'. s:powerline_pyeval) + let s:pyeval = function(s:powerline_pyeval) else - exec g:powerline_pycmd 'import json, vim' + exec s:powerline_pycmd 'import json, vim' function! s:pyeval(e) - exec g:powerline_pycmd 'vim.command("return " + json.dumps(eval(vim.eval("a:e"))))' + exec s:powerline_pycmd 'vim.command("return " + json.dumps(eval(vim.eval("a:e"))))' endfunction endif From 0d39dd55071ed074be3ad35fe52c6691db51f6b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 22 Jan 2013 19:45:56 +0400 Subject: [PATCH 0252/1472] Add installation of powerline.vim into /usr/share/vim/vimfiles/plugin --- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index f130e016..e5f1e37c 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 1443 SHA256 e89bb14e6d1e6e99318bbfbbcda46ed69f5589b3e472a37de30a04a1ba72fb49 SHA512 b1da8c33b1886b697d80f9969417a93a2fcdd4e6601d71c35d18f85bc0cbb161981374c10446dc5a9ccf0fd9a244d8389e294f5d6a97f714b401bce1f03ac844 WHIRLPOOL 2b5706b9113deff373adb384e92d42a697338e75b09bb8bdc6339f6d8aaaa4fb8452dac313d8b7e9e3012acc2a694a9cc0266a4b2a232ab7f1f4616af46e8460 +EBUILD powerline-9999.ebuild 1552 SHA256 21e42a5d4d67b08855f0c1cd04628f32e0b4136676f1b2a2798d5ee527106c09 SHA512 9eeb0393006a394f99b8bfa4540a6bc24bb5e63cee8371ac9507eff427fc03f020e74c3a9ed3a6ab8ae2635fdf02b6f7a5c54709a8c6f8bde0a6de3ae4c135d6 WHIRLPOOL a47bc5f1f420d5b92d4dc5dc5873cc8ae34d39457fb7b48050463e7620e886ab247d623e98857218ceba6a665b40058a02745579bd790f396154907c9f61c90b diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 4dd0a00c..9fe27289 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -51,4 +51,8 @@ src_install() { font_src_install distutils-r1_src_install use doc && dohtml -r docs_output/* + if use vim ; then + insinto /usr/share/vim/vimfiles/plugin + doins powerline/bindings/vim/powerline.vim + fi } From 51c302a6ca7b5200ee23c3c6ab15f1f1c8f6a904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 17:14:45 +0100 Subject: [PATCH 0253/1472] Revert to previous way of sourcing vim plugin Refs #95. --- powerline/bindings/vim/plugin/source_plugin.vim | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/vim/plugin/source_plugin.vim b/powerline/bindings/vim/plugin/source_plugin.vim index d1edf768..ba96920e 100644 --- a/powerline/bindings/vim/plugin/source_plugin.vim +++ b/powerline/bindings/vim/plugin/source_plugin.vim @@ -5,10 +5,7 @@ if ! has('python') && ! has('python3') finish endif -python <:h:h:h:h:h")')) -from powerline.bindings.vim import source_plugin -source_plugin() -EOF +python import sys, vim +python sys.path.append(vim.eval('expand(":h:h:h:h:h")')) + +source :h:h/powerline.vim From c818e7c611b9c86622026274bfe1c55a222a9bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 17:20:58 +0100 Subject: [PATCH 0254/1472] Install source_plugin.vim in Arch Linux PKGBUILD --- packages/archlinux/PKGBUILD | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/archlinux/PKGBUILD b/packages/archlinux/PKGBUILD index 6a807bc2..8931a9f0 100644 --- a/packages/archlinux/PKGBUILD +++ b/packages/archlinux/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=powerline-git -pkgver=20130118 +pkgver=20130122 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -39,8 +39,10 @@ build() { install -dm755 "${pkgdir}/usr/share/fonts/TTF/" install -dm755 "${pkgdir}/etc/fonts/conf.avail" install -dm755 "${pkgdir}/etc/fonts/conf.d" + install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" + install -m644 "powerline/bindings/vim/plugin/source_plugin.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" } From 0cb218212ec42a6dc2007e2aad0891794a39eff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 17:26:01 +0100 Subject: [PATCH 0255/1472] Update installation instructions for tmux and shell prompts --- docs/source/overview.rst | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 4d8b9331..6c48451d 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -145,44 +145,33 @@ If you're using Vundle you can add the following line to your ``vimrc``: Bundle 'Lokaltog/powerline', {'rtp': 'powerline/bindings/vim/'} -Terminal prompts -^^^^^^^^^^^^^^^^ +Shell prompts +^^^^^^^^^^^^^ Bash prompt *********** -Add the following to your ``.bashrc``: +Add the following line to your ``.bashrc``, where ``{path}`` is the absolute +path to your Powerline installation directory: .. code-block:: bash - function _update_ps1() { - export PS1="$(powerline-prompt)" - } - - export PROMPT_COMMAND="_update_ps1" + . {path}/powerline/bindings/bash/prompt.sh Zsh prompt ********** -Add the following to your ``.zshrc``: +Add the following line to your ``.zshrc``, where ``{path}`` is the absolute +path to your Powerline installation directory: .. code-block:: bash - setopt prompt_subst - export PS1='$(powerline-prompt --renderer_module=zsh_prompt)' + . {path}/powerline/bindings/zsh/prompt.zsh Tmux statusline ^^^^^^^^^^^^^^^ -Add the following to your ``tmux.conf``:: +Add the following line to your ``.tmux.conf``, where ``{path}`` is the +absolute path to your Powerline installation directory:: - set-option -g status on - set-option -g status-interval 2 - set-option -g status-utf8 on - set-option -g status-left-length 100 - set-option -g status-left "#(powerline-tmux left)" - set-option -g status-right-length 100 - set-option -g status-right "#(powerline-tmux right)" - set-option -g status-justify "centre" - set-option -g status-bg "colour235" - set-option -g status-fg "colour249" + source '{path}/powerline/bindings/tmux/powerline.conf' From f4a95fbbbfb639d660c663144ef52be0817906a7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 22 Jan 2013 20:51:26 +0400 Subject: [PATCH 0256/1472] Rename prompt.* to powerline.* Reasons: - prompt.* is a bad name for installing into /usr/share/zsh/site-contrib - prompt.* does not conform powerline.vim and powerline.conf (vim and tmux) --- docs/source/overview.rst | 4 ++-- powerline/bindings/bash/{prompt.sh => powerline.sh} | 0 powerline/bindings/zsh/{prompt.zsh => powerline.zsh} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename powerline/bindings/bash/{prompt.sh => powerline.sh} (100%) rename powerline/bindings/zsh/{prompt.zsh => powerline.zsh} (100%) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 6c48451d..59dc0145 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -156,7 +156,7 @@ path to your Powerline installation directory: .. code-block:: bash - . {path}/powerline/bindings/bash/prompt.sh + . {path}/powerline/bindings/bash/powerline.sh Zsh prompt ********** @@ -166,7 +166,7 @@ path to your Powerline installation directory: .. code-block:: bash - . {path}/powerline/bindings/zsh/prompt.zsh + . {path}/powerline/bindings/zsh/powerline.zsh Tmux statusline ^^^^^^^^^^^^^^^ diff --git a/powerline/bindings/bash/prompt.sh b/powerline/bindings/bash/powerline.sh similarity index 100% rename from powerline/bindings/bash/prompt.sh rename to powerline/bindings/bash/powerline.sh diff --git a/powerline/bindings/zsh/prompt.zsh b/powerline/bindings/zsh/powerline.zsh similarity index 100% rename from powerline/bindings/zsh/prompt.zsh rename to powerline/bindings/zsh/powerline.zsh From a462f1e82b17489e5f6e2381e97f74fb7105fcad Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 22 Jan 2013 20:59:21 +0400 Subject: [PATCH 0257/1472] Add zsh USE flag and information about Python 3.3 compatibility --- packages/gentoo/app-misc/powerline/Manifest | 2 +- .../app-misc/powerline/powerline-9999.ebuild | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index e5f1e37c..d032d1ee 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 1552 SHA256 21e42a5d4d67b08855f0c1cd04628f32e0b4136676f1b2a2798d5ee527106c09 SHA512 9eeb0393006a394f99b8bfa4540a6bc24bb5e63cee8371ac9507eff427fc03f020e74c3a9ed3a6ab8ae2635fdf02b6f7a5c54709a8c6f8bde0a6de3ae4c135d6 WHIRLPOOL a47bc5f1f420d5b92d4dc5dc5873cc8ae34d39457fb7b48050463e7620e886ab247d623e98857218ceba6a665b40058a02745579bd790f396154907c9f61c90b +EBUILD powerline-9999.ebuild 1876 SHA256 0d9d6e11e42ea59e83f8b862a7cc40912dca0754cd35391da5eed55a2635db67 SHA512 cf9c3b85e503be41e4a1780924e622c316cb1c84e9ada93f5130672f6e3cdfeea7b94f13c71a6d8689ebf9aaa9aa37df11f6d456d81b3ef9aaa3def6bd31b8bf WHIRLPOOL fdf09d93c0cd5acb41e964edf25b21dae77f3ca8a1a4c017913c17b474dcd1a1581358162e0c98d647c929064d0c269a2e40719c16e7001a38fe6659af57e264 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 9fe27289..faf8e561 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -3,7 +3,7 @@ # $Header: /var/cvsroot/gentoo-x86/dev-python/setuptools/setuptools-9999.ebuild,v 1.1 2013/01/11 09:59:31 mgorny Exp $ EAPI="5" -PYTHON_COMPAT=( python2_7 ) +PYTHON_COMPAT=( python{2_7,3_3} ) #if LIVE EGIT_REPO_URI="https://github.com/Lokaltog/${PN}" @@ -19,7 +19,7 @@ SRC_URI="" LICENSE="CC-Attribution-ShareAlike-3.0" SLOT="0" KEYWORDS="~alpha ~amd64 arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" -IUSE="vim doc" +IUSE="vim zsh doc" #if LIVE SRC_URI= @@ -49,10 +49,19 @@ src_compile() { src_install() { unset DOCS font_src_install - distutils-r1_src_install - use doc && dohtml -r docs_output/* if use vim ; then insinto /usr/share/vim/vimfiles/plugin doins powerline/bindings/vim/powerline.vim fi + # rm powerline/bindings/vim/powerline.vim + if use zsh ; then + insinto /usr/share/zsh/site-contrib + doins powerline/bindings/zsh/powerline.zsh + einfo "To enable powerline prompt add" + einfo " . /usr/share/zsh/site-contrib/powerline.zsh" + einfo "to your zshrc." + fi + rm powerline/bindings/zsh/powerline.zsh + distutils-r1_src_install + use doc && dohtml -r docs_output/* } From 772372786efa05dfc13ba971d278d2f7ab5b9c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 18:24:19 +0100 Subject: [PATCH 0258/1472] Update Arch Linux PKGBUILD --- packages/archlinux/PKGBUILD | 20 +++++++++++++++++--- packages/archlinux/powerline.install | 21 ++++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/archlinux/PKGBUILD b/packages/archlinux/PKGBUILD index 8931a9f0..1521e7bc 100644 --- a/packages/archlinux/PKGBUILD +++ b/packages/archlinux/PKGBUILD @@ -9,6 +9,10 @@ license=('CC BY-SA 3.0') arch=('any') depends=('python2>=2.7') makedepends=('git' 'python2-distribute') +optdepends=('python2-psutil: improved system information' + 'python2-pygit2: improved git support' + 'mercurial: improved mercurial support' + 'zsh: better shell prompt') install='powerline.install' source=() @@ -36,13 +40,23 @@ build() { python2 setup.py build || return 1 python2 setup.py install --root=${pkgdir} || return 1 + msg2 "Installing fonts..." install -dm755 "${pkgdir}/usr/share/fonts/TTF/" install -dm755 "${pkgdir}/etc/fonts/conf.avail" install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" - install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - install -m644 "powerline/bindings/vim/plugin/source_plugin.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + + msg2 "Installing vim plugin..." + install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" + install -m644 "powerline/bindings/vim/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + + msg2 "Installing zsh plugin..." + install -dm755 "${pkgdir}/usr/share/zsh/site-contrib" + install -m644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" + + msg2 "Installing tmux configuration..." + install -dm755 "${pkgdir}/usr/share/tmux" + install -m644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" } diff --git a/packages/archlinux/powerline.install b/packages/archlinux/powerline.install index 18a2e2a3..8c8a9033 100644 --- a/packages/archlinux/powerline.install +++ b/packages/archlinux/powerline.install @@ -4,7 +4,7 @@ post_install() { echo " IMPORTANT ---------- +========= Powerline requires custom glyphs to work properly. A font with these glyphs has been installed along with a fontconfig file which enables the glyphs for many @@ -15,6 +15,25 @@ on GitHub: https://github.com/Lokaltog/powerline/issues Consult the documentation for detailed installation instructions and troubleshooting information: http://lokaltog.github.com/powerline/ + +Vim installation +---------------- + +The plugin has been installed and is enabled by default. + +Zsh installation +---------------- + +Add the following line to your ~/.zshrc: + + . /usr/share/zsh/site-contrib/powerline.zsh + +Tmux installation +----------------- + +Add the following line to your ~/.tmux.conf: + + source '/usr/share/tmux/powerline.conf' " } From fd98313592835d2149365b302f55fecec88c0b2c Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 22 Jan 2013 21:15:30 +0400 Subject: [PATCH 0259/1472] Use elog in place of einfo Otherwise message is not repeated at the end of building process and thus is likely to be missed by user. --- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index d032d1ee..8a76124c 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 1876 SHA256 0d9d6e11e42ea59e83f8b862a7cc40912dca0754cd35391da5eed55a2635db67 SHA512 cf9c3b85e503be41e4a1780924e622c316cb1c84e9ada93f5130672f6e3cdfeea7b94f13c71a6d8689ebf9aaa9aa37df11f6d456d81b3ef9aaa3def6bd31b8bf WHIRLPOOL fdf09d93c0cd5acb41e964edf25b21dae77f3ca8a1a4c017913c17b474dcd1a1581358162e0c98d647c929064d0c269a2e40719c16e7001a38fe6659af57e264 +EBUILD powerline-9999.ebuild 1893 SHA256 89fb4b832b6ef2eda7b9d013ab7471cf9252f68266428cf9ad2e04b913ee44d4 SHA512 606110718187f98adb7dae2c7ff8b90288b7a7613e06a92a16ecac7ca98ef6acbde2c44ee3364bd5878315b5dc00bc8ae0bd4ef84300b93e3ed39827c8cc004b WHIRLPOOL 332205cc8139e30907f83d8299f2bea9fd9adf9de518397bdd74d7ab1baaa0d1bc61756cb6844405e81583e7e6b11006c5f9c7f9a82f663a4f4a9cceadd99513 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index faf8e561..7eeca444 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -57,9 +57,11 @@ src_install() { if use zsh ; then insinto /usr/share/zsh/site-contrib doins powerline/bindings/zsh/powerline.zsh - einfo "To enable powerline prompt add" - einfo " . /usr/share/zsh/site-contrib/powerline.zsh" - einfo "to your zshrc." + elog "" + elog "To enable powerline prompt add" + elog " . /usr/share/zsh/site-contrib/powerline.zsh" + elog "to your zshrc." + elog "" fi rm powerline/bindings/zsh/powerline.zsh distutils-r1_src_install From 2a9edc19ca5ac894f2072e527730820d35131e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 22 Jan 2013 18:50:37 +0100 Subject: [PATCH 0260/1472] Update troubleshooting info for ImportErrors on OS X Refs #39. Refs #95. --- docs/source/troubleshooting.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 41bb9745..2d2d6452 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -87,3 +87,6 @@ I receive an ``ImportError`` when trying to use Powerline on OS X! system Python. Please try to select another Python distribution:: sudo port select python python27-apple + + See the `issue #39 `_ + for a discussion and other possible solutions for this issue. From da6dea74a95d5eb8833fdafac6588df1934ee779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 23 Jan 2013 07:24:33 +0100 Subject: [PATCH 0261/1472] Fix Unicode encoding issue Closes #108. --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 4808887b..5c24ef90 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -152,7 +152,7 @@ def weather(unit='c', location_query=None): 'highlight_group': ['weather_condition_' + icon, 'weather_condition', 'weather'], }, { - 'contents': '{0}°{1}'.format(condition['temp'], unit.upper()), + 'contents': u'{0}°{1}'.format(condition['temp'], unit.upper()), 'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], 'draw_divider': False, }, From bf3d2e878b782c78ab666e0418c9e7c5d0b687ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 23 Jan 2013 07:38:52 +0100 Subject: [PATCH 0262/1472] Include bindings in package Closes #109. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 4114082e..97a85bd5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ recursive-include powerline *.json *.vim +recursive-include powerline/bindings *.* From b620d3a925e7f8709011057b7a6d375fdc81a7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 23 Jan 2013 08:27:44 +0100 Subject: [PATCH 0263/1472] Split Arch Linux packages into Python 2/3 variants --- .../{ => python-powerline-git}/.gitignore | 0 .../archlinux/python-powerline-git/PKGBUILD | 61 +++++++++++++++++++ .../powerline.install | 0 .../python2-powerline-git/.gitignore | 4 ++ .../{ => python2-powerline-git}/PKGBUILD | 4 +- .../python2-powerline-git/powerline.install | 43 +++++++++++++ 6 files changed, 110 insertions(+), 2 deletions(-) rename packages/archlinux/{ => python-powerline-git}/.gitignore (100%) create mode 100644 packages/archlinux/python-powerline-git/PKGBUILD rename packages/archlinux/{ => python-powerline-git}/powerline.install (100%) create mode 100644 packages/archlinux/python2-powerline-git/.gitignore rename packages/archlinux/{ => python2-powerline-git}/PKGBUILD (97%) create mode 100644 packages/archlinux/python2-powerline-git/powerline.install diff --git a/packages/archlinux/.gitignore b/packages/archlinux/python-powerline-git/.gitignore similarity index 100% rename from packages/archlinux/.gitignore rename to packages/archlinux/python-powerline-git/.gitignore diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD new file mode 100644 index 00000000..e847c816 --- /dev/null +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -0,0 +1,61 @@ +# Maintainer: Kim Silkebækken + +pkgname=python-powerline-git +pkgver=20130123 +pkgrel=1 +pkgdesc='The ultimate statusline/prompt utility.' +url='https://github.com/Lokaltog/powerline' +license=('CC BY-SA 3.0') +arch=('any') +depends=('python>=3.3') +makedepends=('git' 'python-distribute') +optdepends=('python-psutil: improved system information' + 'python-pygit2: improved git support' + 'zsh: better shell prompt') +install='powerline.install' +source=() + +_gitroot="https://github.com/Lokaltog/powerline.git" +_gitname="powerline" +_gitbranch="develop" + +build() { + cd ${srcdir} + + msg "Connecting to GitHub..." + + if [ -d ${srcdir}/${_gitname} ]; then + cd ${_gitname} + git pull origin ${_gitbranch} + msg "The local files are updated." + else + git clone ${_gitroot} + cd ${_gitname} + git checkout ${_gitbranch} + fi + + msg "Git checkout done or server timeout." + + python setup.py build || return 1 + python setup.py install --root=${pkgdir} || return 1 + + msg2 "Installing fonts..." + install -dm755 "${pkgdir}/usr/share/fonts/TTF/" + install -dm755 "${pkgdir}/etc/fonts/conf.avail" + install -dm755 "${pkgdir}/etc/fonts/conf.d" + install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" + install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" + ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" + + msg2 "Installing vim plugin..." + install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" + install -m644 "powerline/bindings/vim/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + + msg2 "Installing zsh plugin..." + install -dm755 "${pkgdir}/usr/share/zsh/site-contrib" + install -m644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" + + msg2 "Installing tmux configuration..." + install -dm755 "${pkgdir}/usr/share/tmux" + install -m644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" +} diff --git a/packages/archlinux/powerline.install b/packages/archlinux/python-powerline-git/powerline.install similarity index 100% rename from packages/archlinux/powerline.install rename to packages/archlinux/python-powerline-git/powerline.install diff --git a/packages/archlinux/python2-powerline-git/.gitignore b/packages/archlinux/python2-powerline-git/.gitignore new file mode 100644 index 00000000..4ef5aeeb --- /dev/null +++ b/packages/archlinux/python2-powerline-git/.gitignore @@ -0,0 +1,4 @@ +* +!.gitignore +!PKGBUILD +!*.install diff --git a/packages/archlinux/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD similarity index 97% rename from packages/archlinux/PKGBUILD rename to packages/archlinux/python2-powerline-git/PKGBUILD index 1521e7bc..22f12170 100644 --- a/packages/archlinux/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken -pkgname=powerline-git -pkgver=20130122 +pkgname=python2-powerline-git +pkgver=20130123 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' diff --git a/packages/archlinux/python2-powerline-git/powerline.install b/packages/archlinux/python2-powerline-git/powerline.install new file mode 100644 index 00000000..8c8a9033 --- /dev/null +++ b/packages/archlinux/python2-powerline-git/powerline.install @@ -0,0 +1,43 @@ +post_install() { + echo "Updating font cache..." + fc-cache -f + + echo " +IMPORTANT +========= + +Powerline requires custom glyphs to work properly. A font with these glyphs has +been installed along with a fontconfig file which enables the glyphs for many +common coding fonts. + +If Powerline doesn't work out of the box on your system, please submit an issue +on GitHub: https://github.com/Lokaltog/powerline/issues + +Consult the documentation for detailed installation instructions and +troubleshooting information: http://lokaltog.github.com/powerline/ + +Vim installation +---------------- + +The plugin has been installed and is enabled by default. + +Zsh installation +---------------- + +Add the following line to your ~/.zshrc: + + . /usr/share/zsh/site-contrib/powerline.zsh + +Tmux installation +----------------- + +Add the following line to your ~/.tmux.conf: + + source '/usr/share/tmux/powerline.conf' +" +} + +post_remove() { + echo "Updating font cache..." + fc-cache -f +} From 40d542af12ad209f5b293d9f9dd5ca65f5625090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 23 Jan 2013 09:56:58 +0100 Subject: [PATCH 0264/1472] Add troubleshooting info for PuTTy users Refs #107. --- docs/source/troubleshooting.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 2d2d6452..bc1e470f 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -51,6 +51,10 @@ I'm using tmux and Powerline looks like crap, what's wrong? If you use iTerm2, make sure that you have enabled the setting 'Set locale variables automatically' in Profiles > Terminal > Environment. +I'm using PuTTY and I get huge gaps around the arrow glyphs! + Please uncheck :guilabel:`Treat CJK ambiguous characters as wide` in + :menuselection:`Window --> Translation`. + Vim-specific issues ------------------- From b86f79cf9e480ead764980a6bf89aab52e8609cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 23 Jan 2013 10:24:07 +0100 Subject: [PATCH 0265/1472] Restructure docs slightly The header levels have been switched to correspond more with the Sphinx suggested standards and the troubleshooting section has been restructured to make it easier to find solutions. Minor markup changes and other changes are also included in this commit. --- docs/source/configuration.rst | 15 ++-- docs/source/fontpatching.rst | 11 +-- docs/source/index.rst | 5 +- docs/source/introduction.rst | 7 +- docs/source/license-credits.rst | 4 + docs/source/overview.rst | 59 ++++++------- docs/source/troubleshooting.rst | 147 ++++++++++++++++++++------------ 7 files changed, 147 insertions(+), 101 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 26fc8b73..844b8aad 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -1,5 +1,6 @@ +************* Configuration -============= +************* Powerline is configured with one main configuration file, and with separate configuration files for themes and colorschemes. All configuration files are @@ -37,7 +38,7 @@ file. Example: colorscheme/theme! Main configuration ------------------- +================== :Location: :file:`powerline/config.json` @@ -46,7 +47,7 @@ extensions, as well as some extension-specific options like themes and colorschemes. Common configuration -^^^^^^^^^^^^^^^^^^^^ +-------------------- Common configuration is a subdictionary that is a value of ``common`` key in :file:`powerline/config.json` file. @@ -68,7 +69,7 @@ Common configuration is a subdictionary that is a value of ``common`` key in have priority when searching for modules. Extension-specific configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +-------------------------------- Common configuration is a subdictionary that is a value of ``ext`` key in :file:`powerline/config.json` file. @@ -84,7 +85,7 @@ Common configuration is a subdictionary that is a value of ``ext`` key in buffer-specific statuslines in vim. Requires a custom matcher and theme. Colorschemes ------------- +============ :Location: :file:`powerline/colorschemes/{extension}/{name}.json` @@ -139,7 +140,7 @@ Colorschemes :ref:`groups ` option. Themes ------- +====== :Location: :file:`powerline/themes/{extension}/{name}.json` @@ -243,7 +244,7 @@ Themes *not* included in any modes, *except* for the modes in this list. Segments --------- +======== Segments are written in Python, and the default segments provided with Powerline are located in :file:`powerline/segments/{extension}.py`. diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index 49062b18..8c35c00f 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -1,7 +1,8 @@ .. _font-patching: +************* Font patching -============= +************* Powerline provides a font patcher for custom glyphs like the segment dividers (arrows), branch symbol, padlock symbol, etc. The font patcher @@ -24,7 +25,7 @@ patched versions of some popular programming fonts. configuration. Glyph table ------------ +=========== Powerline stores all special glyphs in the Unicode *Private Use Area* (``U+E000``-``U+F8FF``). @@ -42,7 +43,7 @@ Code point Glyph Description ========== ===== =========== Usage ------ +===== The font patcher is located at :file:`powerline/fontpatcher/fontpatcher.py`. It requires Python 2.7 and FontForge compiled with Python bindings to work. @@ -57,7 +58,7 @@ option to disable font renaming. the glyphs manually using a tool like ``gbdfed``. Linux -^^^^^ +----- 1. Install fontforge with Python bindings. For Ubuntu users the required package is ``python-fontforge``, for Arch Linux users the required @@ -89,7 +90,7 @@ Linux 6. Open vim and enjoy your new statusline! OS X -^^^^ +---- 1. Check if you have a FontForge version with Python support by running ``fontforge -version``. You should see something like this:: diff --git a/docs/source/index.rst b/docs/source/index.rst index 5f1e50ee..15843895 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,8 +1,9 @@ +********* Powerline -========= +********* .. toctree:: - :maxdepth: 3 + :maxdepth: 2 :glob: introduction diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index a0d4c16a..f030aec3 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -1,5 +1,6 @@ +************ Introduction -============ +************ This is the next version of Powerline, implemented in Python. It aims to resolve some of the "unresolvable" problems of the vimscript implementation, @@ -10,7 +11,7 @@ The project is currently in beta, and most of the functionality in the old vimscript project is already implemented. Screenshots ------------ +=========== **Mode-dependent highlighting** @@ -37,7 +38,7 @@ The font in the screenshots is `Pragmata Pro`_ by Fabrizio Schiavi. .. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm Feature highlights ------------------- +================== * **Better performance.** Python performs quite a bit better than vimscript, and by having most of the code in Python instead of vimscript it's also diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index 24eba2ac..89393f05 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -1,3 +1,7 @@ +******************* +License and credits +******************* + License ======= diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 59dc0145..abc0732a 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -1,8 +1,9 @@ +******** Overview -======== +******** Requirements ------------- +============ Powerline requires Python 3.3 or Python 2.7 to work. @@ -12,15 +13,15 @@ font or a patched font on your system. See `Font installation`_ for more details. Vim plugin requirements -^^^^^^^^^^^^^^^^^^^^^^^ +----------------------- The vim plugin requires a vim version with Python support compiled in. You can check if your vim supports Python by running ``vim --version | grep +python``. If your vim version doesn't have support for Python, you'll have to compile -it with the ``--enable-python3interp`` flag (``--enable-pythoninterp`` if -you want Python 2 support instead). Note that this also requires the related +it with the ``--enable-pythoninterp`` flag (``--enable-python3interp`` if +you want Python 3 support instead). Note that this also requires the related Python headers to be installed on your system. Please consult your distribution's documentation for details on how to compile and install packages. @@ -28,20 +29,20 @@ packages. Vim version 7.3.661 or newer is recommended for performance reasons. Optional dependencies -^^^^^^^^^^^^^^^^^^^^^ +--------------------- The following Python packages are not required by all segments, but -recommended for optimal performance: +recommended for optimal performance and extra features: * ``pygit2`` * ``mercurial`` * ``psutil`` Installation ------------- +============ Installing with ``pip`` -^^^^^^^^^^^^^^^^^^^^^^^ +----------------------- To install Powerline system-wide, run the following command as root:: @@ -54,7 +55,7 @@ system-wide, install with ``pip install --user`` instead. conflict with an unrelated project. Distribution-specific packages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------------ The following distribution-specific packages are officially supported, and they provide an easy way of installing and upgrading Powerline: @@ -65,10 +66,10 @@ they provide an easy way of installing and upgrading Powerline: .. _font-installation: Font installation -^^^^^^^^^^^^^^^^^ +----------------- Linux -***** +^^^^^ If you're running Linux, you may be able to avoid patching your coding font to get the special glyphs required by Powerline. This works by utilizing @@ -94,13 +95,13 @@ font (see :ref:`font-patching` for details). an issue on GitHub. OS X and Windows -**************** +^^^^^^^^^^^^^^^^ You'll have to use a patched font to use the Powerline symbols. See :ref:`font-patching` for details on font patching and pre-patched fonts. Usage ------ +===== .. note:: If Powerline is installed somewhere other than Python's site-packages directories (e.g. by having the git repo in your dotfiles @@ -108,14 +109,14 @@ Usage examples below. Vim statusline -^^^^^^^^^^^^^^ +-------------- Regular installation -******************** +^^^^^^^^^^^^^^^^^^^^ **The recommended way of installing Powerline is as a Python package.** You can then enable the vim plugin by adding the following line to your -``vimrc``: +:file:`vimrc`: .. code-block:: vim @@ -129,40 +130,40 @@ If Powerline is installed somewhere other than Python's site-packages directories you'll either have to use a plugin manager like Vundle, or source the vim plugin file with an absolute path to the plugin location. -Add the following line to your ``vimrc``, where ``{path}`` is the path to -the main Powerline project directory: +Add the following line to your :file:`vimrc`, where ``{path}`` is the path +to the main Powerline project directory: .. code-block:: vim source {path}/powerline/bindings/vim/plugin/source_plugin.vim Vundle installation -******************* +^^^^^^^^^^^^^^^^^^^ -If you're using Vundle you can add the following line to your ``vimrc``: +If you're using Vundle you can add the following line to your :file:`vimrc`: .. code-block:: vim Bundle 'Lokaltog/powerline', {'rtp': 'powerline/bindings/vim/'} Shell prompts -^^^^^^^^^^^^^ +------------- Bash prompt -*********** +^^^^^^^^^^^ -Add the following line to your ``.bashrc``, where ``{path}`` is the absolute -path to your Powerline installation directory: +Add the following line to your :file:`bashrc`, where ``{path}`` is the +absolute path to your Powerline installation directory: .. code-block:: bash . {path}/powerline/bindings/bash/powerline.sh Zsh prompt -********** +^^^^^^^^^^ -Add the following line to your ``.zshrc``, where ``{path}`` is the absolute -path to your Powerline installation directory: +Add the following line to your :file:`zshrc`, where ``{path}`` is the +absolute path to your Powerline installation directory: .. code-block:: bash @@ -171,7 +172,7 @@ path to your Powerline installation directory: Tmux statusline ^^^^^^^^^^^^^^^ -Add the following line to your ``.tmux.conf``, where ``{path}`` is the +Add the following line to your :file:`tmux.conf`, where ``{path}`` is the absolute path to your Powerline installation directory:: source '{path}/powerline/bindings/tmux/powerline.conf' diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index bc1e470f..561a8b59 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -1,96 +1,133 @@ +:tocdepth: 2 + +*************** Troubleshooting -=============== +*************** + +.. contents:: + +General issues +============== I can't see any fancy symbols, what's wrong? - Make sure that you've configured gvim or your terminal emulator to use - a patched font (see :ref:`font-patching`). +-------------------------------------------- - Make sure that vim is compiled with the ``--with-features=big`` flag. +Make sure that you've configured gvim or your terminal emulator to use +a patched font (see :ref:`font-patching`). - If you're using rxvt-unicode, make sure that it's compiled with the - ``--enable-unicode3`` flag. +Make sure that vim is compiled with the ``--with-features=big`` flag. - If you're using iTerm2, please update to `this revision - `_ - or newer. +If you're using rxvt-unicode, make sure that it's compiled with the +``--enable-unicode3`` flag. - You need to set your ``LANG`` and ``LC_*`` environment variables to - a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's - documentation for information about setting these variables correctly. +If you're using iTerm2, please update to `this revision +`_ +or newer. + +You need to set your ``LANG`` and ``LC_*`` environment variables to +a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's +documentation for information about setting these variables correctly. The fancy symbols look a bit blurry or "off"! - Make sure that you have patched all variants of your font (i.e. both the - regular and the bold font files). +--------------------------------------------- + +Make sure that you have patched all variants of your font (i.e. both the +regular and the bold font files). I'm unable to patch my font, what should I do? - Font patching is only known to work on most Linux and OS X machines. If - you have followed the instructions on :ref:`font-patching` and still - have problems, please submit an issue on GitHub. +---------------------------------------------- - You could also check out the `powerline-fonts - `_ repository on GitHub for - patched versions of some popular programming fonts. +Font patching is only known to work on most Linux and OS X machines. If you +have followed the instructions on :ref:`font-patching` and still have +problems, please submit an issue on GitHub. -The colors are weird in the default OS X Terminal app! - The default OS X Terminal app is known to have some issues with the - Powerline colors. Please use another terminal emulator. iTerm2 should - work fine. +You could also check out the `powerline-fonts +`_ repository on GitHub for +patched versions of some popular programming fonts. - The arrows may have the wrong colors if you have changed the "minimum - contrast" slider in the color tab of your OS X settings. +The colors look weird in the default OS X Terminal app! +------------------------------------------------------- -The colors are weird in iTerm2! - Please disable background transparency to resolve this issue. +The default OS X Terminal app is known to have some issues with the +Powerline colors. Please use another terminal emulator. iTerm2 should work +fine. + +The arrows may have the wrong colors if you have changed the "minimum +contrast" slider in the color tab of your OS X settings. + +The colors look weird in iTerm2! +-------------------------------- + +Please disable background transparency to resolve this issue. I'm using tmux and Powerline looks like crap, what's wrong? - You need to tell tmux that it has 256-color capabilities. Add this to - your :file:`.tmux.conf` to solve this issue:: +----------------------------------------------------------- - set -g default-terminal "screen-256color" +You need to tell tmux that it has 256-color capabilities. Add this to your +:file:`.tmux.conf` to solve this issue:: - If you use iTerm2, make sure that you have enabled the setting 'Set - locale variables automatically' in Profiles > Terminal > Environment. + set -g default-terminal "screen-256color" + +If you use iTerm2, make sure that you have enabled the setting +:guilabel:`Set locale variables automatically` in :menuselection:`Profiles +--> Terminal --> Environment`. I'm using PuTTY and I get huge gaps around the arrow glyphs! - Please uncheck :guilabel:`Treat CJK ambiguous characters as wide` in - :menuselection:`Window --> Translation`. +------------------------------------------------------------ + +Please uncheck :guilabel:`Treat CJK ambiguous characters as wide` in +:menuselection:`Window --> Translation`. Vim-specific issues -------------------- +=================== The statusline has strange characters like ``^B`` in it! - Please add ``set encoding=utf-8`` to your :file:`vimrc`. +-------------------------------------------------------- + +Please add ``set encoding=utf-8`` to your :file:`vimrc`. The statusline has a lot of ``^`` or underline characters in it! - You need to configure the ``fillchars`` setting to disable statusline - fillchars (see ``:h fillchars`` for details). Add this to your - :file:`vimrc` to solve this issue:: +---------------------------------------------------------------- - set fillchars+=stl:\ ,stlnc:\ +You need to configure the ``fillchars`` setting to disable statusline +fillchars (see ``:h fillchars`` for details). Add this to your :file:`vimrc` +to solve this issue: + +.. code-block:: vim + + set fillchars+=stl:\ ,stlnc:\ The statusline is hidden/only appears in split windows! - Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. +------------------------------------------------------- + +Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. I'm using gVim for Windows, and ``cmd`` windows keep popping up when working in git repos! - Either install ``libgit2`` and ``pygit2``, or disable the VCS segment in - your user configuration to resolve this issue. +------------------------------------------------------------------------------------------ + +Either install ``libgit2`` and ``pygit2``, or disable the VCS segment in +your user configuration to resolve this issue. I receive a ``NameError`` when trying to use Powerline with MacVim! - Please install MacVim using this command:: +------------------------------------------------------------------- - brew install macvim --env-std --override-system-vim +Please install MacVim using this command:: - Then install Powerline locally with ``pip install --user``, or by - running these commands in the ``powerline`` directory:: + brew install macvim --env-std --override-system-vim - ./setup.py build - ./setup.py install --user +Then install Powerline locally with ``pip install --user``, or by +running these commands in the ``powerline`` directory:: + + ./setup.py build + ./setup.py install --user I receive an ``ImportError`` when trying to use Powerline on OS X! - This is caused by an invalid ``sys.path`` when using system vim and - system Python. Please try to select another Python distribution:: +------------------------------------------------------------------ - sudo port select python python27-apple +This is caused by an invalid ``sys.path`` when using system vim and system +Python. Please try to select another Python distribution:: - See the `issue #39 `_ - for a discussion and other possible solutions for this issue. + sudo port select python python27-apple + +See the `issue #39 `_ +for a discussion and other possible solutions for this issue. From 2d6ee2655c44be30259cf2821a2618756a633a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 23 Jan 2013 23:27:13 +0100 Subject: [PATCH 0266/1472] Add 24-bit color support for shell renderers Refs #81. --- powerline/renderer.py | 9 +++++++++ powerline/renderers/shell.py | 10 ++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index d03b03fe..24824381 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -9,6 +9,8 @@ class Renderer(object): ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 + TERM_24BIT = False + def __init__(self, theme_config, local_themes, theme_kwargs): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes @@ -138,5 +140,12 @@ class Renderer(object): ''' return len(''.join([segment['rendered_raw'] for segment in segments])) + @staticmethod + def _int_to_rgb(int): + r = (int >> 16) & 0xff + g = (int >> 8) & 0xff + b = int & 0xff + return r, g, b + def hl(self, fg=None, bg=None, attr=None): raise NotImplementedError diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index e2b63929..b01940ce 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -17,12 +17,18 @@ class ShellRenderer(Renderer): if fg is False or fg[0] is False: ansi += [39] else: - ansi += [38, 5, fg[0]] + if self.TERM_24BIT: + ansi += [38, 2] + list(self._int_to_rgb(fg[1])) + else: + ansi += [38, 5, fg[0]] if bg is not None: if bg is False or bg[0] is False: ansi += [49] else: - ansi += [48, 5, bg[0]] + if self.TERM_24BIT: + ansi += [48, 2] + list(self._int_to_rgb(bg[1])) + else: + ansi += [48, 5, bg[0]] if attr is not None: if attr is False: ansi += [22] From bacb260312448911df9f382c9096b4f3c27b8675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 24 Jan 2013 08:21:44 +0100 Subject: [PATCH 0267/1472] Revert "Remove Ubuntu Mono from fontconfig file" This reverts commit b57174a16cb41ad60abf04dc71c92fb7102c673b. Closes #113. --- font/10-powerline-symbols.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/font/10-powerline-symbols.conf b/font/10-powerline-symbols.conf index de4cca49..7c23e0b7 100644 --- a/font/10-powerline-symbols.conf +++ b/font/10-powerline-symbols.conf @@ -62,4 +62,8 @@ Liberation Mono PowerlineSymbols + + Ubuntu Mono + PowerlineSymbols + From 0d2056ab049aeab2946790b44103680e9d7129db Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 23 Jan 2013 23:45:16 +0100 Subject: [PATCH 0268/1472] Shorten /home/foo/ to ~foo in vim file directory segment Replace "/home/foo/" with "~foo" (foo's home, not current user's). This shortens paths like /home/www-data/foo/. Closes #115. --- powerline/segments/vim.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index d9177184..c1558151 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -81,6 +81,8 @@ def readonly_indicator(text=u''): def file_directory(): '''Return file directory (head component of the file path).''' file_directory = vim_funcs['expand']('%:~:.:h') + if file_directory.startswith('/home/'): + file_directory = '~' + file_directory[6:] return file_directory.decode('utf-8') + os.sep if file_directory else None From 9282bf61f4bac04b0fe1109c5e261d6fccc4199d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 24 Jan 2013 08:00:18 +0100 Subject: [PATCH 0269/1472] Add package conflicts to Arch Linux PKGBUILDs --- packages/archlinux/python-powerline-git/PKGBUILD | 3 ++- packages/archlinux/python2-powerline-git/PKGBUILD | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index e847c816..cdf78f1e 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=python-powerline-git -pkgver=20130123 +pkgver=20130124 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -12,6 +12,7 @@ makedepends=('git' 'python-distribute') optdepends=('python-psutil: improved system information' 'python-pygit2: improved git support' 'zsh: better shell prompt') +conflicts=('powerline-git') install='powerline.install' source=() diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 22f12170..386fafbc 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=python2-powerline-git -pkgver=20130123 +pkgver=20130124 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -13,6 +13,7 @@ optdepends=('python2-psutil: improved system information' 'python2-pygit2: improved git support' 'mercurial: improved mercurial support' 'zsh: better shell prompt') +conflicts=('powerline-git') install='powerline.install' source=() From 5d5841c1fa8c242e45d8c27358df225b5c877385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 24 Jan 2013 12:17:25 +0100 Subject: [PATCH 0270/1472] Fix vim file_directory segment Closes #117. Closes #119. --- powerline/segments/vim.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c1558151..4c337c51 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -81,6 +81,8 @@ def readonly_indicator(text=u''): def file_directory(): '''Return file directory (head component of the file path).''' file_directory = vim_funcs['expand']('%:~:.:h') + if file_directory is None: + return None if file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory.decode('utf-8') + os.sep if file_directory else None From cefa7ef5fe9492e165467a33c276d1319024c0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 24 Jan 2013 12:35:58 +0100 Subject: [PATCH 0271/1472] Add file_size segment for vim Closes #118. --- .../config_files/colorschemes/vim/default.json | 1 + powerline/segments/vim.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 065d7281..8703eaa4 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -64,6 +64,7 @@ "branch:divider": { "fg": "gray7", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, + "file_size": { "fg": "gray8", "bg": "gray2" }, "file_name_no_file": { "fg": "gray9", "bg": "gray4", "attr": ["bold"] }, "file_name_empty": { "fg": "gray9", "bg": "gray4" }, "file_format": { "fg": "gray8", "bg": "gray2" }, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 4c337c51..57602934 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -6,7 +6,7 @@ import os import vim from powerline.bindings.vim import vim_get_func -from powerline.lib import memoize +from powerline.lib import memoize, humanize_bytes from powerline.lib.vcs import guess vim_funcs = { @@ -15,6 +15,7 @@ vim_funcs = { 'expand': vim_get_func('expand'), 'line': vim_get_func('line', rettype=int), 'mode': vim_get_func('mode'), + 'getfsize': vim_get_func('getfsize', rettype=int), } vim_modes = { @@ -101,6 +102,20 @@ def file_name(display_no_file=False, no_file_text='[No file]'): return file_name.decode('utf-8') +@memoize(2) +def file_size(suffix='B', binary_prefix=False): + '''Return file size. + + Returns None if the file isn't saved, or if the size is too + big to fit in a number. + ''' + file_name = vim_funcs['expand']('%') + file_size = vim_funcs['getfsize'](file_name) + if file_size < 0: + return None + return humanize_bytes(file_size, suffix, binary_prefix) + + def file_format(): '''Return file format (i.e. line ending type). From a87309899f42204ddcc9de7ed3aebef37d569caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 24 Jan 2013 17:21:46 +0100 Subject: [PATCH 0272/1472] Add parameter for home dir shortening Closes #115. --- powerline/segments/vim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 57602934..ab885212 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -79,12 +79,12 @@ def readonly_indicator(text=u''): return text if int(vim.eval('&readonly')) else None -def file_directory(): +def file_directory(shorten_home=False): '''Return file directory (head component of the file path).''' file_directory = vim_funcs['expand']('%:~:.:h') if file_directory is None: return None - if file_directory.startswith('/home/'): + if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory.decode('utf-8') + os.sep if file_directory else None From 6ac9f0d602c16b6b73603f8e16e52b81768db9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 24 Jan 2013 17:35:16 +0100 Subject: [PATCH 0273/1472] Restructure and improve installation docs The installation docs have been split into separate guides for OS X and Linux, with OS-specific troubleshooting as part of the installation guide. Terminal emulator support tables have been added to both guides. Closes #121. --- docs/source/_static/img/icons/cross.png | Bin 0 -> 473 bytes docs/source/_static/img/icons/error.png | Bin 0 -> 543 bytes docs/source/_static/img/icons/tick.png | Bin 0 -> 451 bytes docs/source/index.rst | 3 +- docs/source/installation/linux.rst | 125 ++++++++++++++++ docs/source/installation/osx.rst | 129 +++++++++++++++++ .../installation/troubleshooting-common.rst | 32 +++++ docs/source/overview.rst | 85 +---------- docs/source/troubleshooting.rst | 133 ------------------ 9 files changed, 292 insertions(+), 215 deletions(-) create mode 100644 docs/source/_static/img/icons/cross.png create mode 100644 docs/source/_static/img/icons/error.png create mode 100644 docs/source/_static/img/icons/tick.png create mode 100644 docs/source/installation/linux.rst create mode 100644 docs/source/installation/osx.rst create mode 100644 docs/source/installation/troubleshooting-common.rst delete mode 100644 docs/source/troubleshooting.rst diff --git a/docs/source/_static/img/icons/cross.png b/docs/source/_static/img/icons/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..33a383748bca465d6d0e7be4fa2ce83c861e36ba GIT binary patch literal 473 zcmV;~0Ve*5P)1Bai@#8xK6ALSt`Ds!U zgJE$w!x4r~upuBeNDL+qQVRo&AX69_8I6G^o-xSIWB9ggCR~y$DjiIti(QyHiNWT; z1rwm&voHfda$rLYlhYZ#Z(52q5IOy#8P3n03Dykco(1bgG5}#nY&^ryJ)2N8v%B~) zoL{^Ut{E1HENGU1B^ZDHMEDoxSD+ZO8uT!Lhqh5*5X0XKC$V~g*}$CP-1>DOS1`g{ z05<@n+1SUM;or?Ga1BiAI$#=I?Ch4UU_;fHo8W8^o&;cN2@GxNd{hQ%GD^#sypjxcyz{1W3 zEO_G35tPt|#SXeWC><~$WhP^!%wmis3^C+UG80S@4$WBQ;juso00=MutP2U)_A3w6 P00000NkvXXu0mjf8D7L! literal 0 HcmV?d00001 diff --git a/docs/source/_static/img/icons/error.png b/docs/source/_static/img/icons/error.png new file mode 100644 index 0000000000000000000000000000000000000000..dbfda229750f238e5ea15ea9c6de43b9630a5e57 GIT binary patch literal 543 zcmV+)0^t3LP)b-Q zag`tu6obp!L~)B0F04hs!e-fWL9np!FIZTLT3Lt|f`$77+}0w6){1Cgk(Djfu;K@q znfLacmk$ORL`dN9PR=>^<=&eap|wUkpV-OjRHUJl8gxYMiagMXAt_}gDkq~vtrP;U zc9@+hfgq>$t_wJl4*aF=-Vgb%v1t(G)S||f5x?FkocMZf?mH@HmlovI=)HLX+f=@H zJW%)wxc{)Sd1^sUjo#}~WT*kxgpDQf_ z=WBp>izV$c&8R?krb){CiP!g!m=3GV{oeqv0>Jz|F2%SjxZ@ zxGsL|j9QS_wUo`+tf^tGR7!93O%@RMewtR3tzslx+}~C$$Xl0~4703ZtvFdN=!tK= zjZKRB$-`mmjcxlz2)L}~=IZ2h?+;>uuTkEvS{GWXg z=jFEi{#(+mPYdl{bJ+sm-!h2X0+2YXqDA3r`Z z{0G?zlwk(Ok%)vW!-P4vv1&#KAea1K`JDUz+#3vF3}U;W`I!(fb_Hr!{gNAuT}jjo tH^dVdGoGE~X@(m@tY$_^vok<|0RUEP!L?xlWyb&j002ovPDHLkV1gj@#Tx(s literal 0 HcmV?d00001 diff --git a/docs/source/index.rst b/docs/source/index.rst index 15843895..01eae89c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -8,9 +8,8 @@ Powerline introduction overview - fontpatching configuration - troubleshooting + fontpatching license-credits Indices and tables diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst new file mode 100644 index 00000000..0f7967a8 --- /dev/null +++ b/docs/source/installation/linux.rst @@ -0,0 +1,125 @@ +:tocdepth: 2 + +.. _installation-linux: + +********************* +Installation on Linux +********************* + +The following distribution-specific packages are officially supported, and +they provide an easy way of installing and upgrading Powerline. The packages +will automatically do most of the configuration for you, but you should +still skim through this guide so you know how the plugin works. + +* `Arch Linux (AUR), Python 2 version `_ +* `Arch Linux (AUR), Python 3 version `_ +* Gentoo Live ebuild (:file:`packages/gentoo/app-misc/powerline/`) + +If you're running a distribution without an official package you'll have to +follow the installation guide below: + +Plugin installation +=================== + +1. Install Python 3.3+ or Python 2.7. +2. Install Powerline using the following command:: + + pip install --user git+git://github.com/Lokaltog/powerline + +.. note:: You need to use the GitHub URI when installing Powerline! This + project is currently unavailable on the PyPI due to a naming conflict + with an unrelated project. + +.. warning:: Installing Powerline with a vim plugin manager like Vundle is + not recommended and not officially supported. + +Font installation +================= + +================== ============================= ====================== +Application/terminal emulator font support table +----------------------------------------------------------------------- +Name Patched font support Fontconfig support +================== ============================= ====================== +Gnome Terminal |supp_yes| |supp_yes| +Gvim |supp_yes| |supp_no| +Konsole |supp_yes| |supp_yes| +lxterminal |supp_yes| |supp_yes| +rxvt-unicode |supp_partial| [#]_ |supp_no| +st |supp_yes| |supp_yes| +Xfce Terminal |supp_yes| |supp_yes| +xterm |supp_yes| |supp_no| +================== ============================= ====================== + +.. |supp_yes| image:: ../_static/img/icons/tick.png +.. |supp_no| image:: ../_static/img/icons/cross.png +.. |supp_partial| image:: ../_static/img/icons/error.png + +.. [#] Must be compiled with ``--enable-unicode3`` for the + patched font to work. + +Powerline provides two ways of installing the required fonts on Linux. The +recommended method is using ``fontconfig`` if your terminal emulator +supports it (see the table above). + +Fontconfig +---------- + +1. Download the `latest version of PowerlineSymbols + `_ + and the `latest version of the fontconfig file + `_. +2. Move :file:`PowerlineSymbols.otf` to :file:`~/.fonts/` (or another X font + directory). +3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. +4. Move :file:`10-powerline-symbols.conf` to either :file:`~/.fonts.conf.d/` + or :file:`~/.config/fontconfig/conf.d/`, depending on your fontconfig + version. +5. If you don't see the arrow symbols, please close all instances of your + terminal emulator or gvim. You may also have to restart X for the changes + to take effect. If you *still* don't see the arrow symbols, please submit + an issue on GitHub. + +Patched font +------------ + +1. Download the font of your choice from `powerline-fonts`_. If you can't + find your preferred font in the `powerline-fonts`_ repo, you'll have to + patch your own font instead. See :ref:`font-patching` for instructions. +2. Move your patched font to :file:`~/.fonts/` (or another X font + directory). +3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. +4. Update Gvim or your terminal emulator to use the patched font. (the + correct font usually ends with *for Powerline*). +5. If you don't see the arrow symbols, please close all instances of your + terminal emulator or gvim. You may also have to restart X for the changes + to take effect. If you *still* don't see the arrow symbols, please submit + an issue on GitHub. + +.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts + +Troubleshooting +=============== + +.. contents:: + :local: + +I can't see any fancy symbols, what's wrong? +-------------------------------------------- + +* Make sure that you've configured gvim or your terminal emulator to use + a patched font (see :ref:`font-patching`). +* You need to set your ``LANG`` and ``LC_*`` environment variables to + a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's + documentation for information about setting these variables correctly. +* Make sure that vim is compiled with the ``--with-features=big`` flag. +* If you're using rxvt-unicode, make sure that it's compiled with the + ``--enable-unicode3`` flag. + +The fancy symbols look a bit blurry or "off"! +--------------------------------------------- + +* Make sure that you have patched all variants of your font (i.e. both the + regular and the bold font files). + +.. include:: troubleshooting-common.rst diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst new file mode 100644 index 00000000..347e67be --- /dev/null +++ b/docs/source/installation/osx.rst @@ -0,0 +1,129 @@ +:tocdepth: 2 + +.. _installation-osx: + +******************** +Installation on OS X +******************** + +Plugin installation +=================== + +Python package +-------------- + +1. Install a proper Python version (see `issue #39 + `_ for a discussion + regarding the required Python version on OS X):: + + sudo port select python python27-apple + +2. Install Powerline using the following command:: + + pip install --user git+git://github.com/Lokaltog/powerline + +.. note:: You need to use the GitHub URI when installing Powerline! This + project is currently unavailable on the PyPI due to a naming conflict + with an unrelated project. + +.. warning:: Installing Powerline with a vim plugin manager like Vundle is + not recommended and not officially supported. + +Vim installation +---------------- + +Any terminal vim version with Python 3.3 or Python 2.7 support should work, +but if you're using MacVim you need to install it using the following +command:: + + brew install macvim --env-std --override-system-vim + +Font installation +================= + +================== ============================= +Application/terminal emulator font support table +------------------------------------------------ +Name Patched font support +================== ============================= +iTerm2 |supp_yes| +Terminal.app |supp_yes| +================== ============================= + +.. |supp_yes| image:: ../_static/img/icons/tick.png +.. |supp_no| image:: ../_static/img/icons/cross.png +.. |supp_partial| image:: ../_static/img/icons/error.png + +.. note:: You need a patched font for Powerline to work on OS X. Check out + the `powerline-fonts`_ repository on GitHub for patched versions of some + popular programming fonts. + +1. Download the font of your choice and install it by double-clicking the + font file in Finder and then click :guilabel:`Install this font` in the + preview window. + + If you can't find your preferred font in the `powerline-fonts`_ repo, + you'll have to patch your own font instead. See :ref:`font-patching` for + instructions. +2. Configure MacVim or your terminal emulator to use the patched font (the + correct font usually ends with *for Powerline*). + +.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts + +Troubleshooting +=============== + +.. contents:: + :local: + +I can't see any fancy symbols, what's wrong? +-------------------------------------------- + +* If you're using iTerm2, please update to `this revision + `_ + or newer. +* You need to set your ``LANG`` and ``LC_*`` environment variables to + a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's + documentation for information about setting these variables correctly. + +The colors look weird in the default OS X Terminal app! +------------------------------------------------------- + +* The arrows may have the wrong colors if you have changed the "minimum + contrast" slider in the color tab of your OS X settings. +* The default OS X Terminal app is known to have some issues with the + Powerline colors. Please use another terminal emulator. iTerm2 should work + fine. + +The colors look weird in iTerm2! +-------------------------------- + +* The arrows may have the wrong colors if you have changed the "minimum + contrast" slider in the color tab of your OS X settings. +* Please disable background transparency to resolve this issue. + +I receive a ``NameError`` when trying to use Powerline with MacVim! +------------------------------------------------------------------- + +* Please install MacVim using this command:: + + brew install macvim --env-std --override-system-vim + + Then install Powerline locally with ``pip install --user``, or by + running these commands in the ``powerline`` directory:: + + ./setup.py build + ./setup.py install --user + +I receive an ``ImportError`` when trying to use Powerline on OS X! +------------------------------------------------------------------ + +* This is caused by an invalid ``sys.path`` when using system vim and system + Python. Please try to select another Python distribution:: + + sudo port select python python27-apple + +* See `issue #39 `_ for + a discussion and other possible solutions for this issue. + +.. include:: troubleshooting-common.rst diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst new file mode 100644 index 00000000..5570bfc3 --- /dev/null +++ b/docs/source/installation/troubleshooting-common.rst @@ -0,0 +1,32 @@ +I'm using tmux and Powerline looks like crap, what's wrong? +----------------------------------------------------------- + +* You need to tell tmux that it has 256-color capabilities. Add this to your + :file:`.tmux.conf` to solve this issue:: + + set -g default-terminal "screen-256color" + +* If you're using iTerm2, make sure that you have enabled the setting + :guilabel:`Set locale variables automatically` in :menuselection:`Profiles + --> Terminal --> Environment`. + +My vim statusline has strange characters like ``^B`` in it! +----------------------------------------------------------- + +* Please add ``set encoding=utf-8`` to your :file:`vimrc`. + +My vim statusline has a lot of ``^`` or underline characters in it! +------------------------------------------------------------------- + +* You need to configure the ``fillchars`` setting to disable statusline + fillchars (see ``:h fillchars`` for details). Add this to your + :file:`vimrc` to solve this issue: + + .. code-block:: vim + + set fillchars+=stl:\ ,stlnc:\ + +My vim statusline is hidden/only appears in split windows! +---------------------------------------------------------- + +* Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index abc0732a..fa123229 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -9,8 +9,8 @@ Powerline requires Python 3.3 or Python 2.7 to work. Powerline uses several special glyphs to get the arrow effect and some custom symbols for developers. This requires that you either have the symbol -font or a patched font on your system. See `Font installation`_ for more -details. +font or a patched font on your system (details in installation +instructions). Vim plugin requirements ----------------------- @@ -41,82 +41,16 @@ recommended for optimal performance and extra features: Installation ============ -Installing with ``pip`` ------------------------ - -To install Powerline system-wide, run the following command as root:: - - pip install git+git://github.com/Lokaltog/powerline - -If you don't have root access or don't want to install Powerline -system-wide, install with ``pip install --user`` instead. - -.. note:: This project is currently unavailable on the PyPI due to a naming - conflict with an unrelated project. - -Distribution-specific packages ------------------------------- - -The following distribution-specific packages are officially supported, and -they provide an easy way of installing and upgrading Powerline: - -* `Arch Linux (AUR) `_ -* Gentoo Live ebuild (:file:`packages/gentoo/app-misc/powerline/`) - -.. _font-installation: - -Font installation ------------------ - -Linux -^^^^^ - -If you're running Linux, you may be able to avoid patching your coding font -to get the special glyphs required by Powerline. This works by utilizing -fontconfig's fallback font feature, which replaces missing glyphs in a font -with another font on your system. - -This has been tested and works very well with many different coding fonts, -but some fonts may look terrible, in which case you'll have to use a patched -font (see :ref:`font-patching` for details). - -1. Download the `latest version of PowerlineSymbols - `_ - and the `latest version of the fontconfig file - `_. -2. Move :file:`PowerlineSymbols.otf` to :file:`~/.fonts/`. -3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. -4. Move :file:`10-powerline-symbols.conf` to either :file:`~/.fonts.conf.d/` - or :file:`~/.config/fontconfig/conf.d/`, depending on your fontconfig - version. -5. If you don't see the arrow symbols, please close all instances of your - terminal emulator or gvim. You may also have to restart X for the changes - to take effect. If you *still* don't see the arrow symbols, please submit - an issue on GitHub. - -OS X and Windows -^^^^^^^^^^^^^^^^ - -You'll have to use a patched font to use the Powerline symbols. See -:ref:`font-patching` for details on font patching and pre-patched fonts. +* :ref:`installation-linux` +* :ref:`installation-osx` Usage ===== -.. note:: If Powerline is installed somewhere other than Python's - site-packages directories (e.g. by having the git repo in your dotfiles - directory) you'll have to use the absolute path to the scripts in the - examples below. - Vim statusline -------------- -Regular installation -^^^^^^^^^^^^^^^^^^^^ - -**The recommended way of installing Powerline is as a Python package.** -You can then enable the vim plugin by adding the following line to your -:file:`vimrc`: +Add the following line to your :file:`vimrc`: .. code-block:: vim @@ -137,15 +71,6 @@ to the main Powerline project directory: source {path}/powerline/bindings/vim/plugin/source_plugin.vim -Vundle installation -^^^^^^^^^^^^^^^^^^^ - -If you're using Vundle you can add the following line to your :file:`vimrc`: - -.. code-block:: vim - - Bundle 'Lokaltog/powerline', {'rtp': 'powerline/bindings/vim/'} - Shell prompts ------------- diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst deleted file mode 100644 index 561a8b59..00000000 --- a/docs/source/troubleshooting.rst +++ /dev/null @@ -1,133 +0,0 @@ -:tocdepth: 2 - -*************** -Troubleshooting -*************** - -.. contents:: - -General issues -============== - -I can't see any fancy symbols, what's wrong? --------------------------------------------- - -Make sure that you've configured gvim or your terminal emulator to use -a patched font (see :ref:`font-patching`). - -Make sure that vim is compiled with the ``--with-features=big`` flag. - -If you're using rxvt-unicode, make sure that it's compiled with the -``--enable-unicode3`` flag. - -If you're using iTerm2, please update to `this revision -`_ -or newer. - -You need to set your ``LANG`` and ``LC_*`` environment variables to -a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's -documentation for information about setting these variables correctly. - -The fancy symbols look a bit blurry or "off"! ---------------------------------------------- - -Make sure that you have patched all variants of your font (i.e. both the -regular and the bold font files). - -I'm unable to patch my font, what should I do? ----------------------------------------------- - -Font patching is only known to work on most Linux and OS X machines. If you -have followed the instructions on :ref:`font-patching` and still have -problems, please submit an issue on GitHub. - -You could also check out the `powerline-fonts -`_ repository on GitHub for -patched versions of some popular programming fonts. - -The colors look weird in the default OS X Terminal app! -------------------------------------------------------- - -The default OS X Terminal app is known to have some issues with the -Powerline colors. Please use another terminal emulator. iTerm2 should work -fine. - -The arrows may have the wrong colors if you have changed the "minimum -contrast" slider in the color tab of your OS X settings. - -The colors look weird in iTerm2! --------------------------------- - -Please disable background transparency to resolve this issue. - -I'm using tmux and Powerline looks like crap, what's wrong? ------------------------------------------------------------ - -You need to tell tmux that it has 256-color capabilities. Add this to your -:file:`.tmux.conf` to solve this issue:: - - set -g default-terminal "screen-256color" - -If you use iTerm2, make sure that you have enabled the setting -:guilabel:`Set locale variables automatically` in :menuselection:`Profiles ---> Terminal --> Environment`. - -I'm using PuTTY and I get huge gaps around the arrow glyphs! ------------------------------------------------------------- - -Please uncheck :guilabel:`Treat CJK ambiguous characters as wide` in -:menuselection:`Window --> Translation`. - -Vim-specific issues -=================== - -The statusline has strange characters like ``^B`` in it! --------------------------------------------------------- - -Please add ``set encoding=utf-8`` to your :file:`vimrc`. - -The statusline has a lot of ``^`` or underline characters in it! ----------------------------------------------------------------- - -You need to configure the ``fillchars`` setting to disable statusline -fillchars (see ``:h fillchars`` for details). Add this to your :file:`vimrc` -to solve this issue: - -.. code-block:: vim - - set fillchars+=stl:\ ,stlnc:\ - -The statusline is hidden/only appears in split windows! -------------------------------------------------------- - -Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. - -I'm using gVim for Windows, and ``cmd`` windows keep popping up when working in git repos! ------------------------------------------------------------------------------------------- - -Either install ``libgit2`` and ``pygit2``, or disable the VCS segment in -your user configuration to resolve this issue. - -I receive a ``NameError`` when trying to use Powerline with MacVim! -------------------------------------------------------------------- - -Please install MacVim using this command:: - - brew install macvim --env-std --override-system-vim - -Then install Powerline locally with ``pip install --user``, or by -running these commands in the ``powerline`` directory:: - - ./setup.py build - ./setup.py install --user - -I receive an ``ImportError`` when trying to use Powerline on OS X! ------------------------------------------------------------------- - -This is caused by an invalid ``sys.path`` when using system vim and system -Python. Please try to select another Python distribution:: - - sudo port select python python27-apple - -See the `issue #39 `_ -for a discussion and other possible solutions for this issue. From e49f760510543d6723c3bde4e0d3b84aea86bbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 09:37:03 +0100 Subject: [PATCH 0274/1472] Use hex strings for RGB colors in colorschemes Previously you'd have to convert a hex number to an integer, this change makes it possible to use a hex string instead which is much more useful. --- docs/source/configuration.rst | 6 +++--- powerline/colorscheme.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 844b8aad..736666d4 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -99,9 +99,9 @@ Colorschemes color, and the value is one of the following: * A cterm color index. - * A list of two integers, where the first integer is a cterm color - index, and the second is an RGB/hex color. This is useful for - colorschemes that use colors that aren't available in color terminals. + * A list with a cterm color index and a hex color string (e.g. ``[123, + "aabbcc"]``). This is useful for colorschemes that use colors that + aren't available in color terminals. ``groups`` .. _config-colorscheme-groups: diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 89330603..f8801c08 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -12,7 +12,7 @@ class Colorscheme(object): # 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]) + self.colors[color_name] = (color[0], int(color[1], 16)) except TypeError: self.colors[color_name] = (color, cterm_to_hex[color]) From cfe96ff01014332e59a77a5bd6e2ca2ead7cbef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 09:40:07 +0100 Subject: [PATCH 0275/1472] Fix minor doc issues --- docs/source/configuration.rst | 2 +- docs/source/fontpatching.rst | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 736666d4..155ae1a1 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -180,7 +180,7 @@ Themes .. _config-themes-seg-module: Function module, only required for function segments. Defaults to - ``powerline.ext.{extension}.segments``. Default is overriden by + ``powerline.segments.{extension}``. Default is overriden by :ref:`default_module theme option `. ``name`` diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index 8c35c00f..c04d4985 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -8,9 +8,6 @@ Powerline provides a font patcher for custom glyphs like the segment dividers (arrows), branch symbol, padlock symbol, etc. The font patcher requires FontForge with Python bindings to work. -You may be able to avoid patching your coding font to use the custom glyphs! -See :ref:`font-installation` for instructions. - Check out the `powerline-fonts `_ repository on GitHub for patched versions of some popular programming fonts. From 9985ca5313b74bbff9a526b3c71b1ce9601becd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 09:44:31 +0100 Subject: [PATCH 0276/1472] Add hex colors for vim gradient --- powerline/config_files/colorschemes/vim/default.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 8703eaa4..f87b233e 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -46,12 +46,12 @@ "gray9": 250, "gray10": 252, - "gradient1": 190, - "gradient2": 184, - "gradient3": 178, - "gradient4": 172, - "gradient5": 166, - "gradient6": 160 + "gradient1": [190, "8ae71c"], + "gradient2": [184, "c2e821"], + "gradient3": [178, "e9d926"], + "gradient4": [172, "eaa72b"], + "gradient5": [166, "eb7830"], + "gradient6": [160, "ec4b35"] }, "groups": { "background": { "fg": "white", "bg": "gray2" }, From 16b82cf070c9552d4e6d6dbb13a01a294368f13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 10:01:30 +0100 Subject: [PATCH 0277/1472] Add configuration option and docs for 24-bit terminal colors Closes #81. --- docs/source/configuration.rst | 26 ++++++++++++++++++++++++++ powerline/config_files/config.json | 1 + powerline/core.py | 3 ++- powerline/renderer.py | 5 +++-- powerline/renderers/shell.py | 4 ++-- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 155ae1a1..1c188588 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -52,6 +52,32 @@ Common configuration Common configuration is a subdictionary that is a value of ``common`` key in :file:`powerline/config.json` file. +``term_24bit_colors`` + Defines whether to output a cterm index (8-bit) or RGB colors (24-bit) + to the terminal emulator. + + .. table:: 24-bit color support table + :name: term-rgb-color-support + + ================== ===================== + Terminal emulator 24-bit color support + ================== ===================== + Gnome Terminal |supp_no| + Gvim |supp_no| + Konsole |supp_yes| + lxterminal |supp_no| + rxvt-unicode |supp_no| + st |supp_no| + Xfce Terminal |supp_no| + xterm |supp_partial| [#]_ + ================== ===================== + + .. |supp_yes| image:: _static/img/icons/tick.png + .. |supp_no| image:: _static/img/icons/cross.png + .. |supp_partial| image:: _static/img/icons/error.png + + .. [#] Uses nearest color from 8-bit palette. + ``dividers`` Defines the dividers used in all Powerline extensions. This option should usually only be changed if you don't have a patched font, or if diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 35b5bb16..131ffc20 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -1,5 +1,6 @@ { "common": { + "term_24bit_colors": false, "dividers": { "left": { "hard": "", diff --git a/powerline/core.py b/powerline/core.py index ae20f6a1..c8ec5b6e 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -46,7 +46,8 @@ class Powerline(object): renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) - self.renderer = Renderer(theme_config, local_themes, theme_kwargs) + self.renderer = Renderer(theme_config, local_themes, theme_kwargs, + term_24bit_colors=self.config.get('term_24bit_colors', False)) def add_local_theme(self, key, config): '''Add local themes at runtime (e.g. during vim session). diff --git a/powerline/renderer.py b/powerline/renderer.py index 24824381..d3483b57 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -9,12 +9,13 @@ class Renderer(object): ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - TERM_24BIT = False + TERM_24BIT_COLORS = False - def __init__(self, theme_config, local_themes, theme_kwargs): + def __init__(self, theme_config, local_themes, theme_kwargs, term_24bit_colors=False): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs + self.TERM_24BIT_COLORS = term_24bit_colors def add_local_theme(self, matcher, theme): if matcher in self.local_themes: diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index b01940ce..14246998 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -17,7 +17,7 @@ class ShellRenderer(Renderer): if fg is False or fg[0] is False: ansi += [39] else: - if self.TERM_24BIT: + if self.TERM_24BIT_COLORS: ansi += [38, 2] + list(self._int_to_rgb(fg[1])) else: ansi += [38, 5, fg[0]] @@ -25,7 +25,7 @@ class ShellRenderer(Renderer): if bg is False or bg[0] is False: ansi += [49] else: - if self.TERM_24BIT: + if self.TERM_24BIT_COLORS: ansi += [48, 2] + list(self._int_to_rgb(bg[1])) else: ansi += [48, 5, bg[0]] From c06f8836b38925a8f49eae2d220586b704d9c3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 10:48:44 +0100 Subject: [PATCH 0278/1472] Update docs and create a common terminal feature support matrix --- docs/source/configuration.rst | 49 +++++++++--------------------- docs/source/installation/linux.rst | 25 ++------------- docs/source/installation/osx.rst | 19 ++---------- docs/source/overview.rst | 43 +++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 78 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 1c188588..7efd19d6 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -52,31 +52,12 @@ Common configuration Common configuration is a subdictionary that is a value of ``common`` key in :file:`powerline/config.json` file. +.. _config-term_24bit_colors: + ``term_24bit_colors`` - Defines whether to output a cterm index (8-bit) or RGB colors (24-bit) - to the terminal emulator. - - .. table:: 24-bit color support table - :name: term-rgb-color-support - - ================== ===================== - Terminal emulator 24-bit color support - ================== ===================== - Gnome Terminal |supp_no| - Gvim |supp_no| - Konsole |supp_yes| - lxterminal |supp_no| - rxvt-unicode |supp_no| - st |supp_no| - Xfce Terminal |supp_no| - xterm |supp_partial| [#]_ - ================== ===================== - - .. |supp_yes| image:: _static/img/icons/tick.png - .. |supp_no| image:: _static/img/icons/cross.png - .. |supp_partial| image:: _static/img/icons/error.png - - .. [#] Uses nearest color from 8-bit palette. + Defines whether to output cterm indices (8-bit) or RGB colors (24-bit) + to the terminal emulator. See the :ref:`term-feature-support-matrix` for + information on whether your terminal emulator supports 24-bit colors. ``dividers`` Defines the dividers used in all Powerline extensions. This option @@ -87,12 +68,12 @@ Common configuration is a subdictionary that is a value of ``common`` key in background colors, while the ``soft`` dividers are used to divide segments with the same background color. -``paths`` - .. _config-common-paths: +.. _config-common-paths: +``paths`` Defines additional paths which will be searched for modules when using - :ref:`module segment option `. Paths defined here - have priority when searching for modules. + :ref:`module segment option `. Paths defined + here have priority when searching for modules. Extension-specific configuration -------------------------------- @@ -118,9 +99,9 @@ Colorschemes ``name`` Name of the colorscheme. -``colors`` - .. _config-colorscheme-colors: +.. _config-colorscheme-colors: +``colors`` Color definitions, consisting of a dict where the key is the name of the color, and the value is one of the following: @@ -129,9 +110,9 @@ Colorschemes "aabbcc"]``). This is useful for colorschemes that use colors that aren't available in color terminals. -``groups`` - .. _config-colorscheme-groups: +.. _config-colorscheme-groups: +``groups`` Segment highlighting groups, consisting of a dict where the key is the name of the highlighting group (usually the function name for function segments), and the value is a dict that defines the foreground color, @@ -173,9 +154,9 @@ Themes ``name`` Name of the theme. -``default_module`` - .. _config-themes-default_module: +.. _config-themes-default_module: +``default_module`` Python module where segments will be looked by default. ``segments`` diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index 0f7967a8..0e3128e6 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -36,31 +36,10 @@ Plugin installation Font installation ================= -================== ============================= ====================== -Application/terminal emulator font support table ------------------------------------------------------------------------ -Name Patched font support Fontconfig support -================== ============================= ====================== -Gnome Terminal |supp_yes| |supp_yes| -Gvim |supp_yes| |supp_no| -Konsole |supp_yes| |supp_yes| -lxterminal |supp_yes| |supp_yes| -rxvt-unicode |supp_partial| [#]_ |supp_no| -st |supp_yes| |supp_yes| -Xfce Terminal |supp_yes| |supp_yes| -xterm |supp_yes| |supp_no| -================== ============================= ====================== - -.. |supp_yes| image:: ../_static/img/icons/tick.png -.. |supp_no| image:: ../_static/img/icons/cross.png -.. |supp_partial| image:: ../_static/img/icons/error.png - -.. [#] Must be compiled with ``--enable-unicode3`` for the - patched font to work. - Powerline provides two ways of installing the required fonts on Linux. The recommended method is using ``fontconfig`` if your terminal emulator -supports it (see the table above). +supports it. See the :ref:`term-feature-support-matrix` for details about +what features your terminal emulator supports. Fontconfig ---------- diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index 347e67be..55058744 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -41,22 +41,9 @@ command:: Font installation ================= -================== ============================= -Application/terminal emulator font support table ------------------------------------------------- -Name Patched font support -================== ============================= -iTerm2 |supp_yes| -Terminal.app |supp_yes| -================== ============================= - -.. |supp_yes| image:: ../_static/img/icons/tick.png -.. |supp_no| image:: ../_static/img/icons/cross.png -.. |supp_partial| image:: ../_static/img/icons/error.png - -.. note:: You need a patched font for Powerline to work on OS X. Check out - the `powerline-fonts`_ repository on GitHub for patched versions of some - popular programming fonts. +You need a patched font for Powerline to work on OS X. Check out the +`powerline-fonts`_ repository on GitHub for patched versions of some popular +programming fonts. 1. Download the font of your choice and install it by double-clicking the font file in Finder and then click :guilabel:`Install this font` in the diff --git a/docs/source/overview.rst b/docs/source/overview.rst index fa123229..397cbd2f 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -7,11 +7,6 @@ Requirements Powerline requires Python 3.3 or Python 2.7 to work. -Powerline uses several special glyphs to get the arrow effect and some -custom symbols for developers. This requires that you either have the symbol -font or a patched font on your system (details in installation -instructions). - Vim plugin requirements ----------------------- @@ -28,6 +23,44 @@ packages. Vim version 7.3.661 or newer is recommended for performance reasons. +Terminal emulator requirements +------------------------------ + +Powerline uses several special glyphs to get the arrow effect and some +custom symbols for developers. This requires that you either have a symbol +font or a patched font on your system. Your terminal emulator must also +support either patched fonts or fontconfig for Powerline to work properly. + +You can also enable :ref:`24-bit color support ` +if your terminal emulator supports it. + +.. table:: Application/terminal emulator feature support matrix + :name: term-feature-support-matrix + + ===================== ======= ===================== ===================== ===================== + Name OS Patched font support Fontconfig support 24-bit color support + ===================== ======= ===================== ===================== ===================== + Gnome Terminal Linux |i_yes| |i_yes| |i_no| + Gvim Linux |i_yes| |i_no| |i_yes| + iTerm2 OS X |i_yes| |i_no| |i_no| + Konsole Linux |i_yes| |i_yes| |i_yes| + lxterminal Linux |i_yes| |i_yes| |i_no| + MacVim OS X |i_yes| |i_no| |i_yes| + rxvt-unicode Linux |i_partial| [#]_ |i_no| |i_no| + st Linux |i_yes| |i_yes| |i_no| + Terminal.app OS X |i_yes| |i_no| |i_no| + Xfce Terminal Linux |i_yes| |i_yes| |i_no| + xterm Linux |i_yes| |i_no| |i_partial| [#]_ + ===================== ======= ===================== ===================== ===================== + +.. |i_yes| image:: _static/img/icons/tick.png +.. |i_no| image:: _static/img/icons/cross.png +.. |i_partial| image:: _static/img/icons/error.png + +.. [#] Must be compiled with ``--enable-unicode3`` for the + patched font to work. +.. [#] Uses nearest color from 8-bit palette. + Optional dependencies --------------------- From 186c99f95cd67c5d2456dbf1a358b7e4dbdc8703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 11:41:56 +0100 Subject: [PATCH 0279/1472] Add Solarized Dark colorscheme Closes #124. --- .../colorschemes/vim/solarized.json | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 powerline/config_files/colorschemes/vim/solarized.json diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json new file mode 100644 index 00000000..2cad7df0 --- /dev/null +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -0,0 +1,101 @@ +{ + "name": "Solarized Dark", + "colors": { + "base03": [8, "002b36"], + "base02": [0, "073642"], + "base01": [10, "586e75"], + "base00": [11, "657b83"], + "base0": [12, "839496"], + "base1": [14, "93a1a1"], + "base2": [7, "eee8d5"], + "base3": [15, "fdf6e3"], + "yellow": [3, "b58900"], + "orange": [9, "cb4b16"], + "red": [1, "dc322f"], + "magenta": [5, "d33682"], + "violet": [13, "6c71c4"], + "blue": [4, "268bd2"], + "cyan": [6, "2aa198"], + "green": [2, "719e07"] + }, + "groups": { + "background": { "fg": "base3", "bg": "base02" }, + "background:divider": { "fg": "base00", "bg": "base02" }, + "mode": { "fg": "base3", "bg": "green", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "base01", "attr": ["bold"] }, + "paste_indicator": { "fg": "base3", "bg": "orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "base01" }, + "branch": { "fg": "base2", "bg": "base01" }, + "branch:divider": { "fg": "base1", "bg": "base01" }, + "file_directory": { "fg": "base2", "bg": "base01" }, + "file_name": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, + "file_size": { "fg": "base3", "bg": "base01" }, + "file_name_no_file": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, + "file_name_empty": { "fg": "base3", "bg": "base01" }, + "file_format": { "fg": "base1", "bg": "base02" }, + "file_encoding": { "fg": "base1", "bg": "base02" }, + "file_type": { "fg": "base1", "bg": "base02" }, + "file_vcs_status": { "fg": "red", "bg": "base01" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "base01" }, + "file_vcs_status_A": { "fg": "green", "bg": "base01" }, + "line_percent": { "fg": "base3", "bg": "base00" }, + "line_current": { "fg": "base03", "bg": "base2", "attr": ["bold"] }, + "line_current_symbol": { "fg": "base03", "bg": "base2" }, + "col_current": { "fg": "base0", "bg": "base2" } + }, + "mode_translations": { + "nc": { + "colors": { + "base02": "base02", + "base01": "base02", + "base00": "base02", + "base0": "base01", + "base1": "base00", + "base2": "base0", + "base3": "base1" + } + }, + "i": { + "groups": { + "background": { "fg": "base3", "bg": "base01" }, + "background:divider": { "fg": "base2", "bg": "base01" }, + "mode": { "fg": "base3", "bg": "blue", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "base2", "attr": ["bold"] }, + "paste_indicator": { "fg": "base3", "bg": "orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "base2" }, + "branch": { "fg": "base01", "bg": "base2" }, + "branch:divider": { "fg": "base00", "bg": "base2" }, + "file_directory": { "fg": "base01", "bg": "base2" }, + "file_name": { "fg": "base02", "bg": "base2", "attr": ["bold"] }, + "file_size": { "fg": "base02", "bg": "base2" }, + "file_name_no_file": { "fg": "base02", "bg": "base2", "attr": ["bold"] }, + "file_name_empty": { "fg": "base02", "bg": "base2" }, + "file_format": { "fg": "base2", "bg": "base01" }, + "file_encoding": { "fg": "base2", "bg": "base01" }, + "file_type": { "fg": "base2", "bg": "base01" }, + "file_vcs_status": { "fg": "red", "bg": "base2" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "base2" }, + "file_vcs_status_A": { "fg": "green", "bg": "base2" }, + "line_percent": { "fg": "base3", "bg": "base1" }, + "line_current": { "fg": "base03", "bg": "base3", "attr": ["bold"] }, + "line_current_symbol": { "fg": "base03", "bg": "base3" }, + "col_current": { "fg": "base0", "bg": "base3" } + } + }, + "v": { + "groups": { + "mode": { "fg": "base3", "bg": "orange", "attr": ["bold"] } + } + }, + "V": { + "groups": { + "mode": { "fg": "base3", "bg": "orange", "attr": ["bold"] } + } + }, + "R": { + "groups": { + "mode": { "fg": "base3", "bg": "red", "attr": ["bold"] } + } + } + } +} From 147c96a2706a2dad41ceaa94c0f1db7a52a2dc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 11:50:26 +0100 Subject: [PATCH 0280/1472] Add Solarized Dark shell colorscheme --- .../colorschemes/shell/solarized.json | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 powerline/config_files/colorschemes/shell/solarized.json diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json new file mode 100644 index 00000000..f035afe5 --- /dev/null +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -0,0 +1,31 @@ +{ + "name": "Solarized Dark", + "colors": { + "base03": [8, "002b36"], + "base02": [0, "073642"], + "base01": [10, "586e75"], + "base00": [11, "657b83"], + "base0": [12, "839496"], + "base1": [14, "93a1a1"], + "base2": [7, "eee8d5"], + "base3": [15, "fdf6e3"], + "yellow": [3, "b58900"], + "orange": [9, "cb4b16"], + "red": [1, "dc322f"], + "magenta": [5, "d33682"], + "violet": [13, "6c71c4"], + "blue": [4, "268bd2"], + "cyan": [6, "2aa198"], + "green": [2, "719e07"] + }, + "groups": { + "user": { "fg": "base3", "bg": "blue", "attr": ["bold"] }, + "superuser": { "fg": "base3", "bg": "red", "attr": ["bold"] }, + "virtualenv": { "fg": "base3", "bg": "green" }, + "branch": { "fg": "base1", "bg": "base02" }, + "cwd": { "fg": "base2", "bg": "base01" }, + "cwd:current_folder": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, + "cwd:divider": { "fg": "base1", "bg": "base01" }, + "hostname": { "fg": "base3", "bg": "base01" } + } +} From 0dd3393bb3b6f35e2c28bc719f0fcc47cfe4b09a Mon Sep 17 00:00:00 2001 From: Sven Strothoff Date: Fri, 25 Jan 2013 13:38:34 +0100 Subject: [PATCH 0281/1472] Replace old line number character themes/vim/help.json still used the line number character from vim-powerline. --- powerline/config_files/themes/vim/help.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index 6c63be03..3a6ca70b 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -20,7 +20,7 @@ }, { "type": "string", - "contents": "⭡ ", + "contents": " ", "highlight_group": ["line_current_symbol", "line_current"] }, { From 1564e338b3ae42c549cd8fcc9d571f056cc9980a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 14:52:43 +0100 Subject: [PATCH 0282/1472] Remove percent placeholder from themes The vim renderer now handles searching and replacing of the percent placeholder internally so percent signs can be used directly in themes and segments instead of the percent placeholder. Refs #127. --- powerline/config_files/themes/vim/default.json | 2 +- powerline/renderers/vim.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index dbcfdd15..e4acc695 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -69,7 +69,7 @@ "name": "line_percent", "args": { "gradient": true }, "priority": 30, - "after": "", + "after": "%", "rjust": 4 }, { diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 93997a21..c15fa16a 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -15,7 +15,7 @@ vim_setwinvar = vim_get_func('setwinvar') class VimRenderer(Renderer): '''Powerline vim segment renderer.''' - PERCENT_PLACEHOLDER = u'' + _PERCENT_PLACEHOLDER = u'' def __init__(self, *args, **kwargs): super(VimRenderer, self).__init__(*args, **kwargs) @@ -34,13 +34,16 @@ class VimRenderer(Renderer): if current: mode = vim_mode() theme = self.get_theme() - segments = [segment for segment in theme.get_segments()] + segments = [] + for segment in theme.get_segments(): + segment['contents'] = segment['contents'].replace('%', self._PERCENT_PLACEHOLDER) + segments.append(segment) self.window_cache[window_id] = (theme, segments) else: mode = 'nc' theme, segments = self.window_cache.get(window_id, (None, None)) statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) - statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') + statusline = statusline.replace(self._PERCENT_PLACEHOLDER, '%%') return statusline def hl(self, fg=None, bg=None, attr=None): From 38195f490ec2468eb40b6a925a5043c51e9f0051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 25 Jan 2013 17:30:20 +0100 Subject: [PATCH 0283/1472] Handle KeyErrors in common weather segment If the query doesn't return valid weather information (e.g. if the location query is invalid) it will return an empty weather segment. Closes #128. --- powerline/segments/common.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5c24ef90..14363c92 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -140,8 +140,11 @@ def weather(unit='c', location_query=None): } url = 'http://query.yahooapis.com/v1/public/yql?' + _urllib_urlencode(query_data) response = json.loads(_urllib_read(url)) - condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] - condition_code = int(condition['code']) + try: + condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] + condition_code = int(condition['code']) + except KeyError: + return None icon = u'〇' for icon, codes in weather_conditions_codes.items(): if condition_code in codes: From d6da2c8d98a333d45de52f91adc576856977f4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 27 Jan 2013 17:40:02 +0100 Subject: [PATCH 0284/1472] Reset memoize cache if unable to unpickle This is usually due to the cache being stored with Python 3 and then attempted to being loaded in Python 2 which raises a ValueError. Closes #138. --- powerline/lib/memoize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 33be7d7d..00dbeef8 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -29,8 +29,8 @@ class memoize(object): try: with open(self.persistent_file, 'rb') as fileobj: self._cache = pickle.load(fileobj) - except (IOError, EOFError): - pass + except (IOError, EOFError, ValueError): + self._cache = {} cached = self._cache.get(key, None) if cached is None or time.time() - cached['time'] > self.timeout: cached = self._cache[key] = { From 98269a4bb59a28932e3cff0de175b6959e4d7200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 27 Jan 2013 17:42:11 +0100 Subject: [PATCH 0285/1472] Handle more KeyErrors in common weather segment --- powerline/segments/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 14363c92..217b59ee 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -138,12 +138,12 @@ def weather(unit='c', location_query=None): 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit), 'format': 'json' } - url = 'http://query.yahooapis.com/v1/public/yql?' + _urllib_urlencode(query_data) - response = json.loads(_urllib_read(url)) try: + url = 'http://query.yahooapis.com/v1/public/yql?' + _urllib_urlencode(query_data) + response = json.loads(_urllib_read(url)) condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) - except KeyError: + except (KeyError, ValueError): return None icon = u'〇' for icon, codes in weather_conditions_codes.items(): From 0b251425da85028c7933bf7b8b9fe323f71a856b Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 24 Jan 2013 22:36:46 +0400 Subject: [PATCH 0286/1472] Add IPython prompt support --- docs/source/overview.rst | 23 ++++++++++++++++ powerline/bindings/ipython/__init__.py | 0 powerline/bindings/ipython/post_0_11.py | 23 ++++++++++++++++ powerline/bindings/ipython/pre_0_11.py | 27 +++++++++++++++++++ .../colorschemes/ipython/default.json | 25 +++++++++++++++++ powerline/config_files/config.json | 4 +++ .../config_files/themes/ipython/default.json | 16 +++++++++++ powerline/renderers/ipython.py | 16 +++++++++++ 8 files changed, 134 insertions(+) create mode 100644 powerline/bindings/ipython/__init__.py create mode 100644 powerline/bindings/ipython/post_0_11.py create mode 100644 powerline/bindings/ipython/pre_0_11.py create mode 100644 powerline/config_files/colorschemes/ipython/default.json create mode 100644 powerline/config_files/themes/ipython/default.json create mode 100644 powerline/renderers/ipython.py diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 397cbd2f..58850b48 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -134,3 +134,26 @@ Add the following line to your :file:`tmux.conf`, where ``{path}`` is the absolute path to your Powerline installation directory:: source '{path}/powerline/bindings/tmux/powerline.conf' + +IPython prompt +^^^^^^^^^^^^^^ + +For IPython<0.11 add the following lines to your +:file:`.ipython/ipy_user_conf.py`:: + + # top + from powerline.bindings.ipython.pre_0_11 import setup as powerline_setup + + # main() function (assuming you launched ipython without configuration to + # create skeleton ipy_user_conf.py file): + powerline_setup() + +For IPython>=0.11 add the following line to your :file:`ipython_config.py` +file in the profile you are using:: + + c.InteractiveShellApp.extensions = [ + 'powerline.bindings.ipython.post_0_11' + ] + +IPython=0.11* is not supported and does not work. IPython<0.10 was not +tested (not installable by pip). diff --git a/powerline/bindings/ipython/__init__.py b/powerline/bindings/ipython/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py new file mode 100644 index 00000000..5901c63e --- /dev/null +++ b/powerline/bindings/ipython/post_0_11.py @@ -0,0 +1,23 @@ +from powerline.core import Powerline + +from IPython.core.prompts import PromptManager + + +class PowerlinePromptManager(PromptManager): + powerline = None + + def __init__(self, powerline, **kwargs): + self.powerline = powerline + super(PowerlinePromptManager, self).__init__(**kwargs) + + def _render(self, name, color=True, *args, **kwargs): + if name != 'in': + return super(PowerlinePromptManager, self)._render(name, color, *args, **kwargs) + return self.powerline.renderer.render(color=color) + + +def load_ipython_extension(ip): + powerline = Powerline('ipython') + + ip.prompt_manager = PowerlinePromptManager(powerline=powerline, + shell=ip.prompt_manager.shell, config=ip.prompt_manager.config) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py new file mode 100644 index 00000000..6bede602 --- /dev/null +++ b/powerline/bindings/ipython/pre_0_11.py @@ -0,0 +1,27 @@ +from powerline.core import Powerline +from IPython.Prompts import BasePrompt +from IPython.ipapi import get as get_ipython + + +class PowerlinePrompt(BasePrompt): + def __init__(self, powerline, *args, **kwargs): + self.powerline = powerline + super(PowerlinePrompt, self).__init__(*args, **kwargs) + + def set_p_str(self): + self.p_str = self.powerline.renderer.render() + self.p_str_nocolor = self.powerline.renderer.render(color=False) + + +def setup(prompt='1'): + ip = get_ipython() + + powerline = Powerline('ipython') + + attr = 'prompt' + prompt + + def late_startup_hook(): + old_prompt = getattr(ip.IP.outputcache, attr) + setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline, + old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left)) + ip.IP.hooks.late_startup_hook.add(late_startup_hook) diff --git a/powerline/config_files/colorschemes/ipython/default.json b/powerline/config_files/colorschemes/ipython/default.json new file mode 100644 index 00000000..3a9f7355 --- /dev/null +++ b/powerline/config_files/colorschemes/ipython/default.json @@ -0,0 +1,25 @@ +{ + "name": "Default color scheme for IPython prompt", + "colors": { + "black": 16, + "white": 231, + + "darkcyan": 74, + + "gray0": 233, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 252 + }, + "groups": { + "virtualenv": { "fg": "white", "bg": "darkcyan" }, + "prompt": { "fg": "gray9", "bg": "gray4" } + } +} diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 131ffc20..6b9873f9 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -27,6 +27,10 @@ "local_themes": { "help": "help" } + }, + "ipython": { + "colorscheme": "default", + "theme": "default" } } } diff --git a/powerline/config_files/themes/ipython/default.json b/powerline/config_files/themes/ipython/default.json new file mode 100644 index 00000000..95d82566 --- /dev/null +++ b/powerline/config_files/themes/ipython/default.json @@ -0,0 +1,16 @@ +{ + "default_module": "powerline.segments.common", + "segments": { + "left": [ + { + "name": "virtualenv" + }, + { + "type": "string", + "contents": "In:", + "highlight_group": ["prompt"], + "draw_divider": false + } + ] + } +} diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py new file mode 100644 index 00000000..8d32bdf1 --- /dev/null +++ b/powerline/renderers/ipython.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from powerline.renderers.shell import ShellRenderer + + +class IpythonRenderer(ShellRenderer): + '''Powerline ipython segment renderer.''' + def render(self, color=True, *args, **kwargs): + self.color = color + return super(IpythonRenderer, self).render(*args, **kwargs) + + def hl(self, *args, **kwargs): + if not self.color: + return '' + else: + return '\x01' + super(IpythonRenderer, self).hl(*args, **kwargs) + '\x02' From a42f9b6e2fbbe7fbdf33efe519e438b148ab9a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 06:36:52 +0100 Subject: [PATCH 0287/1472] Add output_raw argument to Renderer.render() method Update all ipython modules to use it to get unhighlighted prompt Conflicts: powerline/renderers/ipython.py --- powerline/bindings/ipython/post_0_11.py | 9 ++++++--- powerline/bindings/ipython/pre_0_11.py | 3 +-- powerline/renderer.py | 17 +++++++++++++---- powerline/renderers/ipython.py | 9 +-------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 5901c63e..439793d9 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -10,10 +10,13 @@ class PowerlinePromptManager(PromptManager): self.powerline = powerline super(PowerlinePromptManager, self).__init__(**kwargs) - def _render(self, name, color=True, *args, **kwargs): + def render(self, name, color=True, *args, **kwargs): if name != 'in': - return super(PowerlinePromptManager, self)._render(name, color, *args, **kwargs) - return self.powerline.renderer.render(color=color) + return super(PowerlinePromptManager, self).render(name, color, *args, **kwargs) + res, res_nocolor = self.powerline.renderer.render(output_raw=True) + self.txtwidth = len(res_nocolor) + self.width = self.txtwidth + return res if color else res_nocolor def load_ipython_extension(ip): diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 6bede602..36c1a315 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -9,8 +9,7 @@ class PowerlinePrompt(BasePrompt): super(PowerlinePrompt, self).__init__(*args, **kwargs) def set_p_str(self): - self.p_str = self.powerline.renderer.render() - self.p_str_nocolor = self.powerline.renderer.render(color=False) + self.p_str, self.p_str_nocolor = self.powerline.renderer.render(output_raw=True) def setup(prompt='1'): diff --git a/powerline/renderer.py b/powerline/renderer.py index d3483b57..11d2d66c 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -32,7 +32,14 @@ class Renderer(object): else: return self.theme - def render(self, mode=None, width=None, theme=None, segments=None, side=None): + @staticmethod + def _returned_value(rendered_highlighted, segments, output_raw): + if output_raw: + return rendered_highlighted, ''.join((segment['rendered_raw'] for segment in segments)) + else: + return rendered_highlighted + + def render(self, mode=None, width=None, theme=None, segments=None, side=None, output_raw=False): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -50,7 +57,7 @@ class Renderer(object): rendered_highlighted = self._render_segments(mode, theme, segments) if not width: # No width specified, so we don't need to crop or pad anything - return rendered_highlighted + return self._returned_value(rendered_highlighted, segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] @@ -70,7 +77,8 @@ class Renderer(object): segment['contents'] = segments_fillers_contents # Add remainder whitespace to the first filler segment segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder - return self._render_segments(mode, theme, segments) + + return self._returned_value(self._render_segments(mode, theme, segments), segments, output_raw) def _render_segments(self, mode, theme, segments, render_highlighted=True): '''Internal segment rendering method. @@ -133,7 +141,8 @@ class Renderer(object): rendered_highlighted += self.hl() return rendered_highlighted - def _total_len(self, segments): + @staticmethod + def _total_len(segments): '''Return total/rendered length of all segments. This method uses the rendered_raw property of the segments and requires diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 8d32bdf1..e8122093 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -5,12 +5,5 @@ from powerline.renderers.shell import ShellRenderer class IpythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' - def render(self, color=True, *args, **kwargs): - self.color = color - return super(IpythonRenderer, self).render(*args, **kwargs) - def hl(self, *args, **kwargs): - if not self.color: - return '' - else: - return '\x01' + super(IpythonRenderer, self).hl(*args, **kwargs) + '\x02' + return '\x01' + super(IpythonRenderer, self).hl(*args, **kwargs) + '\x02' From d20df5a38222a21c310f444e03732f1c2fdab69c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 25 Jan 2013 22:08:11 +0400 Subject: [PATCH 0288/1472] Remove placeholder and use escaping instead --- powerline/renderer.py | 14 +++++++++----- powerline/renderers/vim.py | 11 +++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 11d2d66c..2d630b45 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -117,25 +117,25 @@ class Renderer(object): segment_hl = self.hl(**segment['highlight'][mode]) if segment['type'] == 'filler': - rendered_highlighted += segment['contents'] or '' + rendered_highlighted += self.escape(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 + ' ' + rendered_highlighted += segment_hl + self.escape(outer_padding + segment['contents'] + ' ') + divider_hl + self.escape(divider + ' ') else: segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding - rendered_highlighted += ' ' + divider_hl + divider + segment_hl + ' ' + segment['contents'] + outer_padding + rendered_highlighted += self.escape(' ') + divider_hl + self.escape(divider) + segment_hl + self.escape(' ' + 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'] + rendered_highlighted += segment_hl + self.escape(outer_padding + segment['contents']) else: segment['rendered_raw'] += segment['contents'] + outer_padding - rendered_highlighted += segment_hl + segment['contents'] + outer_padding + rendered_highlighted += segment_hl + self.escape(segment['contents'] + outer_padding) else: raise ValueError('Unknown segment type') rendered_highlighted += self.hl() @@ -150,6 +150,10 @@ class Renderer(object): ''' return len(''.join([segment['rendered_raw'] for segment in segments])) + @staticmethod + def escape(string): + return string + @staticmethod def _int_to_rgb(int): r = (int >> 16) & 0xff diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index c15fa16a..18a0d709 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -15,7 +15,6 @@ vim_setwinvar = vim_get_func('setwinvar') class VimRenderer(Renderer): '''Powerline vim segment renderer.''' - _PERCENT_PLACEHOLDER = u'' def __init__(self, *args, **kwargs): super(VimRenderer, self).__init__(*args, **kwargs) @@ -34,18 +33,18 @@ class VimRenderer(Renderer): if current: mode = vim_mode() theme = self.get_theme() - segments = [] - for segment in theme.get_segments(): - segment['contents'] = segment['contents'].replace('%', self._PERCENT_PLACEHOLDER) - segments.append(segment) + segments = [segment for segment in theme.get_segments()] self.window_cache[window_id] = (theme, segments) else: mode = 'nc' theme, segments = self.window_cache.get(window_id, (None, None)) statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) - statusline = statusline.replace(self._PERCENT_PLACEHOLDER, '%%') return statusline + @staticmethod + def escape(string): + return string.replace('%', '%%') + def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment. From 99158e3ef2d9f37635badd517efc691bd0d731e7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 25 Jan 2013 22:25:11 +0400 Subject: [PATCH 0289/1472] Fix zsh prompt security and other issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Escape percent in zsh prompt * Prevent some security issues (directory named `$(echo abc)` appeared as `abc`) (`"$()"` → `'$()'`) * Removed exports --- powerline/bindings/zsh/powerline.zsh | 4 ++-- powerline/renderers/zsh_prompt.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index b3bf9c4d..91762443 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,6 +1,4 @@ _powerline_precmd() { - export PS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" left)" - export RPS1="$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" right)" _powerline_tmux_set_pwd } @@ -25,6 +23,8 @@ _powerline_install_precmd() { fi done precmd_functions+=(_powerline_precmd) + PS1='$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" left)' + RPS1='$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" right)' } trap "_powerline_tmux_set_columns" SIGWINCH diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index fe86c1bb..0a37cadb 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -5,10 +5,14 @@ from powerline.renderers.shell import ShellRenderer class ZshPromptRenderer(ShellRenderer): '''Powerline zsh prompt segment renderer.''' - def hl(self, fg=None, bg=None, attr=None): + def hl(self, *args, **kwargs): '''Highlight a segment. Returns the default ShellRenderer escape sequence with %{...%} wrapped around it (required in zsh prompts). ''' - return '%{' + super(ZshPromptRenderer, self).hl(fg, bg, attr) + '%}' + return '%{' + super(ZshPromptRenderer, self).hl(*args, **kwargs) + '%}' + + @staticmethod + def escape(string): + return string.replace('%', '%%') From 8dd5aeb09d4fd540b90ba4028b518b2814ff25b5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 25 Jan 2013 22:36:48 +0400 Subject: [PATCH 0290/1472] Remove "$()" method in pre-prompt hook Using "$()" method on pre-prompt hook is just asking for security trouble. --- powerline/bindings/bash/powerline.sh | 8 ++------ powerline/renderers/shell.py | 4 ++++ powerline/renderers/zsh_prompt.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index c24ca077..18757743 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -1,8 +1,3 @@ -_powerline_prompt_command() { - export PS1="$(powerline-prompt --last_exit_code=$? left)" - _powerline_tmux_set_pwd -} - _powerline_tmux_setenv() { if [[ -n "$TMUX" ]]; then tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" @@ -20,4 +15,5 @@ _powerline_tmux_set_columns() { trap "_powerline_tmux_set_columns" SIGWINCH kill -SIGWINCH "$$" -export PROMPT_COMMAND="_powerline_prompt_command" +export PROMPT_COMMAND="_powerline_tmux_set_pwd" +PS1='$(powerline-prompt --last_exit_code=$? left)' diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 14246998..3797429f 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -36,3 +36,7 @@ class ShellRenderer(Renderer): if attr & Renderer.ATTR_BOLD: ansi += [1] return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + + @staticmethod + def escape(string): + return string.replace('\\', '\\\\') diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 0a37cadb..f0d0e533 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -15,4 +15,4 @@ class ZshPromptRenderer(ShellRenderer): @staticmethod def escape(string): - return string.replace('%', '%%') + return string.replace('%', '%%').replace('\\', '\\\\') From 45882f95e827313414fa31a11808a8ad9910a23a Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 25 Jan 2013 22:38:36 +0400 Subject: [PATCH 0291/1472] Restore export as it makes sense in bash Bash has weird initialization: bashrc is sourced in non-login interactive shell, but not in login interactive shell; profile is sourced in login interactive shell, but not in non-login interactive, thus exporting in profile does make sense. In zsh case zshrc is always sourced when shell is interactive and exporting thus makes no sense. --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 18757743..8b3185f3 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -16,4 +16,4 @@ trap "_powerline_tmux_set_columns" SIGWINCH kill -SIGWINCH "$$" export PROMPT_COMMAND="_powerline_tmux_set_pwd" -PS1='$(powerline-prompt --last_exit_code=$? left)' +export PS1='$(powerline-prompt --last_exit_code=$? left)' From c56606405323318896d03e8315d83d90a3b6b6b3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 25 Jan 2013 22:41:07 +0400 Subject: [PATCH 0292/1472] Add setopt promptpercent and promptsubst These options are required for Powerline to work in zsh, but the latter is off by default. --- powerline/bindings/zsh/powerline.zsh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 91762443..4a70b205 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -23,6 +23,8 @@ _powerline_install_precmd() { fi done precmd_functions+=(_powerline_precmd) + setopt promptpercent + setopt promptsubst PS1='$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" left)' RPS1='$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" right)' } From 17639a9e259433eaad229446cfdb3d81732fbc24 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 26 Jan 2013 18:36:39 +0400 Subject: [PATCH 0293/1472] Add last_status and last_pipe_status segments --- .../config_files/colorschemes/shell/default.json | 4 +++- powerline/config_files/themes/shell/default.json | 5 +++++ .../themes/shell/default_leftonly.json | 5 +++++ powerline/core.py | 3 ++- powerline/segments/shell.py | 16 ++++++++++++++++ powerline/theme.py | 9 +++++++-- scripts/powerline-prompt | 2 +- 7 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 powerline/segments/shell.py diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 6c744f8a..57cff291 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -61,6 +61,8 @@ "cwd": { "fg": "gray9", "bg": "gray4" }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, "cwd:divider": { "fg": "gray7", "bg": "gray4" }, - "hostname": { "fg": "brightyellow", "bg": "mediumorange" } + "hostname": { "fg": "brightyellow", "bg": "mediumorange" }, + "exit_fail": { "fg": "white", "bg": "darkestred" }, + "exit_success": { "fg": "white", "bg": "darkestgreen" } } } diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 877365b6..29946684 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -25,6 +25,11 @@ } ], "right": [ + { + "module": "powerline.segments.shell", + "name": "last_pipe_status", + "highlight_group": "exit_fail" + }, { "name": "branch", "before": " " diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 33e8b7df..a81c9658 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -23,6 +23,11 @@ "dir_limit_depth": 3 }, "divider_highlight_group": "cwd:divider" + }, + { + "name": "last_status", + "highlight_group": ["exit_fail"], + "module": "powerline.segments.shell" } ] } diff --git a/powerline/core.py b/powerline/core.py index c8ec5b6e..1c588ade 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -11,7 +11,7 @@ from powerline.lib import underscore_to_camelcase class Powerline(object): - def __init__(self, ext, renderer_module=None): + def __init__(self, ext, renderer_module=None, segment_info=None): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) config_path = os.path.join(config_home, 'powerline') plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') @@ -35,6 +35,7 @@ class Powerline(object): 'ext': ext, 'colorscheme': colorscheme, 'common_config': self.config, + 'segment_info': segment_info, } local_themes = {} for key, local_theme_name in self.config_ext.get('local_themes', {}).items(): diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py new file mode 100644 index 00000000..59a33fc0 --- /dev/null +++ b/powerline/segments/shell.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + + +def last_status(segment_info): + return str(segment_info.last_exit_code) if segment_info.last_exit_code else None +last_status.requires_powerline_segment_info = True + + +def last_pipe_status(segment_info): + pipe_status = [int(status) for status in segment_info.last_pipe_status.split()] + if any(pipe_status): + return [{"contents": str(status), "highlight_group": "exit_fail" if status else "exit_success"} + for status in pipe_status] + else: + return None +last_pipe_status.requires_powerline_segment_info = True diff --git a/powerline/theme.py b/powerline/theme.py index d4d14a35..3bf22ea5 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -12,7 +12,7 @@ except NameError: class Theme(object): - def __init__(self, ext, colorscheme, theme_config, common_config): + def __init__(self, ext, colorscheme, theme_config, common_config, segment_info=None): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = { @@ -23,6 +23,7 @@ class Theme(object): 'contents': None, 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) } + self.segment_info = segment_info get_segment = Segment(ext, common_config['paths'], colorscheme, theme_config.get('default_module')).get for side in ['left', 'right']: self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) @@ -41,7 +42,11 @@ class Theme(object): parsed_segments = [] for segment in self.segments[side]: if segment['type'] == 'function': - contents = segment['contents_func'](**segment['args']) + if (hasattr(segment['contents_func'], 'requires_powerline_segment_info') + and segment['contents_func'].requires_powerline_segment_info): + contents = segment['contents_func'](segment_info=self.segment_info, **segment['args']) + else: + contents = segment['contents_func'](**segment['args']) if contents is None: continue if isinstance(contents, list): diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt index 6f125628..729430de 100755 --- a/scripts/powerline-prompt +++ b/scripts/powerline-prompt @@ -20,7 +20,7 @@ parser.add_argument('--last_pipe_status', default=None) if __name__ == '__main__': args = parser.parse_args() - powerline = Powerline(ext=args.ext, renderer_module=args.renderer_module) + powerline = Powerline(ext=args.ext, renderer_module=args.renderer_module, segment_info=args) rendered = powerline.renderer.render(side=args.side) try: sys.stdout.write(rendered) From 57876a87e9d35210979e6bdb16b9ce1fe1e435d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 06:54:38 +0100 Subject: [PATCH 0294/1472] Make highlight_group optional if set in segment function --- powerline/segment.py | 5 +---- powerline/theme.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index 9d634104..04202b5d 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -5,10 +5,9 @@ import sys class Segment(object): - def __init__(self, ext, path, colorscheme, default_module=None): + def __init__(self, ext, path, default_module=None): self.default_module = default_module or 'powerline.segments.{0}'.format(ext) self.path = path - self.colorscheme = colorscheme def get_function(self, segment): oldpath = sys.path @@ -40,9 +39,7 @@ class Segment(object): 'key': key, 'type': segment_type, 'highlight_group': highlight_group, - 'highlight': self.colorscheme.get_group_highlighting(highlight_group), 'divider_highlight_group': divider_highlight_group, - 'divider_highlight': self.colorscheme.get_group_highlighting(divider_highlight_group) if divider_highlight_group else None, 'before': segment.get('before', ''), 'after': segment.get('after', ''), 'contents_func': contents_func, diff --git a/powerline/theme.py b/powerline/theme.py index 3bf22ea5..f8516980 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -24,7 +24,7 @@ class Theme(object): 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) } self.segment_info = segment_info - get_segment = Segment(ext, common_config['paths'], colorscheme, theme_config.get('default_module')).get + get_segment = Segment(ext, common_config['paths'], theme_config.get('default_module')).get for side in ['left', 'right']: self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) @@ -32,6 +32,14 @@ class Theme(object): '''Return segment divider.''' return self.dividers[side][type] + def add_highlight(self, segment): + segment['highlight'] = self.colorscheme.get_group_highlighting(segment['highlight_group']) + if segment['divider_highlight_group']: + segment['divider_highlight'] = self.colorscheme.get_group_highlighting(segment['divider_highlight_group']) + else: + segment['divider_highlight'] = None + return segment + def get_segments(self, side=None): '''Return all segments. @@ -62,7 +70,7 @@ class Theme(object): else: continue for segment in parsed_segments: - segment['highlight'] = self.colorscheme.get_group_highlighting(segment['highlight_group']) + segment = self.add_highlight(segment) segment['contents'] = (segment['before'] + unicode(segment['contents']) + segment['after'])\ .ljust(segment['ljust'])\ .rjust(segment['rjust']) From 091400c84f4910713fd4efe1e4bc562c634359f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 07:09:22 +0100 Subject: [PATCH 0295/1472] Join tmux/prompt shell scripts into one common script --- powerline/bindings/bash/powerline.sh | 2 +- powerline/bindings/tmux/powerline.conf | 4 ++-- powerline/bindings/zsh/powerline.zsh | 4 ++-- scripts/{powerline-tmux => powerline} | 11 ++++++---- scripts/powerline-prompt | 28 -------------------------- setup.py | 3 +-- 6 files changed, 13 insertions(+), 39 deletions(-) rename scripts/{powerline-tmux => powerline} (54%) delete mode 100755 scripts/powerline-prompt diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 8b3185f3..4b6dbe0b 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -16,4 +16,4 @@ trap "_powerline_tmux_set_columns" SIGWINCH kill -SIGWINCH "$$" export PROMPT_COMMAND="_powerline_tmux_set_pwd" -export PS1='$(powerline-prompt --last_exit_code=$? left)' +export PS1='$(powerline shell left --last_exit_code=$?)' diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 03c15d61..5d43cd37 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -4,8 +4,8 @@ set -g status-interval 2 set -g status-fg colour231 set -g status-bg colour234 set -g status-left-length 20 -set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(powerline-tmux left)' -set -g status-right '#(powerline-tmux right)' +set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(powerline tmux left)' +set -g status-right '#(powerline tmux right)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[fg=colour249]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 4a70b205..4512a6f1 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -25,8 +25,8 @@ _powerline_install_precmd() { precmd_functions+=(_powerline_precmd) setopt promptpercent setopt promptsubst - PS1='$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" left)' - RPS1='$(powerline-prompt --renderer_module=zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus" right)' + PS1='$(powerline shell left -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + RPS1='$(powerline shell right -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' } trap "_powerline_tmux_set_columns" SIGWINCH diff --git a/scripts/powerline-tmux b/scripts/powerline similarity index 54% rename from scripts/powerline-tmux rename to scripts/powerline index e9dde783..01cc2dc8 100755 --- a/scripts/powerline-tmux +++ b/scripts/powerline @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -'''Powerline tmux statusline.''' +'''Powerline prompt and statusline script.''' import argparse import sys @@ -12,12 +12,15 @@ except ImportError: from powerline.core import Powerline # NOQA parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('side', nargs='?', default=None, choices=('left', 'right')) -parser.add_argument('--ext', default='tmux') +parser.add_argument('ext', nargs=1) +parser.add_argument('side', nargs='?', choices=('left', 'right')) +parser.add_argument('-r', '--renderer_module', choices=('shell', 'zsh_prompt', 'tmux')) +parser.add_argument('--last_exit_code', type=int, metavar='int') +parser.add_argument('--last_pipe_status', metavar='list') if __name__ == '__main__': args = parser.parse_args() - powerline = Powerline(args.ext) + powerline = Powerline(ext=args.ext[0], renderer_module=args.renderer_module, segment_info=args) rendered = powerline.renderer.render(side=args.side) try: sys.stdout.write(rendered) diff --git a/scripts/powerline-prompt b/scripts/powerline-prompt deleted file mode 100755 index 729430de..00000000 --- a/scripts/powerline-prompt +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -'''Powerline prompt script.''' -import argparse -import sys - -try: - from powerline.core import Powerline -except ImportError: - import os - sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from powerline.core import Powerline # NOQA - -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('side', nargs='?', default=None, choices=('left', 'right')) -parser.add_argument('--renderer_module', default='shell', choices=('shell', 'zsh_prompt')) -parser.add_argument('--ext', default='shell') -parser.add_argument('--last_exit_code', default=None, type=int) -parser.add_argument('--last_pipe_status', default=None) - -if __name__ == '__main__': - args = parser.parse_args() - powerline = Powerline(ext=args.ext, renderer_module=args.renderer_module, segment_info=args) - rendered = powerline.renderer.render(side=args.side) - try: - sys.stdout.write(rendered) - except UnicodeEncodeError: - sys.stdout.write(rendered.encode('utf-8')) diff --git a/setup.py b/setup.py index 15deb0f2..29101ea6 100755 --- a/setup.py +++ b/setup.py @@ -20,8 +20,7 @@ setup(name='Powerline', author_email='kim.silkebaekken+vim@gmail.com', url='https://github.com/Lokaltog/powerline', scripts=[ - 'scripts/powerline-prompt', - 'scripts/powerline-tmux', + 'scripts/powerline', ], keywords='', packages=find_packages(), From 7a1d7ecaf4a25d1fd56447ed0a9f5fab89fc26af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 07:40:42 +0100 Subject: [PATCH 0296/1472] Add width argument to shell script --- scripts/powerline | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/powerline b/scripts/powerline index 01cc2dc8..2fc4c956 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -15,13 +15,14 @@ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('ext', nargs=1) parser.add_argument('side', nargs='?', choices=('left', 'right')) parser.add_argument('-r', '--renderer_module', choices=('shell', 'zsh_prompt', 'tmux')) -parser.add_argument('--last_exit_code', type=int, metavar='int') -parser.add_argument('--last_pipe_status', metavar='list') +parser.add_argument('-w', '--width', type=int) +parser.add_argument('--last_exit_code', type=int, metavar='INT') +parser.add_argument('--last_pipe_status', metavar='LIST') if __name__ == '__main__': args = parser.parse_args() powerline = Powerline(ext=args.ext[0], renderer_module=args.renderer_module, segment_info=args) - rendered = powerline.renderer.render(side=args.side) + rendered = powerline.renderer.render(width=args.width, side=args.side) try: sys.stdout.write(rendered) except UnicodeEncodeError: From 1e4c3612ee8f105da5bba5c315e3450a7aa298f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 11:51:26 +0100 Subject: [PATCH 0297/1472] Pass segment contents to renderer hl() methods This allows the hl() methods to wrap highlighting code *around* the segment contents, this is required for e.g. Pango markup in Awesome statuslines (segments must be wrapped in ... tags). --- powerline/renderer.py | 58 +++++++++++++++++++++---------- powerline/renderers/shell.py | 4 +-- powerline/renderers/tmux.py | 4 +-- powerline/renderers/vim.py | 4 +-- powerline/renderers/zsh_prompt.py | 4 +-- 5 files changed, 47 insertions(+), 27 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 2d630b45..63702d60 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -105,39 +105,59 @@ class Renderer(object): segment['rendered_raw'] = u'' outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' - divider = theme.get_divider(segment['side'], divider_type) - divider_hl = '' - segment_hl = '' + divider_raw = theme.get_divider(segment['side'], divider_type) + divider_highlighted = '' + contents_raw = segment['contents'] + contents_highlighted = '' + + # Pad segments first + if segment['type'] == 'filler': + pass + elif segment['draw_divider'] or divider_type == 'hard': + if segment['side'] == 'left': + contents_raw = outer_padding + contents_raw + ' ' + divider_raw = divider_raw + ' ' + else: + contents_raw = ' ' + contents_raw + outer_padding + divider_raw = ' ' + divider_raw + elif contents_raw: + if segment['side'] == 'left': + contents_raw = outer_padding + contents_raw + else: + contents_raw = contents_raw + outer_padding + else: + raise ValueError('Unknown segment type') + + # Apply highlighting to padded dividers and contents if render_highlighted: if divider_type == 'soft' and segment['divider_highlight_group'] is not None: - divider_hl = self.hl(segment['divider_highlight'][mode]['fg'], segment['divider_highlight'][mode]['bg'], False) + divider_highlighted = self.hl(divider_raw, segment['divider_highlight'][mode]['fg'], segment['divider_highlight'][mode]['bg'], False) elif divider_type == 'hard': - divider_hl = self.hl(segment['highlight'][mode]['bg'], compare_segment['highlight'][mode]['bg'], False) - segment_hl = self.hl(**segment['highlight'][mode]) + divider_highlighted = self.hl(divider_raw, segment['highlight'][mode]['bg'], compare_segment['highlight'][mode]['bg'], False) + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'][mode]) + # Append padded raw and highlighted segments to the rendered segment variables if segment['type'] == 'filler': - rendered_highlighted += self.escape(segment['contents'] or '') + rendered_highlighted += contents_highlighted if contents_raw else '' 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 + self.escape(outer_padding + segment['contents'] + ' ') + divider_hl + self.escape(divider + ' ') + segment['rendered_raw'] += contents_raw + divider_raw + rendered_highlighted += contents_highlighted + divider_highlighted else: - segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding - rendered_highlighted += self.escape(' ') + divider_hl + self.escape(divider) + segment_hl + self.escape(' ' + segment['contents'] + outer_padding) - elif segment['contents']: + segment['rendered_raw'] += divider_raw + contents_raw + rendered_highlighted += divider_highlighted + contents_highlighted + elif contents_raw: # Segments without divider if segment['side'] == 'left': - segment['rendered_raw'] += outer_padding + segment['contents'] - rendered_highlighted += segment_hl + self.escape(outer_padding + segment['contents']) + segment['rendered_raw'] += contents_raw + rendered_highlighted += contents_highlighted else: - segment['rendered_raw'] += segment['contents'] + outer_padding - rendered_highlighted += segment_hl + self.escape(segment['contents'] + outer_padding) - else: - raise ValueError('Unknown segment type') + segment['rendered_raw'] += contents_raw + rendered_highlighted += contents_highlighted rendered_highlighted += self.hl() return rendered_highlighted @@ -161,5 +181,5 @@ class Renderer(object): b = int & 0xff return r, g, b - def hl(self, fg=None, bg=None, attr=None): + def hl(self, contents=None, fg=None, bg=None, attr=None): raise NotImplementedError diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 3797429f..d4708b34 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -5,7 +5,7 @@ from powerline.renderer import Renderer class ShellRenderer(Renderer): '''Powerline shell segment renderer.''' - def hl(self, fg=None, bg=None, attr=None): + def hl(self, contents=None, fg=None, bg=None, attr=None): '''Highlight a segment. If an argument is None, the argument is ignored. If an argument is @@ -35,7 +35,7 @@ class ShellRenderer(Renderer): else: if attr & Renderer.ATTR_BOLD: ansi += [1] - return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + (contents or u'') @staticmethod def escape(string): diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 1b2a93a2..df232a15 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -5,7 +5,7 @@ from powerline.renderer import Renderer class TmuxRenderer(Renderer): '''Powerline tmux segment renderer.''' - def hl(self, fg=None, bg=None, attr=None): + def hl(self, contents=None, fg=None, bg=None, attr=None): '''Highlight a segment.''' # We don't need to explicitly reset attributes, so skip those calls if not attr and not bg and not fg: @@ -37,4 +37,4 @@ class TmuxRenderer(Renderer): tmux_attr += ['underscore'] else: tmux_attr += ['nounderscore'] - return '#[' + ','.join(tmux_attr) + ']' + return '#[' + ','.join(tmux_attr) + ']' + (contents or u'') diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 18a0d709..9d33ca73 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -45,7 +45,7 @@ class VimRenderer(Renderer): def escape(string): return string.replace('%', '%%') - def hl(self, fg=None, bg=None, attr=None): + def hl(self, contents=None, fg=None, bg=None, attr=None): '''Highlight a segment. If an argument is None, the argument is ignored. If an argument is @@ -94,4 +94,4 @@ class VimRenderer(Renderer): 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'] + '#' + return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' + (contents or u'') diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index f0d0e533..6d292ccd 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -5,13 +5,13 @@ from powerline.renderers.shell import ShellRenderer class ZshPromptRenderer(ShellRenderer): '''Powerline zsh prompt segment renderer.''' - def hl(self, *args, **kwargs): + def hl(self, contents=None, fg=None, bg=None, attr=None): '''Highlight a segment. Returns the default ShellRenderer escape sequence with %{...%} wrapped around it (required in zsh prompts). ''' - return '%{' + super(ZshPromptRenderer, self).hl(*args, **kwargs) + '%}' + return '%{' + super(ZshPromptRenderer, self).hl(None, fg, bg, attr) + '%}' + (contents or u'') @staticmethod def escape(string): From bda7384aa74e4eaed22d5c391262820857b6f086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 12:30:11 +0100 Subject: [PATCH 0298/1472] Add Awesome WM widget --- docs/source/overview.rst | 22 ++++++++ powerline/bindings/awesome/powerline.lua | 11 ++++ powerline/bindings/awesome/powerline.sh | 10 ++++ .../colorschemes/awesome/default.json | 55 +++++++++++++++++++ powerline/config_files/config.json | 12 ++-- .../config_files/themes/awesome/default.json | 47 ++++++++++++++++ powerline/renderers/awesome.py | 28 ++++++++++ 7 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 powerline/bindings/awesome/powerline.lua create mode 100755 powerline/bindings/awesome/powerline.sh create mode 100644 powerline/config_files/colorschemes/awesome/default.json create mode 100644 powerline/config_files/themes/awesome/default.json create mode 100644 powerline/renderers/awesome.py diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 58850b48..ef930b56 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -157,3 +157,25 @@ file in the profile you are using:: IPython=0.11* is not supported and does not work. IPython<0.10 was not tested (not installable by pip). + +Awesome widget +-------------- + +.. note:: Powerline currently only supports awesome 3.5. + +.. note:: The Powerline widget will spawn a shell script that runs in the + background and updates the statusline with ``awesome-client``. + +Add the following to your :file:`rc.lua`, where ``{path}`` is the absolute +path to your Powerline installation directory: + +.. code-block:: lua + + package.path = package.path .. ';{path}/powerline/bindings/awesome/?.lua' + require('powerline') + +Then add the ``powerline_widget`` to your ``wibox``: + +.. code-block:: lua + + right_layout:add(powerline_widget) diff --git a/powerline/bindings/awesome/powerline.lua b/powerline/bindings/awesome/powerline.lua new file mode 100644 index 00000000..9424a2fc --- /dev/null +++ b/powerline/bindings/awesome/powerline.lua @@ -0,0 +1,11 @@ +local wibox = require('wibox') +local awful = require('awful') + +powerline_widget = wibox.widget.textbox() +powerline_widget:set_align('right') + +function powerline(mode, widget) end + +bindings_path = string.gsub(debug.getinfo(1).source:match('@(.*)$'), '/[^/]+$', '') +powerline_cmd = bindings_path .. '/powerline.sh' +awful.util.spawn_with_shell('ps ax -u $USER | grep "' .. powerline_cmd .. '" | grep -v grep || (' .. powerline_cmd .. ')') diff --git a/powerline/bindings/awesome/powerline.sh b/powerline/bindings/awesome/powerline.sh new file mode 100755 index 00000000..ece45fd4 --- /dev/null +++ b/powerline/bindings/awesome/powerline.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +SLEEP=2 +[[ "$1" != "" ]] && SLEEP="$1" + +while true; do + PL_AWESOME_RIGHT=$(powerline awesome right) + echo "powerline_widget:set_markup('$PL_AWESOME_RIGHT')" | awesome-client + sleep $SLEEP +done diff --git a/powerline/config_files/colorschemes/awesome/default.json b/powerline/config_files/colorschemes/awesome/default.json new file mode 100644 index 00000000..e4c96b02 --- /dev/null +++ b/powerline/config_files/colorschemes/awesome/default.json @@ -0,0 +1,55 @@ +{ + "name": "Default color scheme for Awesome WM", + "colors": { + "black": 16, + "white": 231, + + "darkestblue": 24, + "darkblue": 31, + "mediumblue": 38, + "brightblue": 117, + "brightestblue": 153, + + "gray0": 234, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 254, + + "system_load_good": 106, + "system_load_bad": 178, + "system_load_ugly": 202, + + "weather_temp_cold": 67, + "weather_temp_hot": 166, + "weather_condition_cold": 117, + "weather_condition_hot": 228 + }, + "groups": { + "background:divider": { "fg": "gray5", "bg": "gray0" }, + "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, + "date": { "fg": "gray8", "bg": "gray2" }, + "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, + "time:divider": { "fg": "gray5", "bg": "gray2" }, + "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, + "weather": { "fg": "gray8", "bg": "gray0" }, + "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, + "weather_temp_hot": { "fg": "weather_temp_hot", "bg": "gray0" }, + "weather_condition_☼": { "fg": "weather_condition_hot", "bg": "gray0" }, + "weather_condition_❅": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_condition_☔": { "fg": "weather_condition_cold", "bg": "gray0" }, + "uptime": { "fg": "gray8", "bg": "gray0" }, + "external_ip": { "fg": "gray8", "bg": "gray0" }, + "network_load": { "fg": "gray8", "bg": "gray0" }, + "system_load": { "fg": "gray8", "bg": "gray0" }, + "system_load_good": { "fg": "system_load_good", "bg": "gray0" }, + "system_load_bad": { "fg": "system_load_bad", "bg": "gray0" }, + "system_load_ugly": { "fg": "system_load_ugly", "bg": "gray0" } + } +} diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 6b9873f9..fca81b15 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -13,6 +13,14 @@ } }, "ext": { + "awesome": { + "colorscheme": "default", + "theme": "default" + }, + "ipython": { + "colorscheme": "default", + "theme": "default" + }, "shell": { "colorscheme": "default", "theme": "default" @@ -27,10 +35,6 @@ "local_themes": { "help": "help" } - }, - "ipython": { - "colorscheme": "default", - "theme": "default" } } } diff --git a/powerline/config_files/themes/awesome/default.json b/powerline/config_files/themes/awesome/default.json new file mode 100644 index 00000000..18d818d9 --- /dev/null +++ b/powerline/config_files/themes/awesome/default.json @@ -0,0 +1,47 @@ +{ + "default_module": "powerline.segments.common", + "segments": { + "right": [ + { + "name": "uptime", + "before": "⇑ ", + "priority": 50, + "divider_highlight_group": "background:divider" + }, + { + "name": "external_ip", + "before": "ⓦ ", + "priority": 50, + "divider_highlight_group": "background:divider" + }, + { + "name": "network_load", + "priority": 50, + "divider_highlight_group": "background:divider" + }, + { + "name": "system_load", + "priority": 50, + "divider_highlight_group": "background:divider" + }, + { + "name": "weather", + "priority": 50, + "divider_highlight_group": "background:divider" + }, + { + "name": "date" + }, + { + "name": "date", + "args": {"format": "%H:%M"}, + "before": "⌚ ", + "highlight_group": ["time", "date"], + "divider_highlight_group": "time:divider" + }, + { + "name": "hostname" + } + ] + } +} diff --git a/powerline/renderers/awesome.py b/powerline/renderers/awesome.py new file mode 100644 index 00000000..47a98a9a --- /dev/null +++ b/powerline/renderers/awesome.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from powerline.renderer import Renderer + + +class AwesomeRenderer(Renderer): + '''Powerline Awesome WM segment renderer.''' + + def hl(self, contents=None, fg=None, bg=None, attr=None): + '''Highlight a segment.''' + # We don't need to explicitly reset attributes, so skip those calls + if not contents or (not attr and not bg and not fg): + return '' + awesome_attr = [] + if fg is not None: + if fg is not False and fg[1] is not False: + awesome_attr += ['foreground="#{0:06x}"'.format(fg[1])] + if bg is not None: + if bg is not False and bg[1] is not False: + awesome_attr += ['background="#{0:06x}"'.format(bg[1])] + if attr is not None and attr is not False: + if attr & Renderer.ATTR_BOLD: + awesome_attr += ['font_weight="bold"'] + if attr & Renderer.ATTR_ITALIC: + awesome_attr += ['font_style="italic"'] + if attr & Renderer.ATTR_UNDERLINE: + awesome_attr += ['underline="single"'] + return '' + contents + '' From 8c003c2683a30566ee6fa296138cd0f627b67322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 13:55:36 +0100 Subject: [PATCH 0299/1472] Add e-mail alert segment --- .../colorschemes/awesome/default.json | 3 +++ .../colorschemes/tmux/default.json | 3 +++ .../config_files/themes/awesome/default.json | 9 +++++++++ .../config_files/themes/tmux/default.json | 9 +++++++++ powerline/segments/common.py | 20 +++++++++++++++++++ 5 files changed, 44 insertions(+) diff --git a/powerline/config_files/colorschemes/awesome/default.json b/powerline/config_files/colorschemes/awesome/default.json index e4c96b02..9e12df82 100644 --- a/powerline/config_files/colorschemes/awesome/default.json +++ b/powerline/config_files/colorschemes/awesome/default.json @@ -4,6 +4,8 @@ "black": 16, "white": 231, + "brightred": 160, + "darkestblue": 24, "darkblue": 31, "mediumblue": 38, @@ -37,6 +39,7 @@ "date": { "fg": "gray8", "bg": "gray2" }, "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, "time:divider": { "fg": "gray5", "bg": "gray2" }, + "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 831747f5..0491b021 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -4,6 +4,8 @@ "black": 16, "white": 231, + "brightred": 160, + "darkestblue": 24, "darkblue": 31, "mediumblue": 38, @@ -37,6 +39,7 @@ "date": { "fg": "gray8", "bg": "gray2" }, "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, "time:divider": { "fg": "gray5", "bg": "gray2" }, + "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, diff --git a/powerline/config_files/themes/awesome/default.json b/powerline/config_files/themes/awesome/default.json index 18d818d9..2c1ad292 100644 --- a/powerline/config_files/themes/awesome/default.json +++ b/powerline/config_files/themes/awesome/default.json @@ -39,6 +39,15 @@ "highlight_group": ["time", "date"], "divider_highlight_group": "time:divider" }, + { + "name": "email_imap_alert", + "before": "✉ ", + "priority": 10, + "args": { + "username": "", + "password": "" + } + }, { "name": "hostname" } diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 18d818d9..2c1ad292 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -39,6 +39,15 @@ "highlight_group": ["time", "date"], "divider_highlight_group": "time:divider" }, + { + "name": "email_imap_alert", + "before": "✉ ", + "priority": 10, + "args": { + "username": "", + "password": "" + } + }, { "name": "hostname" } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 217b59ee..ed94e65a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -229,3 +229,23 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref def virtualenv(): return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None + + +@memoize(60, persistent=True) +def email_imap_alert(username, password, server='imap.gmail.com', port=993, folder='INBOX'): + import imaplib + import re + + try: + mail = imaplib.IMAP4_SSL(server, port) + mail.login(username, password) + rc, message = mail.status(folder, "(UNSEEN)") + unread_count = re.search("UNSEEN (\d+)", message[0]).group(1) + except (imaplib.error, AttributeError): + return None + if not unread_count: + return None + return [{ + 'highlight_group': 'email_alert', + 'contents': unread_count, + }] From a3600158de61c6d257b04643eb76e543eb91f677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 13:55:45 +0100 Subject: [PATCH 0300/1472] Add a quick setup guide to the docs --- docs/source/configuration.rst | 82 ++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 7efd19d6..29bf5d4c 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -2,6 +2,10 @@ Configuration ************* +.. note:: **You DO NOT have to fork the main GitHub repo to personalize your + Powerline configuration!** Please read through the :ref:`quick-guide` for + a quick introduction to user configuration. + Powerline is configured with one main configuration file, and with separate configuration files for themes and colorschemes. All configuration files are written in JSON, with the exception of segment definitions, which are @@ -12,7 +16,7 @@ Powerline provides default configurations in the following locations: `Main configuration`_ :file:`powerline/config.json` `Colorschemes`_ - :file:`powerline/colorschemes/default.json` + :file:`powerline/colorschemes/{extension}/default.json` `Themes`_ :file:`powerline/themes/{extension}/default.json` @@ -21,21 +25,77 @@ configuration files are stored in :file:`$XDG_CONFIG_HOME/powerline` for Linux users, and in :file:`~/.config/powerline` for OS X users. This usually corresponds to :file:`~/.config/powerline` on both platforms. -The easiest way of creating your own version of any configuration file is to -copy the configuration file from the main package to the corresponding path -in your user-specific config directory and make your changes to the new -file. Example: +.. _quick-guide: + +Quick setup guide +================= + +This guide will help you with the initial configuration of Powerline. + +Start by copying the entire set of default configuration files to the +corresponding path in your user config directory: .. code-block:: sh - $ cp /path/to/powerline/colorschemes/default.json \ - ~/.config/powerline/colorschemes/mycolorscheme.json + mkdir ~/.config/powerline + cp -R /path/to/powerline/config_files/* ~/.config/powerline - $ vim ~/.config/powerline/colorschemes/mycolorscheme.json +Each extension (vim, tmux, etc.) has its own theme, and they are located in +:file:`{config directory}/themes/{extension}/default.json`. -.. note:: If you're creating a custom colorscheme or theme, remember to - rename it and update your main configuration to use the new - colorscheme/theme! +If you want to move, remove or customize any of the provided segments, you +can do that by updating the segment dictionary in the theme you want to +customize. A segment dictionary looks like this: + +.. code-block:: javascript + + { + "name": "segment_name" + ... + } + +You can move the segment dictionaries around to change the segment +positions, or remove the entire dictionary to remove the segment from the +prompt or statusline. + +.. note:: It's essential that the contents of all your configuration files + is valid JSON! It's strongly recommended that you run your configuration + files through ``jsonlint`` after changing them. + +Some segments need a user configuration to work properly. Here's a couple of +segments that you may want to customize right away: + +**E-mail alert segment** + You have to set your username and password (and possibly server/port) + for the e-mail alert segment. If you're using GMail it's recommended + that you `generate an application-specific password + `_ for this purpose. + + Open a theme file, scroll down to the ``email_imap_alert`` segment and + set your ``username`` and ``password``. The server defaults to GMail's + IMAP server, but you can set the server/port by adding a ``server`` and + a ``port`` argument. +**Weather segment** + The weather segment will try to find your location using a GeoIP lookup, + so unless you're on a VPN you probably won't have to change the location + query. + + If you want to change the location query or the temperature unit you'll + have to update the segment arguments. Open a theme file, scroll down to + the weather segment and update it to include unit/location query + arguments: + + .. code-block:: javascript + + { + "name": "weather", + "priority": 50, + "divider_highlight_group": "background:divider" + "args": { + "unit": "f", + "location_query": "oslo, norway" + } + }, Main configuration ================== From 3f2ce0a5708114f588135c7e27c98ce621dbb993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 14:02:03 +0100 Subject: [PATCH 0301/1472] Catch the correct imaplib exception --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ed94e65a..4183853a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -241,7 +241,7 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold mail.login(username, password) rc, message = mail.status(folder, "(UNSEEN)") unread_count = re.search("UNSEEN (\d+)", message[0]).group(1) - except (imaplib.error, AttributeError): + except (imaplib.IMAP4.error, AttributeError): return None if not unread_count: return None From f8d6f53a24f2f6cf953cc9e4c26edf4dbbca6031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 14:06:19 +0100 Subject: [PATCH 0302/1472] Don't check for mail if username or password is missing --- powerline/segments/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 4183853a..e27d5985 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -236,6 +236,8 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold import imaplib import re + if not username or not password: + return None try: mail = imaplib.IMAP4_SSL(server, port) mail.login(username, password) From 52a52c8eb6a88dff0c845a3cd45880112455c444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 14:09:51 +0100 Subject: [PATCH 0303/1472] Hide e-mail alert segment if there are no unseen e-mails --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e27d5985..429be576 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -242,7 +242,7 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold mail = imaplib.IMAP4_SSL(server, port) mail.login(username, password) rc, message = mail.status(folder, "(UNSEEN)") - unread_count = re.search("UNSEEN (\d+)", message[0]).group(1) + unread_count = int(re.search("UNSEEN (\d+)", message[0]).group(1)) except (imaplib.IMAP4.error, AttributeError): return None if not unread_count: From 5db6f47562b313074820532e390a96f8e03f6142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 17:28:00 +0100 Subject: [PATCH 0304/1472] Catch ImportErrors when importing renderer module --- powerline/core.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/core.py b/powerline/core.py index 1c588ade..89b8b5de 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -46,7 +46,11 @@ class Powerline(object): renderer_module_name = renderer_module or ext renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) - Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) + try: + Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) + except ImportError as e: + sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) + sys.exit(1) self.renderer = Renderer(theme_config, local_themes, theme_kwargs, term_24bit_colors=self.config.get('term_24bit_colors', False)) From 29793259e10a2815099cd2c81eca6c7d5108588a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 28 Jan 2013 17:29:02 +0100 Subject: [PATCH 0305/1472] Improve argument handling in powerline script Modules can now be any string, and an informative error message will be written to sys.stdout if the module doesn't exist. The `last_pipe_status` argument will also automatically be split into a list. --- powerline/segments/shell.py | 5 ++--- scripts/powerline | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 59a33fc0..5b45b02c 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -7,10 +7,9 @@ last_status.requires_powerline_segment_info = True def last_pipe_status(segment_info): - pipe_status = [int(status) for status in segment_info.last_pipe_status.split()] - if any(pipe_status): + if any(segment_info.last_pipe_status): return [{"contents": str(status), "highlight_group": "exit_fail" if status else "exit_success"} - for status in pipe_status] + for status in segment_info.last_pipe_status] else: return None last_pipe_status.requires_powerline_segment_info = True diff --git a/scripts/powerline b/scripts/powerline index 2fc4c956..fb2d4bc1 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -14,10 +14,10 @@ except ImportError: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('ext', nargs=1) parser.add_argument('side', nargs='?', choices=('left', 'right')) -parser.add_argument('-r', '--renderer_module', choices=('shell', 'zsh_prompt', 'tmux')) +parser.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) parser.add_argument('-w', '--width', type=int) -parser.add_argument('--last_exit_code', type=int, metavar='INT') -parser.add_argument('--last_pipe_status', metavar='LIST') +parser.add_argument('--last_exit_code', metavar='INT', type=int) +parser.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) if __name__ == '__main__': args = parser.parse_args() From 2f361711febaa822e4eb221f809b710cd650bf5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 29 Jan 2013 08:58:22 +0100 Subject: [PATCH 0306/1472] Add cmdwin statusline for vim --- powerline/bindings/vim/powerline.vim | 2 +- powerline/config_files/config.json | 1 + powerline/config_files/themes/vim/cmdwin.json | 15 +++++++++++++++ powerline/matchers/vim.py | 4 ++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 powerline/config_files/themes/vim/cmdwin.json diff --git a/powerline/bindings/vim/powerline.vim b/powerline/bindings/vim/powerline.vim index a180e520..c030596b 100644 --- a/powerline/bindings/vim/powerline.vim +++ b/powerline/bindings/vim/powerline.vim @@ -35,5 +35,5 @@ endfunction augroup Powerline autocmd! - autocmd BufEnter,BufWinEnter,WinEnter * call s:UpdateWindows() + autocmd BufEnter,BufWinEnter,WinEnter,CmdwinEnter * call s:UpdateWindows() augroup END diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index fca81b15..c6a6d35b 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -33,6 +33,7 @@ "colorscheme": "default", "theme": "default", "local_themes": { + "cmdwin": "cmdwin", "help": "help" } } diff --git a/powerline/config_files/themes/vim/cmdwin.json b/powerline/config_files/themes/vim/cmdwin.json new file mode 100644 index 00000000..fb0d1dff --- /dev/null +++ b/powerline/config_files/themes/vim/cmdwin.json @@ -0,0 +1,15 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "Command Line", + "highlight_group": ["file_name"] + }, + { + "type": "filler", + "highlight_group": ["background"] + } + ] + } +} diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index fbaee480..ee13de8f 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -7,3 +7,7 @@ import vim def help(): return bool(int(vim.eval('&buftype is# "help"'))) + + +def cmdwin(): + return bool(int(vim.eval('bufname("%") == "[Command Line]"'))) From 59c316cb34b2dc7251edb72c8c3639114dd961b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 29 Jan 2013 08:58:44 +0100 Subject: [PATCH 0307/1472] Remove percent placeholder from vim help theme --- powerline/config_files/themes/vim/help.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index 3a6ca70b..17bdfcc7 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -15,7 +15,7 @@ "name": "line_percent", "args": { "gradient": true }, "priority": 30, - "after": "", + "after": "%", "rjust": 4 }, { From b918057c40173fc4ebe40402feaff2a1710ee82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 29 Jan 2013 11:57:31 +0100 Subject: [PATCH 0308/1472] Use timedelta.total_seconds() to fetch seconds in uptime segment Closes #143. --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 429be576..932a2778 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -109,7 +109,7 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): try: import psutil from datetime import datetime - seconds = (datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).seconds + seconds = int((datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).total_seconds()) except ImportError: try: with open('/proc/uptime', 'r') as f: From 1f9741b8a286950939cb60cdd6ed201b26822769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 29 Jan 2013 13:47:13 +0100 Subject: [PATCH 0309/1472] Move awesome theme to generic WM theme --- powerline/bindings/awesome/powerline.sh | 2 +- .../colorschemes/{awesome => wm}/default.json | 2 +- powerline/config_files/config.json | 8 +++--- .../themes/{awesome => wm}/default.json | 25 ------------------- 4 files changed, 6 insertions(+), 31 deletions(-) rename powerline/config_files/colorschemes/{awesome => wm}/default.json (97%) rename powerline/config_files/themes/{awesome => wm}/default.json (51%) diff --git a/powerline/bindings/awesome/powerline.sh b/powerline/bindings/awesome/powerline.sh index ece45fd4..2eed3fd2 100755 --- a/powerline/bindings/awesome/powerline.sh +++ b/powerline/bindings/awesome/powerline.sh @@ -4,7 +4,7 @@ SLEEP=2 [[ "$1" != "" ]] && SLEEP="$1" while true; do - PL_AWESOME_RIGHT=$(powerline awesome right) + PL_AWESOME_RIGHT=$(powerline wm right -r awesome) echo "powerline_widget:set_markup('$PL_AWESOME_RIGHT')" | awesome-client sleep $SLEEP done diff --git a/powerline/config_files/colorschemes/awesome/default.json b/powerline/config_files/colorschemes/wm/default.json similarity index 97% rename from powerline/config_files/colorschemes/awesome/default.json rename to powerline/config_files/colorschemes/wm/default.json index 9e12df82..bd3f3879 100644 --- a/powerline/config_files/colorschemes/awesome/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -1,5 +1,5 @@ { - "name": "Default color scheme for Awesome WM", + "name": "Default color scheme for window managers", "colors": { "black": 16, "white": 231, diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index c6a6d35b..93ac8b4c 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -13,10 +13,6 @@ } }, "ext": { - "awesome": { - "colorscheme": "default", - "theme": "default" - }, "ipython": { "colorscheme": "default", "theme": "default" @@ -36,6 +32,10 @@ "cmdwin": "cmdwin", "help": "help" } + }, + "wm": { + "colorscheme": "default", + "theme": "default" } } } diff --git a/powerline/config_files/themes/awesome/default.json b/powerline/config_files/themes/wm/default.json similarity index 51% rename from powerline/config_files/themes/awesome/default.json rename to powerline/config_files/themes/wm/default.json index 2c1ad292..dd834975 100644 --- a/powerline/config_files/themes/awesome/default.json +++ b/powerline/config_files/themes/wm/default.json @@ -2,28 +2,6 @@ "default_module": "powerline.segments.common", "segments": { "right": [ - { - "name": "uptime", - "before": "⇑ ", - "priority": 50, - "divider_highlight_group": "background:divider" - }, - { - "name": "external_ip", - "before": "ⓦ ", - "priority": 50, - "divider_highlight_group": "background:divider" - }, - { - "name": "network_load", - "priority": 50, - "divider_highlight_group": "background:divider" - }, - { - "name": "system_load", - "priority": 50, - "divider_highlight_group": "background:divider" - }, { "name": "weather", "priority": 50, @@ -47,9 +25,6 @@ "username": "", "password": "" } - }, - { - "name": "hostname" } ] } From fb7a0d46e60a0d021d26143689beb7c946ea1f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 30 Jan 2013 09:41:09 +0100 Subject: [PATCH 0310/1472] Rename AwesomeRenderer to PangoMarkupRenderer Awesome WM uses the Pango Text Attribute Markup Language (http://developer.gnome.org/pango/stable/PangoMarkupFormat.html), and since other applications and window managers may use the same markup language it's better to have a common renderer for this markup language. --- powerline/bindings/awesome/powerline.sh | 2 +- powerline/renderers/{awesome.py => pango_markup.py} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename powerline/renderers/{awesome.py => pango_markup.py} (91%) diff --git a/powerline/bindings/awesome/powerline.sh b/powerline/bindings/awesome/powerline.sh index 2eed3fd2..d068b958 100755 --- a/powerline/bindings/awesome/powerline.sh +++ b/powerline/bindings/awesome/powerline.sh @@ -4,7 +4,7 @@ SLEEP=2 [[ "$1" != "" ]] && SLEEP="$1" while true; do - PL_AWESOME_RIGHT=$(powerline wm right -r awesome) + PL_AWESOME_RIGHT=$(powerline wm right -r pango_markup) echo "powerline_widget:set_markup('$PL_AWESOME_RIGHT')" | awesome-client sleep $SLEEP done diff --git a/powerline/renderers/awesome.py b/powerline/renderers/pango_markup.py similarity index 91% rename from powerline/renderers/awesome.py rename to powerline/renderers/pango_markup.py index 47a98a9a..b49cc618 100644 --- a/powerline/renderers/awesome.py +++ b/powerline/renderers/pango_markup.py @@ -3,8 +3,8 @@ from powerline.renderer import Renderer -class AwesomeRenderer(Renderer): - '''Powerline Awesome WM segment renderer.''' +class PangoMarkupRenderer(Renderer): + '''Powerline Pango markup segment renderer.''' def hl(self, contents=None, fg=None, bg=None, attr=None): '''Highlight a segment.''' From cc9b0daefc413e441cf89dc3b1d750b17c7ea190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 30 Jan 2013 09:44:19 +0100 Subject: [PATCH 0311/1472] Pad powerline segments with no-break spaces Some applications using Pango and/or Cairo draw square unknown character glyphs next to divider glyphs instead of regular spaces. Non-breaking spaces resolves this issue, and they are probably more correct to use (no application should break lines at the padding spaces since they "glue" the segment and dividers together as one unit). This commit appears to work fine in all supported applications on Linux. This commit resolves the gvim font rendering problems mentioned in issue #113. --- powerline/renderer.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 63702d60..5f5cee9c 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -11,6 +11,8 @@ class Renderer(object): TERM_24BIT_COLORS = False + PADDING_CHAR = u'\u00a0' # No-break space + def __init__(self, theme_config, local_themes, theme_kwargs, term_24bit_colors=False): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes @@ -72,11 +74,11 @@ class Renderer(object): segments_fillers = [segment for segment in segments if segment['type'] == 'filler'] if segments_fillers: segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len(segments)), len(segments_fillers)) - segments_fillers_contents = ' ' * segments_fillers_len + segments_fillers_contents = self.PADDING_CHAR * 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 + segments_fillers[0]['contents'] += self.PADDING_CHAR * segments_fillers_remainder return self._returned_value(self._render_segments(mode, theme, segments), segments, output_raw) @@ -103,7 +105,7 @@ class Renderer(object): next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT compare_segment = next_segment if segment['side'] == 'left' else prev_segment segment['rendered_raw'] = u'' - outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' + outer_padding = self.PADDING_CHAR if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' divider_raw = theme.get_divider(segment['side'], divider_type) @@ -116,11 +118,11 @@ class Renderer(object): pass elif segment['draw_divider'] or divider_type == 'hard': if segment['side'] == 'left': - contents_raw = outer_padding + contents_raw + ' ' - divider_raw = divider_raw + ' ' + contents_raw = outer_padding + contents_raw + self.PADDING_CHAR + divider_raw = divider_raw + self.PADDING_CHAR else: - contents_raw = ' ' + contents_raw + outer_padding - divider_raw = ' ' + divider_raw + contents_raw = self.PADDING_CHAR + contents_raw + outer_padding + divider_raw = self.PADDING_CHAR + divider_raw elif contents_raw: if segment['side'] == 'left': contents_raw = outer_padding + contents_raw From fb929b76cc70094741228928e09faa19984a5557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 30 Jan 2013 09:52:22 +0100 Subject: [PATCH 0312/1472] Fix vim matcher string comparison Refs 2f361711febaa822e4eb221f809b710cd650bf5b. --- powerline/matchers/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index ee13de8f..7a971da6 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -10,4 +10,4 @@ def help(): def cmdwin(): - return bool(int(vim.eval('bufname("%") == "[Command Line]"'))) + return bool(int(vim.eval('bufname("%") is# "[Command Line]"'))) From 597f67b85ad72344f36902b2cb3bd4cc78d97d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 30 Jan 2013 10:35:01 +0100 Subject: [PATCH 0313/1472] Add Qtile widget --- docs/source/overview.rst | 20 +++++++++++++++++ powerline/bindings/qtile/__init__.py | 0 powerline/bindings/qtile/widget.py | 33 ++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 powerline/bindings/qtile/__init__.py create mode 100644 powerline/bindings/qtile/widget.py diff --git a/docs/source/overview.rst b/docs/source/overview.rst index ef930b56..95014013 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -179,3 +179,23 @@ Then add the ``powerline_widget`` to your ``wibox``: .. code-block:: lua right_layout:add(powerline_widget) + +Qtile widget +------------ + +Add the following to your :file:`~/.config/qtile/config.py`: + +.. code-block:: python + + from powerline.bindings.qtile.widget import Powerline + + screens = [ + Screen( + top=bar.Bar([ + # ... + Powerline(timeout=2), + # ... + ], + ), + ), + ] diff --git a/powerline/bindings/qtile/__init__.py b/powerline/bindings/qtile/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py new file mode 100644 index 00000000..c241793c --- /dev/null +++ b/powerline/bindings/qtile/widget.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +from libqtile import bar +from libqtile.widget import base + +from powerline.core import Powerline as PowerlineCore + + +class Powerline(base._TextBox): + def __init__(self, timeout=2, text=" ", width=bar.CALCULATED, **config): + base._TextBox.__init__(self, text, width, **config) + self.timeout_add(timeout, self.update) + self.powerline = PowerlineCore(ext='wm', renderer_module='pango_markup') + + def update(self): + self.text = self.powerline.renderer.render(side='right') + self.bar.draw() + + def cmd_update(self, text): + self.update(text) + + def cmd_get(self): + return self.text + + def _configure(self, qtile, bar): + base._TextBox._configure(self, qtile, bar) + self.layout = self.drawer.textlayout( + self.text, + self.foreground, + self.font, + self.fontsize, + self.fontshadow, + markup=True) From 7022b47ed984d11474b1aa5721099620cc20956c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 30 Jan 2013 10:53:28 +0100 Subject: [PATCH 0314/1472] Fix updating issue with Qtile widget --- powerline/bindings/qtile/widget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index c241793c..c9ca5751 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -13,8 +13,11 @@ class Powerline(base._TextBox): self.powerline = PowerlineCore(ext='wm', renderer_module='pango_markup') def update(self): + if not self.configured: + return True self.text = self.powerline.renderer.render(side='right') self.bar.draw() + return True def cmd_update(self, text): self.update(text) From fd5cd921017f9eeecb6750d30a56d325181f44b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 31 Jan 2013 07:51:09 +0100 Subject: [PATCH 0315/1472] Fix rendering of dividers without custom highlight group --- powerline/renderer.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 5f5cee9c..688b84ae 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -133,10 +133,14 @@ class Renderer(object): # Apply highlighting to padded dividers and contents if render_highlighted: - if divider_type == 'soft' and segment['divider_highlight_group'] is not None: - divider_highlighted = self.hl(divider_raw, segment['divider_highlight'][mode]['fg'], segment['divider_highlight'][mode]['bg'], False) - elif divider_type == 'hard': - divider_highlighted = self.hl(divider_raw, segment['highlight'][mode]['bg'], compare_segment['highlight'][mode]['bg'], False) + if divider_type == 'soft': + divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' + divider_fg = segment[divider_highlight_group_key][mode]['fg'] + divider_bg = segment[divider_highlight_group_key][mode]['bg'] + else: + divider_fg = segment['highlight'][mode]['bg'] + divider_bg = compare_segment['highlight'][mode]['bg'] + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'][mode]) # Append padded raw and highlighted segments to the rendered segment variables From 7fb67e735098df3ad05b580bd34a2a1e6daaa337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 31 Jan 2013 10:12:05 +0100 Subject: [PATCH 0316/1472] Create now playing segment The segment currently provides info from mpd or Spotify. --- powerline/segments/common.py | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 932a2778..2450391c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os +import sys from powerline.lib import memoize @@ -251,3 +252,100 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold 'highlight_group': 'email_alert', 'contents': unread_count, }] + + +class NowPlayingSegment(object): + STATE_SYMBOLS = { + 'fallback': u'♫', + 'play': u'▶', + 'pause': u'▮▮', + 'stop': u'■', + } + + def __call__(self, player='mpd', format=u'{state_symbol} {artist} - {title} ({total})', *args, **kwargs): + update_func = getattr(self, 'player_{0}'.format(player)) + self.now_playing = { + 'state': None, + 'state_symbol': self.STATE_SYMBOLS['fallback'], + 'album': None, + 'artist': None, + 'title': None, + 'elapsed': None, + 'total': None, + } + updated = update_func(*args, **kwargs) + if not updated: + return None + return format.format(**self.now_playing) + + @staticmethod + def _run_cmd(cmd): + from subprocess import Popen, PIPE + try: + p = Popen(cmd, stdout=PIPE) + stdout, err = p.communicate() + except OSError as e: + sys.stderr.write('Could not execute command ({0}): {1}\n'.format(e, cmd)) + return None + return stdout.strip() + + def player_mpd(self, host='localhost', port=6600): + try: + import mpd + client = mpd.MPDClient() + client.connect(host, port) + now_playing = client.currentsong() + if not now_playing: + return + status = client.status() + client.close() + client.disconnect() + self.now_playing.update({ + 'state': status.get('state'), + 'state_symbol': self.STATE_SYMBOLS.get(status.get('state')), + 'album': now_playing.get('album'), + 'artist': now_playing.get('artist'), + 'title': now_playing.get('title'), + 'elapsed': '{0:.0f}:{1:02.0f}'.format(*divmod(float(status.get('elapsed', 0)), 60)), + 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(now_playing['time']), 60)), + }) + except ImportError: + now_playing = self._run_cmd(['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) + if not now_playing: + return + now_playing = now_playing.split('\n') + self.now_playing.update({ + 'album': now_playing[0], + 'artist': now_playing[1], + 'title': now_playing[2], + 'total': now_playing[3], + }) + return True + + def player_spotify(self): + try: + import dbus + except ImportError: + sys.stderr.write('Could not add Spotify segment: Requires python-dbus.\n') + return + bus = dbus.SessionBus() + DBUS_IFACE_PROPERTIES = 'org.freedesktop.DBus.Properties' + DBUS_IFACE_PLAYER = 'org.freedesktop.MediaPlayer2' + try: + player = bus.get_object('com.spotify.qt', '/') + iface = dbus.Interface(player, DBUS_IFACE_PROPERTIES) + info = iface.Get(DBUS_IFACE_PLAYER, 'Metadata') + state = iface.Get(DBUS_IFACE_PLAYER, 'PlaybackStatus') + except dbus.exceptions.DBusException: + return + state = {'Playing': 'play', 'Paused': 'pause'}.get(state, None) + self.now_playing.update({ + 'state': state, + 'state_symbol': self.STATE_SYMBOLS.get(state), + 'album': str(info['xesam:album']), + 'artist': str(info['xesam:artist'][0]), + 'title': str(info['xesam:title']), + 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(info['mpris:length'] / 1e6), 60)), + }) + return True +now_playing = NowPlayingSegment() From 91c143bdd0caa6770c5ab50a59d5ae265d48691e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 31 Jan 2013 10:34:23 +0100 Subject: [PATCH 0317/1472] Update Arch Linux PKGBUILDs --- .../archlinux/python-powerline-git/PKGBUILD | 23 +++++++++++-------- .../archlinux/python2-powerline-git/PKGBUILD | 21 +++++++++-------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index cdf78f1e..7e02ee6d 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -1,7 +1,8 @@ # Maintainer: Kim Silkebækken +pkgbase=python-powerline pkgname=python-powerline-git -pkgver=20130124 +pkgver=20130131 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -21,24 +22,26 @@ _gitname="powerline" _gitbranch="develop" build() { - cd ${srcdir} + cd "${srcdir}" msg "Connecting to GitHub..." - if [ -d ${srcdir}/${_gitname} ]; then - cd ${_gitname} - git pull origin ${_gitbranch} + if [ -d "${srcdir}/${_gitname}" ]; then + cd "${_gitname}" + git pull origin "${_gitbranch}" msg "The local files are updated." else - git clone ${_gitroot} - cd ${_gitname} - git checkout ${_gitbranch} + git clone "${_gitroot}" + cd "${_gitname}" + git checkout "${_gitbranch}" fi msg "Git checkout done or server timeout." +} - python setup.py build || return 1 - python setup.py install --root=${pkgdir} || return 1 +package() { + cd "${srcdir}/${_gitname}" + python setup.py install --root="${pkgdir}" --optimize=1 || return 1 msg2 "Installing fonts..." install -dm755 "${pkgdir}/usr/share/fonts/TTF/" diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 386fafbc..b78fbe79 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,5 +1,6 @@ # Maintainer: Kim Silkebækken +pkgbase=python-powerline pkgname=python2-powerline-git pkgver=20130124 pkgrel=1 @@ -22,24 +23,26 @@ _gitname="powerline" _gitbranch="develop" build() { - cd ${srcdir} + cd "${srcdir}" msg "Connecting to GitHub..." - if [ -d ${srcdir}/${_gitname} ]; then - cd ${_gitname} - git pull origin ${_gitbranch} + if [ -d "${srcdir}/${_gitname}" ]; then + cd "${_gitname}" + git pull origin "${_gitbranch}" msg "The local files are updated." else - git clone ${_gitroot} - cd ${_gitname} - git checkout ${_gitbranch} + git clone "${_gitroot}" + cd "${_gitname}" + git checkout "${_gitbranch}" fi msg "Git checkout done or server timeout." +} - python2 setup.py build || return 1 - python2 setup.py install --root=${pkgdir} || return 1 +package() { + cd "${srcdir}/${_gitname}" + python2 setup.py install --root="${pkgdir}" --optimize=1 || return 1 msg2 "Installing fonts..." install -dm755 "${pkgdir}/usr/share/fonts/TTF/" From 5f9b8d51723b6e15546b3dae9eec09a0117a7ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 31 Jan 2013 10:34:40 +0100 Subject: [PATCH 0318/1472] Swap quotes in email_alert segment --- powerline/segments/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 2450391c..7ea1ef74 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -242,8 +242,8 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold try: mail = imaplib.IMAP4_SSL(server, port) mail.login(username, password) - rc, message = mail.status(folder, "(UNSEEN)") - unread_count = int(re.search("UNSEEN (\d+)", message[0]).group(1)) + rc, message = mail.status(folder, '(UNSEEN)') + unread_count = int(re.search('UNSEEN (\d+)', message[0]).group(1)) except (imaplib.IMAP4.error, AttributeError): return None if not unread_count: From 0ad417cc793c1e20e3874fbd0c8e06221ade9906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 31 Jan 2013 10:53:33 +0100 Subject: [PATCH 0319/1472] Refactor now_playing segment slightly --- powerline/segments/common.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 7ea1ef74..f44b07b3 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -263,8 +263,8 @@ class NowPlayingSegment(object): } def __call__(self, player='mpd', format=u'{state_symbol} {artist} - {title} ({total})', *args, **kwargs): - update_func = getattr(self, 'player_{0}'.format(player)) - self.now_playing = { + player_func = getattr(self, 'player_{0}'.format(player)) + stats = { 'state': None, 'state_symbol': self.STATE_SYMBOLS['fallback'], 'album': None, @@ -273,10 +273,11 @@ class NowPlayingSegment(object): 'elapsed': None, 'total': None, } - updated = update_func(*args, **kwargs) - if not updated: + func_stats = player_func(*args, **kwargs) + if not func_stats: return None - return format.format(**self.now_playing) + stats.update(func_stats) + return format.format(**stats) @staticmethod def _run_cmd(cmd): @@ -300,7 +301,7 @@ class NowPlayingSegment(object): status = client.status() client.close() client.disconnect() - self.now_playing.update({ + return { 'state': status.get('state'), 'state_symbol': self.STATE_SYMBOLS.get(status.get('state')), 'album': now_playing.get('album'), @@ -308,19 +309,18 @@ class NowPlayingSegment(object): 'title': now_playing.get('title'), 'elapsed': '{0:.0f}:{1:02.0f}'.format(*divmod(float(status.get('elapsed', 0)), 60)), 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(now_playing['time']), 60)), - }) + } except ImportError: now_playing = self._run_cmd(['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) if not now_playing: return now_playing = now_playing.split('\n') - self.now_playing.update({ + return { 'album': now_playing[0], 'artist': now_playing[1], 'title': now_playing[2], 'total': now_playing[3], - }) - return True + } def player_spotify(self): try: @@ -339,13 +339,12 @@ class NowPlayingSegment(object): except dbus.exceptions.DBusException: return state = {'Playing': 'play', 'Paused': 'pause'}.get(state, None) - self.now_playing.update({ + return { 'state': state, 'state_symbol': self.STATE_SYMBOLS.get(state), 'album': str(info['xesam:album']), 'artist': str(info['xesam:artist'][0]), 'title': str(info['xesam:title']), 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(info['mpris:length'] / 1e6), 60)), - }) - return True + } now_playing = NowPlayingSegment() From cb860ce5d01eedb939b4802224e7780add16c1ce Mon Sep 17 00:00:00 2001 From: Gaurav Narula Date: Thu, 31 Jan 2013 17:55:18 +0530 Subject: [PATCH 0320/1472] Add Rhythmbox to now_playing segment Closes #155. --- powerline/segments/common.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f44b07b3..5598b956 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -347,4 +347,17 @@ class NowPlayingSegment(object): 'title': str(info['xesam:title']), 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(info['mpris:length'] / 1e6), 60)), } + + def player_rhythmbox(self): + now_playing = self._run_cmd(['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) + if not now_playing: + return + now_playing = now_playing.split('\n') + return { + 'album': now_playing[0], + 'artist': now_playing[1], + 'title': now_playing[2], + 'elapsed': now_playing[3], + 'total': now_playing[4], + } now_playing = NowPlayingSegment() From bfdb7f8028dc6bd4b9543f43bcd6f67962b871d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 16:06:43 +0100 Subject: [PATCH 0321/1472] Refactor segment rendering This commit introduces the following changes to themes and segment rendering: - Spacer segments are now regular string/function type segments with "width": "auto" in the themes. - The "rjust"/"ljust" properties have been replaced by the "width" option combined with a new "align" option. - Renderer._render_segments() is now a generator which renders each segment separately, and assigns the rendered contents to "_rendered_hl" and "_rendered_raw" in the segment dict. - Renderer.render() returns the segments by joining the "_rendered_hl" values for each segment. - Spacer segment widths are calculated in the render() method, and assigned to "_space_left" and "_space_right" in the segment dict. These spaces are then applied in Renderer._render_segments(). - All space characters are converted to no-break spaces (U+00A0) in the "_rendered_hl" property. Refs #113. Refs #154. --- docs/source/configuration.rst | 23 ++-- powerline/config_files/themes/vim/cmdwin.json | 6 +- .../config_files/themes/vim/default.json | 15 ++- powerline/config_files/themes/vim/help.json | 12 +- powerline/renderer.py | 120 ++++++++---------- powerline/renderers/vim.py | 3 + powerline/segment.py | 9 +- powerline/theme.py | 14 +- 8 files changed, 107 insertions(+), 95 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 29bf5d4c..e1d3f59a 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -237,12 +237,6 @@ Themes highlighting group is defined in the :ref:`highlight_group option `. - ``filler`` - If the statusline is rendered with a specific width, remaining - whitespace is distributed among filler segments. The - highlighting group is defined in the :ref:`highlight_group - option `. - ``module`` .. _config-themes-seg-module: @@ -276,13 +270,18 @@ Themes ``args`` A dict of arguments to be passed to a ``function`` segment. - ``ljust`` - If set, the segment will be left justified to the width specified by - this option. + ``align`` + Aligns the segments contents to the left (``l``), center (``c``) or + right (``r``). - ``rjust`` - If set, the segment will be right justified to the width specified - by this option. + ``width`` + Enforces a specific width for this segment. + + This segment will work as a spacer if the width is set to ``auto``. + Several spacers may be used, and the space will be distributed + equally among all the spacer segments. Spacers may have contents, + either returned by a function or a static string, and the contents + can be aligned with the ``align`` property. ``priority`` Optional segment priority. Segments with priority ``-1`` (the diff --git a/powerline/config_files/themes/vim/cmdwin.json b/powerline/config_files/themes/vim/cmdwin.json index fb0d1dff..cffd9e1c 100644 --- a/powerline/config_files/themes/vim/cmdwin.json +++ b/powerline/config_files/themes/vim/cmdwin.json @@ -7,8 +7,10 @@ "highlight_group": ["file_name"] }, { - "type": "filler", - "highlight_group": ["background"] + "type": "string", + "highlight_group": ["background"], + "draw_divider": false, + "width": "auto" } ] } diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index e4acc695..29fe36b4 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -41,8 +41,10 @@ "before": " " }, { - "type": "filler", - "highlight_group": ["background"] + "type": "string", + "highlight_group": ["background"], + "draw_divider": false, + "width": "auto" } ], "right": [ @@ -70,7 +72,8 @@ "args": { "gradient": true }, "priority": 30, "after": "%", - "rjust": 4 + "width": 4, + "align": "r" }, { "type": "string", @@ -80,14 +83,16 @@ { "name": "line_current", "draw_divider": false, - "rjust": 3 + "width": 3, + "align": "r" }, { "name": "col_current", "draw_divider": false, "priority": 30, "before": ":", - "ljust": 3 + "width": 3, + "align": "l" } ] } diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index 17bdfcc7..83961cba 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -6,8 +6,10 @@ "draw_divider": false }, { - "type": "filler", - "highlight_group": ["background"] + "type": "string", + "highlight_group": ["background"], + "draw_divider": false, + "width": "auto" } ], "right": [ @@ -16,7 +18,8 @@ "args": { "gradient": true }, "priority": 30, "after": "%", - "rjust": 4 + "width": 4, + "align": "r" }, { "type": "string", @@ -26,7 +29,8 @@ { "name": "line_current", "draw_divider": false, - "rjust": 3 + "width": 3, + "align": "r" } ] } diff --git a/powerline/renderer.py b/powerline/renderer.py index 688b84ae..7e3d354e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -11,8 +11,6 @@ class Renderer(object): TERM_24BIT_COLORS = False - PADDING_CHAR = u'\u00a0' # No-break space - def __init__(self, theme_config, local_themes, theme_kwargs, term_24bit_colors=False): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes @@ -34,13 +32,6 @@ class Renderer(object): else: return self.theme - @staticmethod - def _returned_value(rendered_highlighted, segments, output_raw): - if output_raw: - return rendered_highlighted, ''.join((segment['rendered_raw'] for segment in segments)) - else: - return rendered_highlighted - def render(self, mode=None, width=None, theme=None, segments=None, side=None, output_raw=False): '''Render all segments. @@ -56,31 +47,37 @@ class Renderer(object): # Handle excluded/included segments for the current mode segments = [segment for segment in segments\ if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] - rendered_highlighted = self._render_segments(mode, theme, segments) + + segments = [segment for segment in self._render_segments(mode, theme, segments)] + if not width: # No width specified, so we don't need to crop or pad anything - return self._returned_value(rendered_highlighted, segments, output_raw) + return self._returned_value(u''.join([segment['_rendered_hl'] for segment in segments]) + self.hl(), segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] - while self._total_len(segments) > width and len(segments_priority): + while sum([segment['_len'] for segment in segments]) > width and len(segments_priority): segments.remove(segments_priority[0]) segments_priority.pop(0) - # Do another render pass so we can calculate the correct amount of filler space - self._render_segments(mode, theme, segments, render_highlighted=False) + # Distribute the remaining space on spacer segments + segments_spacers = [segment for segment in segments if segment['width'] == 'auto'] + if segments_spacers: + distribute_len, distribute_len_remainder = divmod(width - sum([segment['_len'] for segment in segments]), len(segments_spacers)) + for segment in segments_spacers: + if segment['align'] == 'l': + segment['_space_right'] += distribute_len + elif segment['align'] == 'r': + segment['_space_left'] += distribute_len + elif segment['align'] == 'c': + space_side, space_side_remainder = divmod(distribute_len, 2) + segment['_space_left'] += space_side + space_side_remainder + segment['_space_right'] += space_side + segments_spacers[0]['_space_right'] += distribute_len_remainder - # Distribute the remaining space on the filler segments - segments_fillers = [segment for segment in segments if segment['type'] == 'filler'] - if segments_fillers: - segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len(segments)), len(segments_fillers)) - segments_fillers_contents = self.PADDING_CHAR * 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'] += self.PADDING_CHAR * segments_fillers_remainder + rendered_highlighted = u''.join([segment['_rendered_hl'] for segment in self._render_segments(mode, theme, segments)]) + self.hl() - return self._returned_value(self._render_segments(mode, theme, segments), segments, output_raw) + return self._returned_value(rendered_highlighted, segments, output_raw) def _render_segments(self, mode, theme, segments, render_highlighted=True): '''Internal segment rendering method. @@ -93,19 +90,20 @@ class Renderer(object): highlighting strings added), and only renders the highlighted statusline if render_highlighted is True. ''' - rendered_highlighted = u'' segments_len = len(segments) try: mode = mode if mode in segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY except IndexError: - return '' + pass for index, segment in enumerate(segments): + segment['_rendered_raw'] = u'' + segment['_rendered_hl'] = u'' + prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT compare_segment = next_segment if segment['side'] == 'left' else prev_segment - segment['rendered_raw'] = u'' - outer_padding = self.PADDING_CHAR if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' + outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' divider_raw = theme.get_divider(segment['side'], divider_type) @@ -114,22 +112,18 @@ class Renderer(object): contents_highlighted = '' # Pad segments first - if segment['type'] == 'filler': - pass - elif segment['draw_divider'] or divider_type == 'hard': + if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'): if segment['side'] == 'left': - contents_raw = outer_padding + contents_raw + self.PADDING_CHAR - divider_raw = divider_raw + self.PADDING_CHAR + contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + ' ' + divider_raw = divider_raw + ' ' else: - contents_raw = self.PADDING_CHAR + contents_raw + outer_padding - divider_raw = self.PADDING_CHAR + divider_raw - elif contents_raw: - if segment['side'] == 'left': - contents_raw = outer_padding + contents_raw - else: - contents_raw = contents_raw + outer_padding + contents_raw = ' ' + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding + divider_raw = ' ' + divider_raw else: - raise ValueError('Unknown segment type') + if segment['side'] == 'left': + contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + else: + contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding # Apply highlighting to padded dividers and contents if render_highlighted: @@ -144,37 +138,31 @@ class Renderer(object): contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'][mode]) # Append padded raw and highlighted segments to the rendered segment variables - if segment['type'] == 'filler': - rendered_highlighted += contents_highlighted if contents_raw else '' - 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['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'): if segment['side'] == 'left': - segment['rendered_raw'] += contents_raw + divider_raw - rendered_highlighted += contents_highlighted + divider_highlighted + segment['_rendered_raw'] += contents_raw + divider_raw + segment['_rendered_hl'] += contents_highlighted + divider_highlighted else: - segment['rendered_raw'] += divider_raw + contents_raw - rendered_highlighted += divider_highlighted + contents_highlighted - elif contents_raw: - # Segments without divider + segment['_rendered_raw'] += divider_raw + contents_raw + segment['_rendered_hl'] += divider_highlighted + contents_highlighted + else: if segment['side'] == 'left': - segment['rendered_raw'] += contents_raw - rendered_highlighted += contents_highlighted + segment['_rendered_raw'] += contents_raw + segment['_rendered_hl'] += contents_highlighted else: - segment['rendered_raw'] += contents_raw - rendered_highlighted += contents_highlighted - rendered_highlighted += self.hl() - return rendered_highlighted + segment['_rendered_raw'] += contents_raw + segment['_rendered_hl'] += contents_highlighted + segment['_len'] = len(segment['_rendered_raw']) + # Replace rendered spaces with no-break spaces + segment['_rendered_hl'] = segment['_rendered_hl'].replace(' ', u'\u00a0') + yield segment @staticmethod - def _total_len(segments): - '''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 segments])) + def _returned_value(rendered_highlighted, segments, output_raw): + if output_raw: + return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) + else: + return rendered_highlighted @staticmethod def escape(string): diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 9d33ca73..c6a53590 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -38,6 +38,9 @@ class VimRenderer(Renderer): else: mode = 'nc' theme, segments = self.window_cache.get(window_id, (None, None)) + for segment in segments: + segment['_space_left'] = 0 + segment['_space_right'] = 0 statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) return statusline diff --git a/powerline/segment.py b/powerline/segment.py index 04202b5d..a843a46b 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -45,11 +45,16 @@ class Segment(object): 'contents_func': contents_func, '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', []), + 'width': segment.get('width'), + 'align': segment.get('align', 'l'), + '_rendered_raw': u'', + '_rendered_hl': u'', + '_len': 0, + '_space_left': 0, + '_space_right': 0, } diff --git a/powerline/theme.py b/powerline/theme.py index f8516980..542b635a 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -65,15 +65,21 @@ class Theme(object): else: segment['contents'] = contents parsed_segments.append(segment) - elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None): + elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): parsed_segments.append(segment) else: continue for segment in parsed_segments: segment = self.add_highlight(segment) - segment['contents'] = (segment['before'] + unicode(segment['contents']) + segment['after'])\ - .ljust(segment['ljust'])\ - .rjust(segment['rjust']) + segment['contents'] = segment['before'] + unicode(segment['contents'] if segment['contents'] is not None else '') + segment['after'] + # Align segment contents + if segment['width'] and segment['width'] != 'auto': + if segment['align'] == 'l': + segment['contents'] = segment['contents'].ljust(segment['width']) + elif segment['align'] == 'r': + segment['contents'] = segment['contents'].rjust(segment['width']) + elif segment['align'] == 'c': + segment['contents'] = segment['contents'].center(segment['width']) # We need to yield a copy of the segment, or else mode-dependent # segment contents can't be cached correctly e.g. when caching # non-current window contents for vim statuslines From 260e40a544abdb81f614c2255919e9b9e5006d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 16:42:46 +0100 Subject: [PATCH 0322/1472] Replace spaces *before* highlighting contents The previous change obviously didn't work for renderers like the Pango markup renderer since the highlighting code uses regular spaces and not no-break spaces. Now spaces are replaced in the contents and dividers only, and it's done before highlighting the segment so the added highlighting code stays untouched. Refs #113. --- powerline/renderer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 7e3d354e..1cc5eaa8 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -125,6 +125,10 @@ class Renderer(object): else: contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding + # Replace spaces with no-break spaces + contents_raw = contents_raw.replace(' ', u'\u00a0') + divider_raw = divider_raw.replace(' ', u'\u00a0') + # Apply highlighting to padded dividers and contents if render_highlighted: if divider_type == 'soft': @@ -153,8 +157,6 @@ class Renderer(object): segment['_rendered_raw'] += contents_raw segment['_rendered_hl'] += contents_highlighted segment['_len'] = len(segment['_rendered_raw']) - # Replace rendered spaces with no-break spaces - segment['_rendered_hl'] = segment['_rendered_hl'].replace(' ', u'\u00a0') yield segment @staticmethod From 4cda38e2149b17061c5b92cdd03cd607acbb6891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 22:01:53 +0100 Subject: [PATCH 0323/1472] Remove support for persistent memoization A different form of persistent memoization should be reimplemented later. Refs #159. --- powerline/lib/memoize.py | 26 +------------------------- powerline/segments/common.py | 6 +++--- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 00dbeef8..7bf75c13 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,11 +1,5 @@ # -*- coding: utf-8 -*- -try: - import cPickle as pickle -except ImportError: - import pickle -import os -import tempfile import time @@ -13,11 +7,9 @@ class memoize(object): '''Memoization decorator with timeout.''' _cache = {} - def __init__(self, timeout, additional_key=None, persistent=False, persistent_file=None): + def __init__(self, timeout, additional_key=None): self.timeout = timeout self.additional_key = additional_key - self.persistent = persistent - self.persistent_file = persistent_file or os.path.join(tempfile.gettempdir(), 'powerline-cache') def __call__(self, func): def decorated_function(*args, **kwargs): @@ -25,27 +17,11 @@ class memoize(object): key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) else: key = (func.__name__, args, tuple(kwargs.items())) - if self.persistent: - try: - with open(self.persistent_file, 'rb') as fileobj: - self._cache = pickle.load(fileobj) - except (IOError, EOFError, ValueError): - self._cache = {} cached = self._cache.get(key, None) if cached is None or time.time() - cached['time'] > self.timeout: cached = self._cache[key] = { 'result': func(*args, **kwargs), 'time': time.time(), } - if self.persistent: - try: - with open(self.persistent_file, 'wb') as fileobj: - pickle.dump(self._cache, fileobj) - except IOError: - # Unable to write to file - pass - except TypeError: - # Unable to pickle function result - pass return cached['result'] return decorated_function diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5598b956..e25b985e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -101,7 +101,7 @@ def date(format='%Y-%m-%d'): return datetime.now().strftime(format) -@memoize(600, persistent=True) +@memoize(600) def external_ip(query_url='http://ipv4.icanhazip.com/'): return _urllib_read(query_url).strip() @@ -123,7 +123,7 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): return format.format(days=int(days), hours=hours, minutes=minutes) -@memoize(1800, persistent=True) +@memoize(1800) def weather(unit='c', location_query=None): import json @@ -232,7 +232,7 @@ def virtualenv(): return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None -@memoize(60, persistent=True) +@memoize(60) def email_imap_alert(username, password, server='imap.gmail.com', port=993, folder='INBOX'): import imaplib import re From ea32006715dc39a21a5ac1c0bc0ed366b83de183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 22:32:39 +0100 Subject: [PATCH 0324/1472] Fix Unicode issues with mail segment --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e25b985e..0d87119a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -242,7 +242,7 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold try: mail = imaplib.IMAP4_SSL(server, port) mail.login(username, password) - rc, message = mail.status(folder, '(UNSEEN)') + rc, message = mail.status(folder, '(UNSEEN)').decode('utf-8') unread_count = int(re.search('UNSEEN (\d+)', message[0]).group(1)) except (imaplib.IMAP4.error, AttributeError): return None From e8ebb07662a7e6087e88cc40f24a8d8ee547d366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 22:33:07 +0100 Subject: [PATCH 0325/1472] Add common player conversion methods --- powerline/segments/common.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 0d87119a..31899123 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -290,6 +290,20 @@ class NowPlayingSegment(object): return None return stdout.strip() + @staticmethod + def _convert_state(state): + state = state.lower() + if 'play' in state: + return 'play' + if 'pause' in state: + return 'pause' + if 'stop' in state: + return 'stop' + + @staticmethod + def _convert_seconds(seconds): + return u'{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) + def player_mpd(self, host='localhost', port=6600): try: import mpd From def7a1e82e01eb5aed204d4078217f2ab9acf321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 22:33:24 +0100 Subject: [PATCH 0326/1472] Update now_playing methods to use common formatting methods --- powerline/segments/common.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 31899123..58a1fc2d 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -321,8 +321,8 @@ class NowPlayingSegment(object): 'album': now_playing.get('album'), 'artist': now_playing.get('artist'), 'title': now_playing.get('title'), - 'elapsed': '{0:.0f}:{1:02.0f}'.format(*divmod(float(status.get('elapsed', 0)), 60)), - 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(now_playing['time']), 60)), + 'elapsed': self._convert_seconds(now_playing.get('elapsed', 0)), + 'total': self._convert_seconds(now_playing.get('time', 0)), } except ImportError: now_playing = self._run_cmd(['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) @@ -349,17 +349,17 @@ class NowPlayingSegment(object): player = bus.get_object('com.spotify.qt', '/') iface = dbus.Interface(player, DBUS_IFACE_PROPERTIES) info = iface.Get(DBUS_IFACE_PLAYER, 'Metadata') - state = iface.Get(DBUS_IFACE_PLAYER, 'PlaybackStatus') + status = iface.Get(DBUS_IFACE_PLAYER, 'PlaybackStatus') except dbus.exceptions.DBusException: return - state = {'Playing': 'play', 'Paused': 'pause'}.get(state, None) + state = self._convert_state(status) return { 'state': state, 'state_symbol': self.STATE_SYMBOLS.get(state), - 'album': str(info['xesam:album']), - 'artist': str(info['xesam:artist'][0]), - 'title': str(info['xesam:title']), - 'total': '{0:.0f}:{1:02.0f}'.format(*divmod(float(info['mpris:length'] / 1e6), 60)), + 'album': info['xesam:album'], + 'artist': info['xesam:artist'][0], + 'title': info['xesam:title'], + 'total': self._convert_seconds(info['mpris:length'] / 1e6), } def player_rhythmbox(self): From 7e14a84cc96b49738e59cc30549219281955686c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 1 Feb 2013 22:33:28 +0100 Subject: [PATCH 0327/1472] Add cmus player to now_playing segment Closes #162. --- powerline/segments/common.py | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 58a1fc2d..65edbed4 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -304,6 +304,44 @@ class NowPlayingSegment(object): def _convert_seconds(seconds): return u'{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) + def player_cmus(self): + '''Return cmus player information. + + cmus-remote -Q returns data with multi-level information i.e. + status playing + file + tag artist + tag title + tag .. + tag n + set continue + set repeat + set .. + set n + + For the information we are looking for we don't really care if we're on + the tag level or the set level. The dictionary comprehension in this + method takes anything in ignore_levels and brings the key inside that + to the first level of the dictionary. + ''' + now_playing_str = self._run_cmd(['cmus-remote', '-Q']) + if not now_playing_str: + return + ignore_levels = ('tag', 'set',) + now_playing = {token[0] if token[0] not in ignore_levels else token[1]: + ' '.join(token[1:]) if token[0] not in ignore_levels else + ' '.join(token[2:]) for token in [line.split(' ') for line in now_playing_str.split('\n')[:-1]]} + state = self._convert_state(now_playing.get('status')) + return { + 'state': state, + 'state_symbol': self.STATE_SYMBOLS.get(state), + 'album': now_playing.get('album'), + 'artist': now_playing.get('artist'), + 'title': now_playing.get('title'), + 'elapsed': self._convert_seconds(now_playing.get('position', 0)), + 'total': self._convert_seconds(now_playing.get('duration', 0)), + } + def player_mpd(self, host='localhost', port=6600): try: import mpd From 57bd0af2721bf5ad18b512f347d6a08468ce0767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 3 Feb 2013 16:27:30 +0100 Subject: [PATCH 0328/1472] Make 'powerline' a namespace package This will allow third-party plugins to use the 'powerline' namespace for contributed segments. Refs #3. --- powerline/__init__.py | 5 +++++ setup.py | 1 + 2 files changed, 6 insertions(+) diff --git a/powerline/__init__.py b/powerline/__init__.py index e69de29b..7ca9f62e 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -0,0 +1,5 @@ +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/setup.py b/setup.py index 29101ea6..d0e6a0d0 100755 --- a/setup.py +++ b/setup.py @@ -33,4 +33,5 @@ setup(name='Powerline', 'Sphinx', ], }, + namespace_packages=['powerline'], ) From 7a25ab1d53c29bf35266221c7c9ffa3786372dec Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Feb 2013 00:19:04 +0400 Subject: [PATCH 0329/1472] =?UTF-8?q?Fix=20=E2=80=9C'NoneType'=20object=20?= =?UTF-8?q?is=20not=20iterable=E2=80=9D=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #158 --- powerline/renderers/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index c6a53590..6548f9b9 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -37,7 +37,7 @@ class VimRenderer(Renderer): self.window_cache[window_id] = (theme, segments) else: mode = 'nc' - theme, segments = self.window_cache.get(window_id, (None, None)) + theme, segments = self.window_cache.get(window_id, (None, [])) for segment in segments: segment['_space_left'] = 0 segment['_space_right'] = 0 From b99e9cb0748e0bbbadd8438613a22aee9bef4dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 4 Feb 2013 13:04:17 +0100 Subject: [PATCH 0330/1472] Revert "Make 'powerline' a namespace package" This reverts commit 57bd0af2721bf5ad18b512f347d6a08468ce0767. --- powerline/__init__.py | 5 ----- setup.py | 1 - 2 files changed, 6 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 7ca9f62e..e69de29b 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -1,5 +0,0 @@ -try: - __import__('pkg_resources').declare_namespace(__name__) -except ImportError: - from pkgutil import extend_path - __path__ = extend_path(__path__, __name__) diff --git a/setup.py b/setup.py index d0e6a0d0..29101ea6 100755 --- a/setup.py +++ b/setup.py @@ -33,5 +33,4 @@ setup(name='Powerline', 'Sphinx', ], }, - namespace_packages=['powerline'], ) From 6f693220243780e63e0b364414f7ec4c93cd6681 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 28 Jan 2013 22:53:06 +0400 Subject: [PATCH 0331/1472] Add support for zsh/zpython Closes #163. --- powerline/bindings/zsh/__init__.py | 43 ++++++++++++++++++++++++++++ powerline/bindings/zsh/powerline.zsh | 16 ++++++----- 2 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 powerline/bindings/zsh/__init__.py diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py new file mode 100644 index 00000000..75b36658 --- /dev/null +++ b/powerline/bindings/zsh/__init__.py @@ -0,0 +1,43 @@ +import zsh +from powerline.core import Powerline + + +class Args(object): + @property + def last_exit_code(self): + return zsh.last_exit_code() + + @property + def last_pipe_status(self): + return zsh.pipestatus() + + +class Prompt(object): + __slots__ = ('render', 'side', 'savedpsvar', 'savedps') + + def __init__(self, powerline, side, savedpsvar=None, savedps=None): + self.render = powerline.renderer.render + self.side = side + self.savedpsvar = savedpsvar + self.savedps = savedps + + def __str__(self): + return self.render(width=zsh.columns(), side=self.side).encode('utf-8') + + def __del__(self): + if self.savedps: + zsh.setvalue(self.savedpsvar, self.savedps) + + +def set_prompt(powerline, psvar, side): + savedps = zsh.getvalue(psvar) + zpyvar = 'ZPYTHON_POWERLINE_' + psvar + prompt = Prompt(powerline, side, psvar, savedps) + zsh.set_special_string(zpyvar, prompt) + zsh.setvalue(psvar, '${' + zpyvar + '}') + + +def setup(): + powerline = Powerline(ext='shell', renderer_module='zsh_prompt', segment_info=Args()) + set_prompt(powerline, 'PS1', 'left') + set_prompt(powerline, 'RPS1', 'right') diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 4512a6f1..4499bf9c 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,7 +1,3 @@ -_powerline_precmd() { - _powerline_tmux_set_pwd -} - _powerline_tmux_setenv() { if [[ -n "$TMUX" ]]; then tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" @@ -22,11 +18,17 @@ _powerline_install_precmd() { return fi done - precmd_functions+=(_powerline_precmd) + precmd_functions+=(_powerline_tmux_set_pwd) setopt promptpercent setopt promptsubst - PS1='$(powerline shell left -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' - RPS1='$(powerline shell right -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + if zmodload zsh/zpython &>/dev/null ; then + zpython 'from powerline.bindings.zsh import setup as powerline_setup' + zpython 'powerline_setup()' + zpython 'del powerline_setup' + else + PS1='$(powerline shell left -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + RPS1='$(powerline shell right -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + fi } trap "_powerline_tmux_set_columns" SIGWINCH From 184dac1bff737a17f383b59bc66c023da7051981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 5 Feb 2013 13:48:33 +0100 Subject: [PATCH 0332/1472] Add solarized support for shell exit status segments Closes #165. --- powerline/config_files/colorschemes/shell/solarized.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index f035afe5..b850b34f 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -26,6 +26,8 @@ "cwd": { "fg": "base2", "bg": "base01" }, "cwd:current_folder": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, "cwd:divider": { "fg": "base1", "bg": "base01" }, - "hostname": { "fg": "base3", "bg": "base01" } + "hostname": { "fg": "base3", "bg": "base01" }, + "exit_fail": { "fg": "base3", "bg": "red" }, + "exit_success": { "fg": "base3", "bg": "green" } } } From 56e661b21b17a17043d326f691182aac7bf5f437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 08:51:47 +0100 Subject: [PATCH 0333/1472] Handle more request errors in common segments --- powerline/segments/common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 65edbed4..6da9370e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -24,13 +24,13 @@ def _urllib_read(url): import urllib.error import urllib.request try: - return urllib.request.urlopen(url).read().decode('utf-8') - except urllib.error.HTTPError: + return urllib.request.urlopen(url, timeout=5).read().decode('utf-8') + except: return except ImportError: import urllib2 try: - return urllib2.urlopen(url).read() + return urllib2.urlopen(url, timeout=5).read() except urllib2.HTTPError: return @@ -131,7 +131,7 @@ def weather(unit='c', location_query=None): try: location = json.loads(_urllib_read('http://freegeoip.net/json/' + external_ip())) location_query = ','.join([location['city'], location['region_name'], location['country_name']]) - except ValueError: + except (TypeError, ValueError): return None query_data = { 'q': From e3ce4adc3d6b28f0f9aa2bbec5cc1c5a048bf57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 08:51:55 +0100 Subject: [PATCH 0334/1472] Fix UTF-8 encoding issue with weather segment Closes #172. --- powerline/segments/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 6da9370e..db82f89a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -135,8 +135,8 @@ def weather(unit='c', location_query=None): return None query_data = { 'q': - 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' - 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit), + u'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' + u'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit).encode('utf-8'), 'format': 'json' } try: From 5cc265affda6a69fd4f121eab93d498777bb2cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 08:56:18 +0100 Subject: [PATCH 0335/1472] Fix repo.status() functions --- powerline/lib/vcs/git.py | 2 +- powerline/lib/vcs/mercurial.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index ab865a4f..d44a69c9 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -59,7 +59,7 @@ try: wt_column = ' ' index_column = ' ' untracked_column = ' ' - for status in self._repo().status(): + for status in self._repo().status().values(): if status & (git.GIT_STATUS_WT_DELETED | git.GIT_STATUS_WT_MODIFIED): wt_column = 'D' diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index 63b18dc6..bc2f7add 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -42,7 +42,8 @@ class Repository(object): else: resulting_status = 0 for status, paths in zip(self.repo_statuses, repo.status(unknown=True)): - resulting_status |= status + if paths: + resulting_status |= status return self.repo_statuses_str[resulting_status] def branch(self): From c8d4e58a9366a9244d66413c417b97e0c6ce9b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 08:59:53 +0100 Subject: [PATCH 0336/1472] Add repository_status vim segment Split segment highlighting compared to the file_vcs_status segment is missing. Refs #173. --- powerline/segments/vim.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index ab885212..ce11deb4 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -194,3 +194,11 @@ def file_vcs_status(): ret[0]['before'] = ' ' return ret return None + + +@memoize(2) +def repository_status(): + repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) + if repo: + return repo.status() + return None From 9a95d738d565e8bebd26e50c6c38f33271456644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 09:09:18 +0100 Subject: [PATCH 0337/1472] Fix e-mail segment UTF-8 issues Closes #178. --- powerline/segments/common.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index db82f89a..3cf2efa2 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -242,10 +242,11 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold try: mail = imaplib.IMAP4_SSL(server, port) mail.login(username, password) - rc, message = mail.status(folder, '(UNSEEN)').decode('utf-8') - unread_count = int(re.search('UNSEEN (\d+)', message[0]).group(1)) - except (imaplib.IMAP4.error, AttributeError): - return None + rc, message = mail.status(folder, '(UNSEEN)') + unread_str = message[0].decode('utf-8') + unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) + except imaplib.IMAP4.error as e: + unread_count = str(e) if not unread_count: return None return [{ From 4fa17f6ff5b7e6741ff9e1ad7709ed60f07a1c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 09:10:40 +0100 Subject: [PATCH 0338/1472] Catch more errors in weather segment --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 3cf2efa2..f0265ffa 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -144,7 +144,7 @@ def weather(unit='c', location_query=None): response = json.loads(_urllib_read(url)) condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) - except (KeyError, ValueError): + except (KeyError, TypeError, ValueError): return None icon = u'〇' for icon, codes in weather_conditions_codes.items(): From 2e5b9383a5ade97697db7a9ed4d782b785456e07 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Feb 2013 08:34:35 +0400 Subject: [PATCH 0339/1472] Properly escape color codes in bash Closes #177. Closes #179. --- powerline/bindings/bash/powerline.sh | 16 ++++++++++++---- powerline/renderers/bash_prompt.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 powerline/renderers/bash_prompt.py diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 4b6dbe0b..15e34f58 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -12,8 +12,16 @@ _powerline_tmux_set_columns() { _powerline_tmux_setenv COLUMNS "$COLUMNS" } -trap "_powerline_tmux_set_columns" SIGWINCH -kill -SIGWINCH "$$" +_powerline_prompt() { + [[ -z "$POWERLINE_OLD_PROMPT_COMMAND" ]] || + eval $POWERLINE_OLD_PROMPT_COMMAND + PS1="$(powerline shell left -r bash_prompt --last_exit_code=$?)" + _powerline_tmux_set_pwd +} -export PROMPT_COMMAND="_powerline_tmux_set_pwd" -export PS1='$(powerline shell left --last_exit_code=$?)' +trap "_powerline_tmux_set_columns" SIGWINCH +_powerline_tmux_set_columns + +[[ "$PROMPT_COMMAND" == "_powerline_prompt" ]] || + POWERLINE_OLD_PROMPT_COMMAND="$PROMPT_COMMAND" +export PROMPT_COMMAND="_powerline_prompt" diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py new file mode 100644 index 00000000..dbc30692 --- /dev/null +++ b/powerline/renderers/bash_prompt.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from powerline.renderers.shell import ShellRenderer + + +class BashPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + def hl(self, contents=None, fg=None, bg=None, attr=None): + '''Highlight a segment. + + Returns the default ShellRenderer escape sequence with \[...\] wrapped + around it (required in bash prompts). + ''' + return '\[' + super(BashPromptRenderer, self).hl(None, fg, bg, attr) + '\]' + (contents or u'') + + @staticmethod + def escape(string): + return string.replace('\\', '\\\\').replace('$', '\\$').replace('`', '\\`') From c2e7124da065ac72bd3b6394650aacd47810e8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 6 Feb 2013 13:18:49 +0100 Subject: [PATCH 0340/1472] Fix statusline highlighting after leaving a cmdwin Because vim doesn't trigger any autocmds after leaving a cmdwin the statusline in the window the user returned to from the cmdwin would be highlighted as non-current even if it should have been current. This issue is resolved by storing the last current window ID in a script variable, and when leaving the cmdwin we show the last current window as current instead of detecting it after the WinEnter autocmd has been triggered (which doesn't happen after leaving a cmdwin). Closes #184. --- powerline/bindings/vim/powerline.vim | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/powerline/bindings/vim/powerline.vim b/powerline/bindings/vim/powerline.vim index c030596b..9f0499f1 100644 --- a/powerline/bindings/vim/powerline.vim +++ b/powerline/bindings/vim/powerline.vim @@ -23,17 +23,25 @@ function! Powerline(winnr, current) return s:pyeval('powerline.renderer.render('. a:winnr .', '. a:current .')') endfunction -function! s:UpdateWindows() +function! s:UpdateWindows(use_last_current_window_id) if ! exists('w:window_id') let w:window_id = s:pyeval('str(uuid.uuid4())') endif for winnr in range(1, winnr('$')) - call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .', '. (w:window_id == getwinvar(winnr, 'window_id')) .')') + let current = 0 + if w:window_id == getwinvar(winnr, 'window_id') || (a:use_last_current_window_id && getwinvar(winnr, 'window_id') == s:last_current_window_id) + let current = 1 + if bufname(winbufnr(winnr)) isnot# '[Command Line]' + let s:last_current_window_id = getwinvar(winnr, 'window_id') + endif + endif + call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .', '. current .')') endfor - redrawstatus endfunction +let s:last_current_window_id = '' augroup Powerline autocmd! - autocmd BufEnter,BufWinEnter,WinEnter,CmdwinEnter * call s:UpdateWindows() + autocmd BufEnter,BufWinEnter,WinEnter,CmdwinEnter * call s:UpdateWindows(0) | redrawstatus + autocmd CmdwinLeave * call s:UpdateWindows(1) augroup END From 8b8de32be90f69a992bbff818c6898893393aa4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 7 Feb 2013 11:42:10 +0100 Subject: [PATCH 0341/1472] Improve vim plugin loading Several of the plugin loading methods have been joined into one plugin file that will be loaded by updating the runtimepath. More informative error messages will be displayed if Python support is missing or if the module import fails. Note that this commit will break existing plugin loading, the new method with updating the runtimepath will be required. Closes #156. Closes #181. --- docs/source/overview.rst | 27 ++++++----------- powerline/bindings/vim/__init__.py | 13 -------- .../bindings/vim/{ => plugin}/powerline.vim | 30 ++++++++++++++++++- .../bindings/vim/plugin/source_plugin.vim | 11 ------- 4 files changed, 38 insertions(+), 43 deletions(-) rename powerline/bindings/vim/{ => plugin}/powerline.vim (58%) delete mode 100644 powerline/bindings/vim/plugin/source_plugin.vim diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 95014013..c4c971f7 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -83,26 +83,17 @@ Usage Vim statusline -------------- -Add the following line to your :file:`vimrc`: +Add the following line to your :file:`vimrc`, where ``{path}`` is the +absolute path to your Powerline installation directory: .. code-block:: vim - python from powerline.bindings.vim import source_plugin; source_plugin() + set rtp+={path}/powerline/bindings/vim -If you want to enable Python 3 support, substitute the ``python`` command -above with ``python3``. Note that this is somewhat experimental as some -segments don't have support for Python 3 yet. - -If Powerline is installed somewhere other than Python's site-packages -directories you'll either have to use a plugin manager like Vundle, or -source the vim plugin file with an absolute path to the plugin location. - -Add the following line to your :file:`vimrc`, where ``{path}`` is the path -to the main Powerline project directory: - -.. code-block:: vim - - source {path}/powerline/bindings/vim/plugin/source_plugin.vim +If you're using Vundle or Pathogen and don't want Powerline functionality in +any other applications, simply add Powerline as a bundle and point the path +above to the Powerline bundle directory, e.g. +``~/.vim/bundle/powerline/powerline/bindings/vim``. Shell prompts ------------- @@ -128,7 +119,7 @@ absolute path to your Powerline installation directory: . {path}/powerline/bindings/zsh/powerline.zsh Tmux statusline -^^^^^^^^^^^^^^^ +--------------- Add the following line to your :file:`tmux.conf`, where ``{path}`` is the absolute path to your Powerline installation directory:: @@ -136,7 +127,7 @@ absolute path to your Powerline installation directory:: source '{path}/powerline/bindings/tmux/powerline.conf' IPython prompt -^^^^^^^^^^^^^^ +-------------- For IPython<0.11 add the following lines to your :file:`.ipython/ipy_user_conf.py`:: diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index ef46b4ae..6608372e 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -2,19 +2,6 @@ import vim - -def source_plugin(): - import sys - import os - if sys.version_info[:2] == (3, 3): - vim.command('let g:powerline_pycmd = "python3"') - vim.command('let g:powerline_pyeval = "py3eval"') - else: - vim.command('let g:powerline_pycmd = "python"') - vim.command('let g:powerline_pyeval = "pyeval"') - fnameescape = vim_get_func('fnameescape') - vim.command('source ' + fnameescape(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'powerline.vim')).decode('utf-8')) - try: _vim_globals = vim.bindeval('g:') diff --git a/powerline/bindings/vim/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim similarity index 58% rename from powerline/bindings/vim/powerline.vim rename to powerline/bindings/vim/plugin/powerline.vim index 9f0499f1..a7284117 100644 --- a/powerline/bindings/vim/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -3,11 +3,39 @@ if exists('g:powerline_loaded') endif let g:powerline_loaded = 1 +function! s:CriticalError(message) + echohl ErrorMsg + echomsg a:message + echohl None +endfunction + +if ! has('python') && ! has('python3') + call s:CriticalError('You need vim compiled with Python 2.7 or 3.3+ support + \ for Powerline to work. Please consult the documentation for more details.') + finish +endif + let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', 'py'), '\v^(py)%[thon](3?)$', '\1\2', '') let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') exec s:powerline_pycmd 'import uuid' -exec s:powerline_pycmd 'from powerline.core import Powerline' +try + exec s:powerline_pycmd 'from powerline.core import Powerline' +catch + " An error occured while importing the module, it could be installed + " outside of Python's module search paths. Update sys.path and try again. + exec s:powerline_pycmd 'import sys, vim' + exec s:powerline_pycmd 'sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' + try + exec s:powerline_pycmd 'from powerline.core import Powerline' + catch + call s:CriticalError('An error occured while importing the Powerline package. + \ This could be caused by an invalid sys.path setting, or by an incompatible + \ Python version (Powerline requires Python 2.7 or 3.3+ to work). Please consult + \ the troubleshooting section in the documentation for possible solutions.') + finish + endtry +endtry exec s:powerline_pycmd 'powerline = Powerline("vim")' if exists('*'. s:powerline_pyeval) diff --git a/powerline/bindings/vim/plugin/source_plugin.vim b/powerline/bindings/vim/plugin/source_plugin.vim deleted file mode 100644 index ba96920e..00000000 --- a/powerline/bindings/vim/plugin/source_plugin.vim +++ /dev/null @@ -1,11 +0,0 @@ -if ! has('python') && ! has('python3') - echohl ErrorMsg - echomsg 'You need vim compiled with Python 3.3 or Python 2.7 support for Powerline to work. Please consult the documentation for more details.' - echohl None - finish -endif - -python import sys, vim -python sys.path.append(vim.eval('expand(":h:h:h:h:h")')) - -source :h:h/powerline.vim From 4e4a2b2ef86db309616973930a925a338c8e5fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 7 Feb 2013 12:32:34 +0100 Subject: [PATCH 0342/1472] Add tips & tricks to the docs Closes #182. --- docs/source/index.rst | 1 + docs/source/tipstricks.rst | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docs/source/tipstricks.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 01eae89c..e70e3af6 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,6 +9,7 @@ Powerline introduction overview configuration + tipstricks fontpatching license-credits diff --git a/docs/source/tipstricks.rst b/docs/source/tipstricks.rst new file mode 100644 index 00000000..e445ab95 --- /dev/null +++ b/docs/source/tipstricks.rst @@ -0,0 +1,37 @@ +************* +Tips & Tricks +************* + +Vim +=== + +Fix terminal timeout when pressing escape +----------------------------------------- + +When you're pressing :kbd:`Escape` to leave insert mode in the terminal, it +will by default take a second or another keystroke to leave insert mode +completely and update the statusline. If you find this annoying, you can add +the following snippet to your :file:`vimrc` to escape insert mode +immediately: + +.. code-block:: vim + + if ! has('gui_running') + set ttimeoutlen=10 + augroup FastEscape + autocmd! + au InsertEnter * set timeoutlen=0 + au InsertLeave * set timeoutlen=1000 + augroup END + endif + +Useful settings +--------------- + +You may find the following vim settings useful when using the Powerline +statusline: + +.. code-block:: vim + + set laststatus=2 " Always display the statusline in all windows + set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline) From 323a838511304d71890a9c07842d9f13fc0ffe1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 7 Feb 2013 13:48:36 +0100 Subject: [PATCH 0343/1472] Fix math error in humanize_bytes Closes #175. --- powerline/lib/humanize_bytes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index e9a13479..5d82c8b7 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -12,7 +12,7 @@ def humanize_bytes(num, suffix='B', binary_prefix=False): if num == 0: return '0 ' + suffix div = 1000 if binary_prefix else 1024 - exponent = min(int(log(num, div)), len(unit_list) - 1) + exponent = min(int(log(num, div)) if num else 0, len(unit_list) - 1) quotient = float(num) / div ** exponent unit, decimals = unit_list[exponent] if unit and binary_prefix: From 907e85931bd773272e6fae8a4062f7a98389f346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Feb 2013 14:40:41 +0100 Subject: [PATCH 0344/1472] Fix vim plugin path in Arch Linux packages --- packages/archlinux/python-powerline-git/PKGBUILD | 4 ++-- packages/archlinux/python2-powerline-git/PKGBUILD | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 7e02ee6d..42ca251f 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -2,7 +2,7 @@ pkgbase=python-powerline pkgname=python-powerline-git -pkgver=20130131 +pkgver=20130208 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -53,7 +53,7 @@ package() { msg2 "Installing vim plugin..." install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" - install -m644 "powerline/bindings/vim/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" msg2 "Installing zsh plugin..." install -dm755 "${pkgdir}/usr/share/zsh/site-contrib" diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index b78fbe79..8d8388f3 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -2,7 +2,7 @@ pkgbase=python-powerline pkgname=python2-powerline-git -pkgver=20130124 +pkgver=20130208 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -54,7 +54,7 @@ package() { msg2 "Installing vim plugin..." install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" - install -m644 "powerline/bindings/vim/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" msg2 "Installing zsh plugin..." install -dm755 "${pkgdir}/usr/share/zsh/site-contrib" From 5399265135cbf854ec9e222e3d45ed144fd23c0b Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 7 Feb 2013 21:58:18 +0400 Subject: [PATCH 0345/1472] Add note about zpython branch TODO: To be removed after (if) zsh maintainers accept the patch. Refs #104. --- docs/source/overview.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index c4c971f7..3728c973 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -118,6 +118,9 @@ absolute path to your Powerline installation directory: . {path}/powerline/bindings/zsh/powerline.zsh +If you are not satisfied with powerline speed in this case, compile zpython +branch from https://bitbucket.org/ZyX_I/zsh. + Tmux statusline --------------- From d386ae71fdb3c7e5d18353af33b59360f583dc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Feb 2013 16:20:26 +0100 Subject: [PATCH 0346/1472] Add modified_buffers segment This segment returns a comma-separated list of modified buffers. The segment is not enabled by default. Closes #173. --- powerline/config_files/colorschemes/vim/default.json | 3 ++- powerline/segments/vim.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index f87b233e..5e602bcb 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -82,7 +82,8 @@ "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" } + "col_current": { "fg": "gray6", "bg": "gray10" }, + "modified_buffers": { "fg": "brightyellow", "bg": "gray2" } }, "mode_translations": { "nc": { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index ce11deb4..f23d87c0 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -167,6 +167,15 @@ def col_current(virtcol=True): return vim_funcs['virtcol' if virtcol else 'col']('.') +def modified_buffers(text=u'+'): + '''Return a comma-separated list of modified buffers.''' + buffer_len = int(vim.eval('bufnr("$")')) + buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if vim.eval('getbufvar({0}, "&mod")'.format(bufnr)) == '1'] + if buffer_mod: + return u'{0} {1}'.format(text, ','.join(buffer_mod)) + return None + + @memoize(2) def branch(): repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) From 6dc81ab855707d14a92ab98710b38b2ddeedfd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Feb 2013 17:13:37 +0100 Subject: [PATCH 0347/1472] Use functools.wraps on memoize decorator --- powerline/lib/memoize.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 7bf75c13..97cd1103 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from functools import wraps import time @@ -12,6 +13,7 @@ class memoize(object): self.additional_key = additional_key def __call__(self, func): + @wraps(func) def decorated_function(*args, **kwargs): if self.additional_key: key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) From 55ad48f0e657635273736428e3c145361487e5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Feb 2013 17:32:23 +0100 Subject: [PATCH 0348/1472] Update sphinx configuration --- docs/source/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index be00d779..bb86ae6f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -import sys, os +import os +import sys -sys.path.insert(0, os.path.abspath('../powerline')) +sys.path.insert(0, os.path.abspath('../..')) extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] source_suffix = '.rst' @@ -13,5 +14,5 @@ version = 'beta' release = 'beta' exclude_patterns = ['_build'] pygments_style = 'sphinx' -html_theme = 'nature' +html_theme = 'default' html_static_path = ['_static'] From b599761ae6cb47679c396af3a7f014f14c714176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Feb 2013 17:33:54 +0100 Subject: [PATCH 0349/1472] Work around missing vim module when generating sphinx docs Horrible solution, should be fixed asap. --- powerline/bindings/vim/__init__.py | 5 ++++- powerline/segments/vim.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 6608372e..2291bb40 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- -import vim +try: + import vim +except ImportError: + vim = {} try: _vim_globals = vim.bindeval('g:') diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f23d87c0..1c1217f1 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -3,7 +3,10 @@ from __future__ import absolute_import import os -import vim +try: + import vim +except ImportError: + vim = {} from powerline.bindings.vim import vim_get_func from powerline.lib import memoize, humanize_bytes From 2caa136f26334b9dadc78668bdef1a088377f627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Feb 2013 17:33:04 +0100 Subject: [PATCH 0350/1472] Add docs for all segments --- docs/source/index.rst | 8 +++ docs/source/segments/common.rst | 6 ++ docs/source/segments/shell.rst | 6 ++ docs/source/segments/vim.rst | 6 ++ powerline/segments/common.py | 106 ++++++++++++++++++++++++++++++++ powerline/segments/shell.py | 2 + powerline/segments/vim.py | 77 ++++++++++++++++------- 7 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 docs/source/segments/common.rst create mode 100644 docs/source/segments/shell.rst create mode 100644 docs/source/segments/vim.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index e70e3af6..2afd8241 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,6 +13,14 @@ Powerline fontpatching license-credits +Segments +======== + +.. toctree:: + segments/common + segments/shell + segments/vim + Indices and tables ================== diff --git a/docs/source/segments/common.rst b/docs/source/segments/common.rst new file mode 100644 index 00000000..49dc2519 --- /dev/null +++ b/docs/source/segments/common.rst @@ -0,0 +1,6 @@ +*************** +Common segments +*************** + +.. automodule:: powerline.segments.common + :members: diff --git a/docs/source/segments/shell.rst b/docs/source/segments/shell.rst new file mode 100644 index 00000000..fb3c8045 --- /dev/null +++ b/docs/source/segments/shell.rst @@ -0,0 +1,6 @@ +************** +Shell segments +************** + +.. automodule:: powerline.segments.shell + :members: diff --git a/docs/source/segments/vim.rst b/docs/source/segments/vim.rst new file mode 100644 index 00000000..5d6c31a9 --- /dev/null +++ b/docs/source/segments/vim.rst @@ -0,0 +1,6 @@ +************ +Vim segments +************ + +.. automodule:: powerline.segments.vim + :members: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f0265ffa..bae7b129 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -45,6 +45,11 @@ def _urllib_urlencode(string): def hostname(only_if_ssh=False): + '''Return the current hostname. + + :param bool only_if_ssh: + only return the hostname if currently in an SSH session + ''' import socket if only_if_ssh and not os.environ.get('SSH_CLIENT'): return None @@ -52,6 +57,10 @@ def hostname(only_if_ssh=False): def user(): + '''Return the current user. + + Highlights the user with the ``superuser`` if the effective user ID is 0. + ''' user = os.environ.get('USER') euid = os.geteuid() return [{ @@ -61,6 +70,7 @@ def user(): def branch(): + '''Return the current VCS branch.''' from powerline.lib.vcs import guess repo = guess(os.path.abspath(os.getcwd())) if repo: @@ -69,6 +79,15 @@ def branch(): def cwd(dir_shorten_len=None, dir_limit_depth=None): + '''Return the current working directory. + + Returns a segment list to create a breadcrumb-like effect. + + :param int dir_shorten_len: + shorten parent directory names to this length (e.g. :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) + :param int dir_limit_depth: + limit directory depth to this number (e.g. :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) + ''' import re try: cwd = os.getcwdu() @@ -97,16 +116,40 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): def date(format='%Y-%m-%d'): + '''Return the current date. + + :param str format: + strftime-style date format string + ''' from datetime import datetime return datetime.now().strftime(format) @memoize(600) def external_ip(query_url='http://ipv4.icanhazip.com/'): + '''Return external IP address. + + Suggested URIs: + + * http://ipv4.icanhazip.com/ + * http://ipv6.icanhazip.com/ + * http://icanhazip.com/ (returns IPv6 address if available, else IPv4) + + :param str query_url: + URI to query for IP address, should return only the IP address as a text string + ''' return _urllib_read(query_url).strip() def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): + '''Return system uptime. + + Uses the ``psutil`` module if available for multi-platform compatibility, + falls back to reading :file:`/proc/uptime`. + + :param str format: + format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments + ''' try: import psutil from datetime import datetime @@ -125,6 +168,20 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): @memoize(1800) def weather(unit='c', location_query=None): + '''Return weather from Yahoo! Weather. + + Uses GeoIP lookup from http://freegeoip.net/ to automatically determine + your current location. This should be changed if you're in a VPN or if your + IP address is registered at another location. + + Returns a list of colorized icon and temperature segments depending on + weather conditions. + + :param str unit: + temperature unit, can be one of ``F``, ``C`` or ``K`` + :param str location_query: + location query for your current location, e.g. ``oslo, norway`` + ''' import json if not location_query: @@ -164,6 +221,19 @@ def weather(unit='c', location_query=None): def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): + '''Return normalized system load average. + + Highlights using ``system_load_good``, ``system_load_bad`` and + ``system_load_ugly`` highlighting groups, depending on the thresholds + passed to the function. + + :param str format: + format string, receives ``avg`` as an argument + :param float threshold_good: + threshold for "good load" highlighting + :param float threshold_bad: + threshold for "bad load" highlighting + ''' import multiprocessing cpu_count = multiprocessing.cpu_count() ret = [] @@ -187,6 +257,13 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): def cpu_load_percent(measure_interval=.5): + '''Return the average CPU load as a percentage. + + Requires the ``psutil`` module. + + :param float measure_interval: + interval used to measure CPU load (in seconds) + ''' try: import psutil except ImportError: @@ -196,6 +273,21 @@ def cpu_load_percent(measure_interval=.5): def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_prefix=False): + '''Return the network load. + + Uses the ``psutil`` module if available for multi-platform compatibility, + falls back to reading + :file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`. + + :param str interface: + network interface to measure + :param float measure_interval: + interval used to measure the network load (in seconds) + :param str suffix: + string appended to each load string + :param bool binary_prefix: + use binary prefix, e.g. MiB instead of MB + ''' import time from powerline.lib import humanize_bytes @@ -229,11 +321,25 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref def virtualenv(): + '''Return the name of the current Python virtualenv.''' return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None @memoize(60) def email_imap_alert(username, password, server='imap.gmail.com', port=993, folder='INBOX'): + '''Return unread e-mail count for IMAP servers. + + :param str username: + login username + :param str password: + login password + :param str server: + e-mail server + :param int port: + e-mail server port + :param str folder: + folder to check for e-mails + ''' import imaplib import re diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 5b45b02c..29c6a562 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -2,11 +2,13 @@ def last_status(segment_info): + '''Return last exit code.''' return str(segment_info.last_exit_code) if segment_info.last_exit_code else None last_status.requires_powerline_segment_info = True def last_pipe_status(segment_info): + '''Return last pipe status.''' if any(segment_info.last_pipe_status): return [{"contents": str(status), "highlight_group": "exit_fail" if status else "exit_success"} for status in segment_info.last_pipe_status] diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 1c1217f1..6ddc00f2 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -51,11 +51,8 @@ mode_translations = { def mode(override=None): '''Return the current vim mode. - This function returns a tuple with the shorthand mode and the mode expanded - into a descriptive string. The longer string can be overridden by providing - a dict with the mode and the new string:: - - mode = mode({ 'n': 'NORM' }) + :param dict override: + dict for overriding default mode strings, e.g. ``{ 'n': 'NORM' }`` ''' mode = vim_funcs['mode']().decode('utf-8') mode = mode_translations.get(mode, mode) @@ -68,22 +65,38 @@ def mode(override=None): def modified_indicator(text=u'+'): - '''Return a file modified indicator.''' + '''Return a file modified indicator. + + :param string text: + text to display if the current buffer is modified + ''' return text if int(vim.eval('&modified')) else None def paste_indicator(text='PASTE'): - '''Return a paste mode indicator.''' + '''Return a paste mode indicator. + + :param string text: + text to display if paste mode is enabled + ''' return text if int(vim.eval('&paste')) else None def readonly_indicator(text=u''): - '''Return a read-only indicator.''' + '''Return a read-only indicator. + + :param string text: + text to display if the current buffer is read-only + ''' return text if int(vim.eval('&readonly')) else None def file_directory(shorten_home=False): - '''Return file directory (head component of the file path).''' + '''Return file directory (head component of the file path). + + :param bool shorten_home: + shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. + ''' file_directory = vim_funcs['expand']('%:~:.:h') if file_directory is None: return None @@ -93,7 +106,13 @@ def file_directory(shorten_home=False): def file_name(display_no_file=False, no_file_text='[No file]'): - '''Return file name (tail component of the file path).''' + '''Return file name (tail component of the file path). + + :param bool display_no_file: + display a string if the buffer is missing a file name + :param str no_file_text: + the string to display if the buffer is missing a file name + ''' file_name = vim_funcs['expand']('%:~:.:t') if not file_name and not display_no_file: return None @@ -109,8 +128,11 @@ def file_name(display_no_file=False, no_file_text='[No file]'): def file_size(suffix='B', binary_prefix=False): '''Return file size. - Returns None if the file isn't saved, or if the size is too - big to fit in a number. + :param str suffix: + string appended to the file size + :param bool binary_prefix: + use binary prefix, e.g. MiB instead of MB + :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' file_name = vim_funcs['expand']('%') file_size = vim_funcs['getfsize'](file_name) @@ -122,7 +144,7 @@ def file_size(suffix='B', binary_prefix=False): def file_format(): '''Return file format (i.e. line ending type). - Returns None for unknown or missing file format. + :return: file format or None if unknown or missing file format ''' return vim.eval('&fileformat') or None @@ -130,7 +152,7 @@ def file_format(): def file_encoding(): '''Return file encoding/character set. - Returns None for unknown or missing file encoding. + :return: file encoding/character set or None if unknown or missing file encoding ''' return vim.eval('&fileencoding') or None @@ -138,13 +160,17 @@ def file_encoding(): def file_type(): '''Return file type. - Returns None for unknown file types. + :return: file type or None if unknown file type ''' return vim.eval('&filetype') or None def line_percent(gradient=False): - '''Return the cursor position in the file as a percentage.''' + '''Return the cursor position in the file as a percentage. + + :param bool gradient: + highlight the percentage with a color gradient (by default a green to red gradient) + ''' line_current = vim_funcs['line']('.') line_last = vim_funcs['line']('$') percentage = int(line_current * 100 // line_last) @@ -164,23 +190,30 @@ def line_current(): def col_current(virtcol=True): '''Return the current cursor column. - If the optional argument is True then returns visual column with concealed - characters ignored (default), else returns byte offset. + :param bool virtcol: + return visual column with concealed characters ingored ''' return vim_funcs['virtcol' if virtcol else 'col']('.') -def modified_buffers(text=u'+'): - '''Return a comma-separated list of modified buffers.''' +def modified_buffers(text=u'+', join_str=','): + '''Return a comma-separated list of modified buffers. + + :param str text: + text to display before the modified buffer list + :param str join_str: + string to use for joining the modified buffer list + ''' buffer_len = int(vim.eval('bufnr("$")')) buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if vim.eval('getbufvar({0}, "&mod")'.format(bufnr)) == '1'] if buffer_mod: - return u'{0} {1}'.format(text, ','.join(buffer_mod)) + return u'{0} {1}'.format(text, join_str.join(buffer_mod)) return None @memoize(2) def branch(): + '''Return the current working branch.''' repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) if repo: return repo.branch() @@ -190,6 +223,7 @@ def branch(): # TODO Drop cache on BufWrite event @memoize(2, additional_key=lambda: vim.current.buffer.number) def file_vcs_status(): + '''Return the VCS status for this buffer.''' if vim.current.buffer.name and not vim.eval('&buftype'): repo = guess(os.path.abspath(vim.current.buffer.name)) if repo: @@ -210,6 +244,7 @@ def file_vcs_status(): @memoize(2) def repository_status(): + '''Return the status for the current repo.''' repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) if repo: return repo.status() From a6b3bc93abc5ec01dc057450140a34d2cd7c50c9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Feb 2013 22:13:09 +0400 Subject: [PATCH 0351/1472] =?UTF-8?q?Don=E2=80=99t=20mess=20with=20hl()+co?= =?UTF-8?q?ntents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What does hl() mean? It looks like “highlight an empty string”, but is actually “reset highlight style”. In addition if you are writing “hl()” function for new renderer you need to care about “contents” variable (in two places!: None for super() call and (contents or u'') after super() call) though function is actually doing nothing with it. It is just inconvenient. --- powerline/renderer.py | 9 ++++++--- powerline/renderers/bash_prompt.py | 4 ++-- powerline/renderers/ipython.py | 4 ++-- powerline/renderers/pango_markup.py | 10 ++++++---- powerline/renderers/shell.py | 4 ++-- powerline/renderers/tmux.py | 4 ++-- powerline/renderers/vim.py | 4 ++-- powerline/renderers/zsh_prompt.py | 4 ++-- 8 files changed, 24 insertions(+), 19 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 1cc5eaa8..75754d4a 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -52,7 +52,7 @@ class Renderer(object): if not width: # No width specified, so we don't need to crop or pad anything - return self._returned_value(u''.join([segment['_rendered_hl'] for segment in segments]) + self.hl(), segments, output_raw) + return self._returned_value(u''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] @@ -75,7 +75,7 @@ class Renderer(object): segment['_space_right'] += space_side segments_spacers[0]['_space_right'] += distribute_len_remainder - rendered_highlighted = u''.join([segment['_rendered_hl'] for segment in self._render_segments(mode, theme, segments)]) + self.hl() + rendered_highlighted = u''.join([segment['_rendered_hl'] for segment in self._render_segments(mode, theme, segments)]) + self.hlstyle() return self._returned_value(rendered_highlighted, segments, output_raw) @@ -177,5 +177,8 @@ class Renderer(object): b = int & 0xff return r, g, b - def hl(self, contents=None, fg=None, bg=None, attr=None): + def hlstyle(fg=None, bg=None, attr=None): raise NotImplementedError + + def hl(self, contents, fg=None, bg=None, attr=None): + return self.hlstyle(fg, bg, attr) + (contents or u'') diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index dbc30692..82e87dce 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -5,13 +5,13 @@ from powerline.renderers.shell import ShellRenderer class BashPromptRenderer(ShellRenderer): '''Powerline bash prompt segment renderer.''' - def hl(self, contents=None, fg=None, bg=None, attr=None): + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. Returns the default ShellRenderer escape sequence with \[...\] wrapped around it (required in bash prompts). ''' - return '\[' + super(BashPromptRenderer, self).hl(None, fg, bg, attr) + '\]' + (contents or u'') + return '\[' + super(BashPromptRenderer, self).hlstyle(fg, bg, attr) + '\]' @staticmethod def escape(string): diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index e8122093..8c6e5a54 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -5,5 +5,5 @@ from powerline.renderers.shell import ShellRenderer class IpythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' - def hl(self, *args, **kwargs): - return '\x01' + super(IpythonRenderer, self).hl(*args, **kwargs) + '\x02' + def hlstyle(self, *args, **kwargs): + return '\x01' + super(IpythonRenderer, self).hlstyle(*args, **kwargs) + '\x02' diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index b49cc618..b3c40ab2 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -6,11 +6,13 @@ from powerline.renderer import Renderer class PangoMarkupRenderer(Renderer): '''Powerline Pango markup segment renderer.''' - def hl(self, contents=None, fg=None, bg=None, attr=None): - '''Highlight a segment.''' + @staticmethod + def hlstyle(*args, **kwargs): # We don't need to explicitly reset attributes, so skip those calls - if not contents or (not attr and not bg and not fg): - return '' + return '' + + def hl(self, contents, fg=None, bg=None, attr=None): + '''Highlight a segment.''' awesome_attr = [] if fg is not None: if fg is not False and fg[1] is not False: diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index d4708b34..deaf2169 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -5,7 +5,7 @@ from powerline.renderer import Renderer class ShellRenderer(Renderer): '''Powerline shell segment renderer.''' - def hl(self, contents=None, fg=None, bg=None, attr=None): + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. If an argument is None, the argument is ignored. If an argument is @@ -35,7 +35,7 @@ class ShellRenderer(Renderer): else: if attr & Renderer.ATTR_BOLD: ansi += [1] - return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + (contents or u'') + return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) @staticmethod def escape(string): diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index df232a15..ce9e4ff0 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -5,7 +5,7 @@ from powerline.renderer import Renderer class TmuxRenderer(Renderer): '''Powerline tmux segment renderer.''' - def hl(self, contents=None, fg=None, bg=None, attr=None): + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment.''' # We don't need to explicitly reset attributes, so skip those calls if not attr and not bg and not fg: @@ -37,4 +37,4 @@ class TmuxRenderer(Renderer): tmux_attr += ['underscore'] else: tmux_attr += ['nounderscore'] - return '#[' + ','.join(tmux_attr) + ']' + (contents or u'') + return '#[' + ','.join(tmux_attr) + ']' diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 6548f9b9..86a587b5 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -48,7 +48,7 @@ class VimRenderer(Renderer): def escape(string): return string.replace('%', '%%') - def hl(self, contents=None, fg=None, bg=None, attr=None): + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. If an argument is None, the argument is ignored. If an argument is @@ -97,4 +97,4 @@ class VimRenderer(Renderer): 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'] + '#' + (contents or u'') + return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 6d292ccd..08e0977b 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -5,13 +5,13 @@ from powerline.renderers.shell import ShellRenderer class ZshPromptRenderer(ShellRenderer): '''Powerline zsh prompt segment renderer.''' - def hl(self, contents=None, fg=None, bg=None, attr=None): + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. Returns the default ShellRenderer escape sequence with %{...%} wrapped around it (required in zsh prompts). ''' - return '%{' + super(ZshPromptRenderer, self).hl(None, fg, bg, attr) + '%}' + (contents or u'') + return '%{' + super(ZshPromptRenderer, self).hlstyle(None, fg, bg, attr) + '%}' @staticmethod def escape(string): From 4202bd2ee61ff7be08b32fd3d9be582acbfb7116 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Feb 2013 22:23:15 +0400 Subject: [PATCH 0352/1472] Use escape_hl_* variables for prompts deriving from ShellRenderer This is faster then super() calls and also more convenient. Fixes #142 just as well Conflicts: powerline/renderers/bash_prompt.py powerline/renderers/ipython.py powerline/renderers/shell.py powerline/renderers/zsh_prompt.py --- powerline/renderers/bash_prompt.py | 9 ++------- powerline/renderers/ipython.py | 4 ++-- powerline/renderers/shell.py | 5 ++++- powerline/renderers/zsh_prompt.py | 9 ++------- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index 82e87dce..875527b8 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -5,13 +5,8 @@ from powerline.renderers.shell import ShellRenderer class BashPromptRenderer(ShellRenderer): '''Powerline bash prompt segment renderer.''' - def hlstyle(self, fg=None, bg=None, attr=None): - '''Highlight a segment. - - Returns the default ShellRenderer escape sequence with \[...\] wrapped - around it (required in bash prompts). - ''' - return '\[' + super(BashPromptRenderer, self).hlstyle(fg, bg, attr) + '\]' + escape_hl_start = '\[' + escape_hl_end = '\]' @staticmethod def escape(string): diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 8c6e5a54..f00573af 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -5,5 +5,5 @@ from powerline.renderers.shell import ShellRenderer class IpythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' - def hlstyle(self, *args, **kwargs): - return '\x01' + super(IpythonRenderer, self).hlstyle(*args, **kwargs) + '\x02' + escape_hl_start = '\x01' + escape_hl_end = '\x02' diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index deaf2169..89b5b666 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -5,6 +5,9 @@ from powerline.renderer import Renderer class ShellRenderer(Renderer): '''Powerline shell segment renderer.''' + escape_hl_start = '' + escape_hl_end = '' + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -35,7 +38,7 @@ class ShellRenderer(Renderer): else: if attr & Renderer.ATTR_BOLD: ansi += [1] - return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + return self.escape_hl_start + '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + self.escape_hl_end @staticmethod def escape(string): diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 08e0977b..52c85dc5 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -5,13 +5,8 @@ from powerline.renderers.shell import ShellRenderer class ZshPromptRenderer(ShellRenderer): '''Powerline zsh prompt segment renderer.''' - def hlstyle(self, fg=None, bg=None, attr=None): - '''Highlight a segment. - - Returns the default ShellRenderer escape sequence with %{...%} wrapped - around it (required in zsh prompts). - ''' - return '%{' + super(ZshPromptRenderer, self).hlstyle(None, fg, bg, attr) + '%}' + escape_hl_start = '%{' + escape_hl_end = '%}' @staticmethod def escape(string): From 9a0b970b07d364673156767d752c1a9cd67f3f1f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Feb 2013 11:15:33 +0400 Subject: [PATCH 0353/1472] Clear highlight groups on ColorScheme event Closes #196 Fixes #153 Fixes #194 --- powerline/bindings/vim/plugin/powerline.vim | 1 + powerline/renderers/vim.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index a7284117..cb7fa829 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -72,4 +72,5 @@ augroup Powerline autocmd! autocmd BufEnter,BufWinEnter,WinEnter,CmdwinEnter * call s:UpdateWindows(0) | redrawstatus autocmd CmdwinLeave * call s:UpdateWindows(1) + autocmd ColorScheme * exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' augroup END diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 86a587b5..d3c58204 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -44,6 +44,9 @@ class VimRenderer(Renderer): statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) return statusline + def reset_highlight(self): + self.hl_groups.clear() + @staticmethod def escape(string): return string.replace('%', '%%') From da5ac610625741d551917abdba503043a0e43141 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Feb 2013 11:26:11 +0400 Subject: [PATCH 0354/1472] Fix location of powerline.vim file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rm is uncommented because there is no “source_plugin” now. --- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index 8a76124c..fa3316fb 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 1893 SHA256 89fb4b832b6ef2eda7b9d013ab7471cf9252f68266428cf9ad2e04b913ee44d4 SHA512 606110718187f98adb7dae2c7ff8b90288b7a7613e06a92a16ecac7ca98ef6acbde2c44ee3364bd5878315b5dc00bc8ae0bd4ef84300b93e3ed39827c8cc004b WHIRLPOOL 332205cc8139e30907f83d8299f2bea9fd9adf9de518397bdd74d7ab1baaa0d1bc61756cb6844405e81583e7e6b11006c5f9c7f9a82f663a4f4a9cceadd99513 +EBUILD powerline-9999.ebuild 2043 SHA256 b7031c4706b0106571004a483a89c712507014322ddfc2da93e95d6a7a205592 SHA512 0556fdba996375ef3e306de22cf91f6984268958e8ffebef58549618eb876f3049486c76396198af1335b52673682697247cbe2fcc11cbc371ba4b9ecebeab98 WHIRLPOOL b72d70a3703366514a18b2c6b5353b21ae44275efda4df3b35a3917c11706a61d6e6c662682f776e888d179a8de4fa18fae2770eb502dfec5182c3468ecd2715 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 7eeca444..f88f2bbc 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -18,7 +18,7 @@ SRC_URI="" LICENSE="CC-Attribution-ShareAlike-3.0" SLOT="0" -KEYWORDS="~alpha ~amd64 arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" +KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" IUSE="vim zsh doc" #if LIVE @@ -51,9 +51,11 @@ src_install() { font_src_install if use vim ; then insinto /usr/share/vim/vimfiles/plugin - doins powerline/bindings/vim/powerline.vim + # Don't do sys.path.append, it points to wrong location + sed -i -e '/sys\.path\.append/d' powerline/bindings/vim/plugin/powerline.vim + doins powerline/bindings/vim/plugin/powerline.vim fi - # rm powerline/bindings/vim/powerline.vim + rm powerline/bindings/vim/plugin/powerline.vim if use zsh ; then insinto /usr/share/zsh/site-contrib doins powerline/bindings/zsh/powerline.zsh From d414a7e49130309b46e3f1e0d58693354b7abed7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Feb 2013 12:58:47 +0400 Subject: [PATCH 0355/1472] Add more USE flags to gentoo ebuild --- packages/gentoo/app-misc/powerline/Manifest | 2 +- .../app-misc/powerline/powerline-9999.ebuild | 49 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index fa3316fb..107024e6 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 2043 SHA256 b7031c4706b0106571004a483a89c712507014322ddfc2da93e95d6a7a205592 SHA512 0556fdba996375ef3e306de22cf91f6984268958e8ffebef58549618eb876f3049486c76396198af1335b52673682697247cbe2fcc11cbc371ba4b9ecebeab98 WHIRLPOOL b72d70a3703366514a18b2c6b5353b21ae44275efda4df3b35a3917c11706a61d6e6c662682f776e888d179a8de4fa18fae2770eb502dfec5182c3468ecd2715 +EBUILD powerline-9999.ebuild 3428 SHA256 56a885903451b133dfd3ff3567613bd04d63242a2a3f6aace3d22eded39f2dcd SHA512 3861c0bd9170ea4d6c28d3a57a63f15ee6c710c4636c41ae4272259a6ddfd0f4a865f851d176115edf95eb335a14cb01fd6c8b8e9bb6b8b393b1025461e53c5c WHIRLPOOL 853fd571b9436ce19a08c4a0bd35c35f6cdb66eeacb4649d1eb235cef2fa47025499cd2d08558913ef2fec09cb21dc62d6c6b2b8f3a252aa8628cd0c181b1523 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index f88f2bbc..c5c42cfa 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -19,7 +19,7 @@ SRC_URI="" LICENSE="CC-Attribution-ShareAlike-3.0" SLOT="0" KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" -IUSE="vim zsh doc" +IUSE="vim zsh doc awesome tmux bash ipython" #if LIVE SRC_URI= @@ -28,7 +28,9 @@ KEYWORDS= S="${WORKDIR}/${PN}" -RDEPEND="vim? ( || ( app-editors/vim[python] app-editors/gvim[python] ) )" +RDEPEND=" + vim? ( || ( app-editors/vim[python] app-editors/gvim[python] ) ) + awesome? ( >=x11-wm/awesome-3.5 )" DEPEND="doc? ( dev-python/sphinx dev-python/docutils )" FONT_SUFFIX="otf" @@ -60,12 +62,49 @@ src_install() { insinto /usr/share/zsh/site-contrib doins powerline/bindings/zsh/powerline.zsh elog "" - elog "To enable powerline prompt add" + elog "To enable powerline prompt in zsh add" elog " . /usr/share/zsh/site-contrib/powerline.zsh" - elog "to your zshrc." - elog "" + elog "to your .zshrc." fi rm powerline/bindings/zsh/powerline.zsh + if use awesome ; then + elog "" + elog "To enable powerline statusline in awesome add" + elog " require(\"powerline\")" + elog "and" + elog " right_layout:add(powerline_widget)" + elog "to your .config/awesome/rc.lua. Assuming you were using" + elog "/etc/xdg/awesome/rc.lua as a template for you own configuration." + insinto /usr/share/awesome/lib/powerline + mv powerline/bindings/awesome/powerline.lua init.lua + doins init.lua + rm init.lua + exeinto /usr/share/awesome/lib/powerline + doexe powerline/bindings/awesome/powerline.sh + else + rm powerline/bindings/awesome/powerline.lua + fi + rm powerline/bindings/awesome/powerline.sh + # There are no standard location for this, thus using /usr/share/powerline + if use tmux ; then + elog "" + elog "To enable powerline statusline in tmux add" + elog " source /usr/share/powerline/tmux/powerline.conf" + elog "to your .tmux.conf." + insinto /usr/share/powerline/tmux + doins powerline/bindings/tmux/powerline.conf + fi + rm powerline/bindings/tmux/powerline.conf + if use bash ; then + insinto /usr/share/powerline/bash + doins powerline/bindings/bash/powerline.sh + elog "" + elog "To enable powerline prompt in bash add" + elog " . /usr/share/powerline/bash/powerline.sh" + elog "to your .bashrc/.profile." + fi + rm powerline/bindings/bash/powerline.sh + elog "" distutils-r1_src_install use doc && dohtml -r docs_output/* } From 2d9dd21a70eb031155adf55c808404f4f03e4f09 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Feb 2013 22:27:34 +0400 Subject: [PATCH 0356/1472] Add auto_rewrite method to pre_0_11 It raises error without this. --- powerline/bindings/ipython/pre_0_11.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 36c1a315..f48e71a9 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -10,6 +10,12 @@ class PowerlinePrompt(BasePrompt): def set_p_str(self): self.p_str, self.p_str_nocolor = self.powerline.renderer.render(output_raw=True) + self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group()) + self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces - 1 + + def auto_rewrite(self): + # TODO color this + return '%s>%s' % ('-'*self.prompt_text_len, ' '*self.nrspaces) def setup(prompt='1'): From 21b59149bfce5f840e02c59804bdf42bf2cf45e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 10 Feb 2013 14:04:07 +0100 Subject: [PATCH 0357/1472] Move docs to readthedocs.org --- README.rst | 5 +++-- packages/archlinux/python-powerline-git/powerline.install | 2 +- packages/archlinux/python2-powerline-git/powerline.install | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 4166d38e..0acc43b4 100644 --- a/README.rst +++ b/README.rst @@ -8,8 +8,9 @@ Powerline This is the upcoming version of Powerline, implemented in Python. The project is currently in a stable beta and almost ready for release. -* Consult the `documentation `_ for - more information and installation instructions. +* Consult the `documentation + `_ for more information and + installation instructions. * Check out `powerline-fonts `_ for pre-patched versions of popular coding fonts. diff --git a/packages/archlinux/python-powerline-git/powerline.install b/packages/archlinux/python-powerline-git/powerline.install index 8c8a9033..d7938d2b 100644 --- a/packages/archlinux/python-powerline-git/powerline.install +++ b/packages/archlinux/python-powerline-git/powerline.install @@ -14,7 +14,7 @@ If Powerline doesn't work out of the box on your system, please submit an issue on GitHub: https://github.com/Lokaltog/powerline/issues Consult the documentation for detailed installation instructions and -troubleshooting information: http://lokaltog.github.com/powerline/ +troubleshooting information: https://powerline.readthedocs.org/en/latest/ Vim installation ---------------- diff --git a/packages/archlinux/python2-powerline-git/powerline.install b/packages/archlinux/python2-powerline-git/powerline.install index 8c8a9033..d7938d2b 100644 --- a/packages/archlinux/python2-powerline-git/powerline.install +++ b/packages/archlinux/python2-powerline-git/powerline.install @@ -14,7 +14,7 @@ If Powerline doesn't work out of the box on your system, please submit an issue on GitHub: https://github.com/Lokaltog/powerline/issues Consult the documentation for detailed installation instructions and -troubleshooting information: http://lokaltog.github.com/powerline/ +troubleshooting information: https://powerline.readthedocs.org/en/latest/ Vim installation ---------------- From e18665c5fe4e365bd736bc41cf94b9575ac63d34 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Feb 2013 07:43:13 +0400 Subject: [PATCH 0358/1472] Purge out constants from classes, lowercase TERM_24_BIT Remove recursive import as well --- powerline/colorscheme.py | 18 ++++++++++-------- powerline/renderer.py | 11 ++++------- powerline/renderers/pango_markup.py | 7 ++++--- powerline/renderers/shell.py | 7 ++++--- powerline/renderers/tmux.py | 7 ++++--- powerline/renderers/vim.py | 7 ++++--- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index f8801c08..4fbcfe8e 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,13 +1,16 @@ # -*- coding: utf-8 -*- +DEFAULT_MODE_KEY = None +ATTR_BOLD = 1 +ATTR_ITALIC = 2 +ATTR_UNDERLINE = 4 class Colorscheme(object): - DEFAULT_MODE_KEY = '__default__' def __init__(self, colorscheme): '''Initialize a colorscheme.''' self.colors = {} - self.modes_groups = {self.DEFAULT_MODE_KEY: {}} + self.modes_groups = {DEFAULT_MODE_KEY: {}} # Create a dict of color tuples with both a cterm and hex value for color_name, color in colorscheme['colors'].items(): @@ -19,7 +22,7 @@ class Colorscheme(object): # 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] = { + self.modes_groups[DEFAULT_MODE_KEY][group_name] = { 'fg': self.colors[group_props['fg']], 'bg': self.colors[group_props['bg']], 'attr': group_attr_flag, @@ -67,7 +70,7 @@ class Colorscheme(object): the default mode is returned. ''' if not mode or mode not in self.modes_groups: - mode = self.DEFAULT_MODE_KEY + mode = DEFAULT_MODE_KEY try: return self.modes_groups[mode][group] except TypeError: @@ -79,14 +82,13 @@ class Colorscheme(object): 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 + attr_flag |= ATTR_BOLD if 'italic' in attributes: - attr_flag |= Renderer.ATTR_ITALIC + attr_flag |= ATTR_ITALIC if 'underline' in attributes: - attr_flag |= Renderer.ATTR_UNDERLINE + attr_flag |= ATTR_UNDERLINE return attr_flag cterm_to_hex = { diff --git a/powerline/renderer.py b/powerline/renderer.py index 75754d4a..1b9e90e3 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,21 +1,18 @@ # -*- coding: utf-8 -*- -from powerline.colorscheme import Colorscheme +from powerline.colorscheme import DEFAULT_MODE_KEY from powerline.theme import Theme class Renderer(object): - ATTR_BOLD = 1 - ATTR_ITALIC = 2 - ATTR_UNDERLINE = 4 - TERM_24BIT_COLORS = False + term_truecolor = False def __init__(self, theme_config, local_themes, theme_kwargs, term_24bit_colors=False): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs - self.TERM_24BIT_COLORS = term_24bit_colors + self.term_truecolor = term_24bit_colors def add_local_theme(self, matcher, theme): if matcher in self.local_themes: @@ -92,7 +89,7 @@ class Renderer(object): ''' segments_len = len(segments) try: - mode = mode if mode in segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY + mode = mode if mode in segments[0]['highlight'] else DEFAULT_MODE_KEY except IndexError: pass diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index b3c40ab2..f8e3ed88 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE class PangoMarkupRenderer(Renderer): @@ -21,10 +22,10 @@ class PangoMarkupRenderer(Renderer): if bg is not False and bg[1] is not False: awesome_attr += ['background="#{0:06x}"'.format(bg[1])] if attr is not None and attr is not False: - if attr & Renderer.ATTR_BOLD: + if attr & ATTR_BOLD: awesome_attr += ['font_weight="bold"'] - if attr & Renderer.ATTR_ITALIC: + if attr & ATTR_ITALIC: awesome_attr += ['font_style="italic"'] - if attr & Renderer.ATTR_UNDERLINE: + if attr & ATTR_UNDERLINE: awesome_attr += ['underline="single"'] return '' + contents + '' diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 89b5b666..9efc4b8c 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE class ShellRenderer(Renderer): @@ -20,7 +21,7 @@ class ShellRenderer(Renderer): if fg is False or fg[0] is False: ansi += [39] else: - if self.TERM_24BIT_COLORS: + if self.term_truecolor: ansi += [38, 2] + list(self._int_to_rgb(fg[1])) else: ansi += [38, 5, fg[0]] @@ -28,7 +29,7 @@ class ShellRenderer(Renderer): if bg is False or bg[0] is False: ansi += [49] else: - if self.TERM_24BIT_COLORS: + if self.term_truecolor: ansi += [48, 2] + list(self._int_to_rgb(bg[1])) else: ansi += [48, 5, bg[0]] @@ -36,7 +37,7 @@ class ShellRenderer(Renderer): if attr is False: ansi += [22] else: - if attr & Renderer.ATTR_BOLD: + if attr & ATTR_BOLD: ansi += [1] return self.escape_hl_start + '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + self.escape_hl_end diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index ce9e4ff0..3b122dcf 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE class TmuxRenderer(Renderer): @@ -25,15 +26,15 @@ class TmuxRenderer(Renderer): if attr is False: tmux_attr += ['nobold', 'noitalics', 'nounderscore'] else: - if attr & Renderer.ATTR_BOLD: + if attr & ATTR_BOLD: tmux_attr += ['bold'] else: tmux_attr += ['nobold'] - if attr & Renderer.ATTR_ITALIC: + if attr & ATTR_ITALIC: tmux_attr += ['italics'] else: tmux_attr += ['noitalics'] - if attr & Renderer.ATTR_UNDERLINE: + if attr & ATTR_UNDERLINE: tmux_attr += ['underscore'] else: tmux_attr += ['nounderscore'] diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index d3c58204..798adcf2 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -4,6 +4,7 @@ from __future__ import absolute_import from powerline.bindings.vim import vim_get_func from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE import vim @@ -79,11 +80,11 @@ class VimRenderer(Renderer): hl_group['guibg'] = bg[1] if attr: hl_group['attr'] = [] - if attr & self.ATTR_BOLD: + if attr & ATTR_BOLD: hl_group['attr'].append('bold') - if attr & self.ATTR_ITALIC: + if attr & ATTR_ITALIC: hl_group['attr'].append('italic') - if attr & self.ATTR_UNDERLINE: + if attr & ATTR_UNDERLINE: hl_group['attr'].append('underline') hl_group['name'] = 'Pl_' + \ str(hl_group['ctermfg']) + '_' + \ From 52635d05ae483a62e959311a026a9dddaeb80c5e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Feb 2013 07:50:23 +0400 Subject: [PATCH 0359/1472] Add support for italic and underline attributes in shell renderer --- powerline/renderers/shell.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 9efc4b8c..42023ef6 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -39,6 +39,12 @@ class ShellRenderer(Renderer): else: if attr & ATTR_BOLD: ansi += [1] + elif attr & ATTR_ITALIC: + # Note: is likely not to work or even be inverse in place of + # italic. Omit using this in colorschemes. + ansi += [3] + elif attr & ATTR_UNDERLINE: + ansi += [4] return self.escape_hl_start + '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + self.escape_hl_end @staticmethod From d12299c18c44a5bdd0aff34d064b88dafb8b4b0b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Feb 2013 08:37:58 +0400 Subject: [PATCH 0360/1472] Add a way to specify renderer options on command-line, added tmux_escape Note: tmux escaping does not fix things, on my system it produces ![Gap between ... and directory name](http://img-fotki.yandex.ru/get/6439/9151298.2/0_93b4c_5d9a317f_orig.png) Fixes #199 --- powerline/core.py | 7 ++++--- powerline/renderer.py | 7 ++----- powerline/renderers/shell.py | 7 ++++++- scripts/powerline | 14 +++++++++++++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 89b8b5de..6f4d5fe7 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -11,7 +11,7 @@ from powerline.lib import underscore_to_camelcase class Powerline(object): - def __init__(self, ext, renderer_module=None, segment_info=None): + def __init__(self, ext, renderer_module=None, segment_info=None, renderer_options={}): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) config_path = os.path.join(config_home, 'powerline') plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') @@ -51,8 +51,9 @@ class Powerline(object): except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) - self.renderer = Renderer(theme_config, local_themes, theme_kwargs, - term_24bit_colors=self.config.get('term_24bit_colors', False)) + options = {'term_truecolor': self.config.get('term_24bit_colors', False)} + options.update(renderer_options) + self.renderer = Renderer(theme_config, local_themes, theme_kwargs, **options) def add_local_theme(self, key, config): '''Add local themes at runtime (e.g. during vim session). diff --git a/powerline/renderer.py b/powerline/renderer.py index 1b9e90e3..19f50d19 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -5,14 +5,11 @@ from powerline.theme import Theme class Renderer(object): - - term_truecolor = False - - def __init__(self, theme_config, local_themes, theme_kwargs, term_24bit_colors=False): + def __init__(self, theme_config, local_themes, theme_kwargs, **options): + self.__dict__.update(options) self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs - self.term_truecolor = term_24bit_colors def add_local_theme(self, matcher, theme): if matcher in self.local_themes: diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 42023ef6..fe3d7a3c 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -8,6 +8,8 @@ class ShellRenderer(Renderer): '''Powerline shell segment renderer.''' escape_hl_start = '' escape_hl_end = '' + term_truecolor = False + tmux_escape = False def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -45,7 +47,10 @@ class ShellRenderer(Renderer): ansi += [3] elif attr & ATTR_UNDERLINE: ansi += [4] - return self.escape_hl_start + '[{0}m'.format(';'.join(str(attr) for attr in ansi)) + self.escape_hl_end + r = '\033[{0}m'.format(';'.join(str(attr) for attr in ansi)) + if self.tmux_escape: + r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\' + return self.escape_hl_start + r + self.escape_hl_end @staticmethod def escape(string): diff --git a/scripts/powerline b/scripts/powerline index fb2d4bc1..acdcf577 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -3,6 +3,7 @@ '''Powerline prompt and statusline script.''' import argparse import sys +import json try: from powerline.core import Powerline @@ -11,6 +12,16 @@ except ImportError: sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from powerline.core import Powerline # NOQA +def oval(s): + if '=' not in s: + raise TypeError('Option must look like option=json_value') + if s[0] == '_': + raise ValueError('Option names must not start with `_\'') + idx = s.index('=') + o = s[:idx] + val = json.loads(s[idx+1:]) + return (o, val) + parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('ext', nargs=1) parser.add_argument('side', nargs='?', choices=('left', 'right')) @@ -18,10 +29,11 @@ parser.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) parser.add_argument('-w', '--width', type=int) parser.add_argument('--last_exit_code', metavar='INT', type=int) parser.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) +parser.add_argument('-o', '--renderer_option', nargs='*', metavar='OPTION=VALUE', type=oval) if __name__ == '__main__': args = parser.parse_args() - powerline = Powerline(ext=args.ext[0], renderer_module=args.renderer_module, segment_info=args) + powerline = Powerline(ext=args.ext[0], renderer_module=args.renderer_module, segment_info=args, renderer_options=dict(args.renderer_option or {})) rendered = powerline.renderer.render(width=args.width, side=args.side) try: sys.stdout.write(rendered) From 28547930cc8ddf50755703ccce3535255aa4ffbd Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Feb 2013 08:47:41 +0400 Subject: [PATCH 0361/1472] Add screen_escape option Refs #199. --- powerline/renderers/shell.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index fe3d7a3c..a322ce4e 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -10,6 +10,7 @@ class ShellRenderer(Renderer): escape_hl_end = '' term_truecolor = False tmux_escape = False + screen_escape = False def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -50,6 +51,8 @@ class ShellRenderer(Renderer): r = '\033[{0}m'.format(';'.join(str(attr) for attr in ansi)) if self.tmux_escape: r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\' + elif self.screen_escape: + r = '\033P' + r.replace('\033', '\033\033') + '\033\\' return self.escape_hl_start + r + self.escape_hl_end @staticmethod From d9611164858667449e6cb95effa2be3cbf5b9285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Feb 2013 07:20:39 +0100 Subject: [PATCH 0362/1472] Move weather condition codes into weather function --- powerline/segments/common.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index bae7b129..703e5668 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -5,19 +5,6 @@ import sys from powerline.lib import memoize -# Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes -weather_conditions_codes = { - u'〇': [25, 34], - u'⚑': [24], - u'☔': [5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47], - u'☁': [26, 27, 28, 29, 30, 44], - u'❅': [7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46], - u'☈': [0, 1, 2, 3, 4, 37, 38, 39], - u'〰': [19, 20, 21, 22, 23], - u'☼': [32, 36], - u'☾': [31, 33], -} - def _urllib_read(url): try: @@ -184,6 +171,19 @@ def weather(unit='c', location_query=None): ''' import json + # Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes + weather_conditions_codes = { + u'〇': [25, 34], + u'⚑': [24], + u'☔': [5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47], + u'☁': [26, 27, 28, 29, 30, 44], + u'❅': [7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46], + u'☈': [0, 1, 2, 3, 4, 37, 38, 39], + u'〰': [19, 20, 21, 22, 23], + u'☼': [32, 36], + u'☾': [31, 33], + } + if not location_query: try: location = json.loads(_urllib_read('http://freegeoip.net/json/' + external_ip())) From e48645f5d8124a2073093dc8961244f26932e720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Feb 2013 07:23:33 +0100 Subject: [PATCH 0363/1472] Move urllib_read/urlencode functions to lib --- powerline/lib/__init__.py | 1 + powerline/lib/url.py | 26 ++++++++++++++++++++++++++ powerline/segments/common.py | 35 +++++------------------------------ 3 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 powerline/lib/url.py diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 903ca183..5877f65b 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,5 +1,6 @@ from powerline.lib.memoize import memoize # NOQA from powerline.lib.humanize_bytes import humanize_bytes # NOQA +from powerline.lib.url import urllib_read, urllib_urlencode # NOQA def underscore_to_camelcase(string): diff --git a/powerline/lib/url.py b/powerline/lib/url.py new file mode 100644 index 00000000..41a40938 --- /dev/null +++ b/powerline/lib/url.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + + +def urllib_read(url): + try: + import urllib.error + import urllib.request + try: + return urllib.request.urlopen(url, timeout=5).read().decode('utf-8') + except: + return + except ImportError: + import urllib2 + try: + return urllib2.urlopen(url, timeout=5).read() + except urllib2.HTTPError: + return + + +def urllib_urlencode(string): + try: + import urllib.parse + return urllib.parse.urlencode(string) + except ImportError: + import urllib + return urllib.urlencode(string) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 703e5668..780d8264 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -3,32 +3,7 @@ import os import sys -from powerline.lib import memoize - - -def _urllib_read(url): - try: - import urllib.error - import urllib.request - try: - return urllib.request.urlopen(url, timeout=5).read().decode('utf-8') - except: - return - except ImportError: - import urllib2 - try: - return urllib2.urlopen(url, timeout=5).read() - except urllib2.HTTPError: - return - - -def _urllib_urlencode(string): - try: - import urllib.parse - return urllib.parse.urlencode(string) - except ImportError: - import urllib - return urllib.urlencode(string) +from powerline.lib import memoize, urllib_read, urllib_urlencode def hostname(only_if_ssh=False): @@ -125,7 +100,7 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): :param str query_url: URI to query for IP address, should return only the IP address as a text string ''' - return _urllib_read(query_url).strip() + return urllib_read(query_url).strip() def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): @@ -186,7 +161,7 @@ def weather(unit='c', location_query=None): if not location_query: try: - location = json.loads(_urllib_read('http://freegeoip.net/json/' + external_ip())) + location = json.loads(urllib_read('http://freegeoip.net/json/' + external_ip())) location_query = ','.join([location['city'], location['region_name'], location['country_name']]) except (TypeError, ValueError): return None @@ -197,8 +172,8 @@ def weather(unit='c', location_query=None): 'format': 'json' } try: - url = 'http://query.yahooapis.com/v1/public/yql?' + _urllib_urlencode(query_data) - response = json.loads(_urllib_read(url)) + url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data) + response = json.loads(urllib_read(url)) condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) except (KeyError, TypeError, ValueError): From cc83d741ff4b28b4b9fcb60d7e8e555ad66fa2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Feb 2013 13:08:27 +0100 Subject: [PATCH 0364/1472] Create fuzzy_time segment --- powerline/segments/common.py | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 780d8264..2d8127f0 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -87,6 +87,58 @@ def date(format='%Y-%m-%d'): return datetime.now().strftime(format) +def fuzzy_time(): + '''Display the current time as fuzzy time, e.g. "quarter past six".''' + from datetime import datetime + + hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'] + minute_str = { + 5: 'five past', + 10: 'ten past', + 15: 'quarter past', + 20: 'twenty past', + 25: 'twenty-five past', + 30: 'half past', + 35: 'twenty-five to', + 40: 'twenty to', + 45: 'quarter to', + 50: 'ten to', + 55: 'five to', + } + special_case_str = { + (23, 58): 'round about midnight', + (23, 59): 'round about midnight', + (0, 0): 'midnight', + (0, 1): 'round about midnight', + (0, 2): 'round about midnight', + (12, 0): 'noon', + } + + now = datetime.now() + + try: + return special_case_str[(now.hour, now.minute)] + except KeyError: + pass + + hour = now.hour + if now.minute > 32: + if hour == 23: + hour = 0 + else: + hour += 1 + if hour > 11: + hour = hour - 12 + hour = hour_str[hour] + + minute = int(round(now.minute / 5.0) * 5) + if minute == 60 or minute == 0: + return ' '.join([hour, 'o\'clock']) + else: + minute = minute_str[minute] + return ' '.join([minute, hour]) + + @memoize(600) def external_ip(query_url='http://ipv4.icanhazip.com/'): '''Return external IP address. From d638f1d6ea0482e6b5f194d5e2c91c59a2c6f0ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Feb 2013 22:10:09 +0400 Subject: [PATCH 0365/1472] Remove non-current window caching API changes done: - memoize additional_key function now accepts all function arguments - get_theme now receives matcher_info - render now receives segment_info and matcher_info, but segments and themes were removed - due to very different ways of obtaining column information col_current splitted back to col_current and virtcol_current. The former should be false in case of horizontal scrollbind (when &scrollopt contains hor) - added requires_segment_info decorator for convenience Other changes: - removed all vim function calls that were possible to remove - removed direct vim.eval calls --- powerline/bindings/vim/__init__.py | 2 + powerline/bindings/vim/plugin/powerline.vim | 2 +- .../config_files/themes/vim/default.json | 3 +- powerline/lib/memoize.py | 2 +- powerline/matchers/vim.py | 12 +- powerline/renderer.py | 13 +- powerline/renderers/vim.py | 21 ++- powerline/segments/vim.py | 156 ++++++++++++------ powerline/theme.py | 5 + 9 files changed, 137 insertions(+), 79 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 2291bb40..13af748c 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -46,3 +46,5 @@ except AttributeError: return r vim_get_func = VimFunc + +getbufvar = vim_get_func('getbufvar') diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index cb7fa829..8cf4c32c 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -36,7 +36,7 @@ catch finish endtry endtry -exec s:powerline_pycmd 'powerline = Powerline("vim")' +exec s:powerline_pycmd 'powerline = Powerline("vim", segment_info={})' if exists('*'. s:powerline_pyeval) let s:pyeval = function(s:powerline_pyeval) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 29fe36b4..ff206cf2 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -87,11 +87,12 @@ "align": "r" }, { - "name": "col_current", + "name": "virtcol_current", "draw_divider": false, "priority": 30, "before": ":", "width": 3, + "highlight_group": ["col_current"], "align": "l" } ] diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 97cd1103..567ec6ac 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -16,7 +16,7 @@ class memoize(object): @wraps(func) def decorated_function(*args, **kwargs): if self.additional_key: - key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) + key = (func.__name__, args, tuple(kwargs.items()), self.additional_key(*args, **kwargs)) else: key = (func.__name__, args, tuple(kwargs.items())) cached = self._cache.get(key, None) diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index 7a971da6..195f5e40 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -2,12 +2,14 @@ from __future__ import absolute_import -import vim +import os +from powerline.bindings.vim import getbufvar -def help(): - return bool(int(vim.eval('&buftype is# "help"'))) +def help(matcher_info): + return getbufvar(matcher_info['bufnr'], '&buftype') == 'help' -def cmdwin(): - return bool(int(vim.eval('bufname("%") is# "[Command Line]"'))) +def cmdwin(matcher_info): + name = matcher_info['buffer'].name + return name and os.path.basename(name) == '[Command Line]' diff --git a/powerline/renderer.py b/powerline/renderer.py index 19f50d19..8c95721f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -16,9 +16,9 @@ class Renderer(object): raise KeyError('There is already a local theme with given matcher') self.local_themes[matcher] = theme - def get_theme(self): + def get_theme(self, matcher_info): for matcher in self.local_themes.keys(): - if matcher(): + if matcher(matcher_info): match = self.local_themes[matcher] if 'config' in match: match['theme'] = Theme(theme_config=match.pop('config'), **self.theme_kwargs) @@ -26,7 +26,7 @@ class Renderer(object): else: return self.theme - def render(self, mode=None, width=None, theme=None, segments=None, side=None, output_raw=False): + def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -35,8 +35,11 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - theme = theme or self.get_theme() - segments = segments or theme.get_segments(side) + theme = self.get_theme(matcher_info) + segments = theme.get_segments(side) + + if segment_info: + theme.segment_info.update(segment_info) # Handle excluded/included segments for the current mode segments = [segment for segment in segments\ diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 798adcf2..fabd535b 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -9,7 +9,6 @@ from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE 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') @@ -20,7 +19,6 @@ class VimRenderer(Renderer): def __init__(self, *args, **kwargs): super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} - self.window_cache = {} def render(self, winnr, current): '''Render all segments. @@ -30,19 +28,20 @@ class VimRenderer(Renderer): used in non-current windows. ''' window_id = vim_getwinvar(winnr, 'window_id') - winwidth = vim_winwidth(winnr) if current: mode = vim_mode() - theme = self.get_theme() - segments = [segment for segment in theme.get_segments()] - self.window_cache[window_id] = (theme, segments) else: mode = 'nc' - theme, segments = self.window_cache.get(window_id, (None, [])) - for segment in segments: - segment['_space_left'] = 0 - segment['_space_right'] = 0 - statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) + segment_info = { + 'window': vim.windows[winnr - 1], + 'winnr': winnr, + 'mode': mode, + 'window_id': window_id, + } + segment_info['buffer'] = segment_info['window'].buffer + segment_info['bufnr'] = segment_info['buffer'].number + winwidth = segment_info['window'].width + statusline = super(VimRenderer, self).render(mode, winwidth, segment_info=segment_info, matcher_info=segment_info) return statusline def reset_highlight(self): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 6ddc00f2..3490506c 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -8,17 +8,16 @@ try: except ImportError: vim = {} -from powerline.bindings.vim import vim_get_func +from powerline.bindings.vim import vim_get_func, getbufvar +from powerline.theme import requires_segment_info from powerline.lib import memoize, humanize_bytes from powerline.lib.vcs import guess vim_funcs = { - 'col': vim_get_func('col', rettype=int), 'virtcol': vim_get_func('virtcol', rettype=int), - 'expand': vim_get_func('expand'), - 'line': vim_get_func('line', rettype=int), - 'mode': vim_get_func('mode'), + 'fnamemodify': vim_get_func('fnamemodify'), 'getfsize': vim_get_func('getfsize', rettype=int), + 'bufnr': vim_get_func('bufnr', rettype=int), } vim_modes = { @@ -48,13 +47,38 @@ mode_translations = { } -def mode(override=None): +def bufnr(segment_info, *args, **kwargs): + '''Used for cache key, returns current buffer number''' + return segment_info['bufnr'] + + +# TODO Remove cache when needed +def window_cached(func): + cache = {} + + def ret(segment_info, *args, **kwargs): + window_id = segment_info['window_id'] + if segment_info['mode'] == 'nc': + return cache.get(window_id) + else: + r = func(*args, **kwargs) + cache[window_id] = r + return r + ret = requires_segment_info(ret) + ret.__name__ = func.__name__ + return ret + + +@requires_segment_info +def mode(segment_info, override=None): '''Return the current vim mode. :param dict override: dict for overriding default mode strings, e.g. ``{ 'n': 'NORM' }`` ''' - mode = vim_funcs['mode']().decode('utf-8') + mode = segment_info['mode'] + if mode == 'nc': + return None mode = mode_translations.get(mode, mode) if not override: return vim_modes[mode] @@ -64,48 +88,54 @@ def mode(override=None): return vim_modes[mode] -def modified_indicator(text=u'+'): +@requires_segment_info +def modified_indicator(segment_info, text=u'+'): '''Return a file modified indicator. :param string text: text to display if the current buffer is modified ''' - return text if int(vim.eval('&modified')) else None + return text if int(getbufvar(segment_info['bufnr'], '&modified')) else None -def paste_indicator(text='PASTE'): +@requires_segment_info +def paste_indicator(segment_info, text='PASTE'): '''Return a paste mode indicator. :param string text: text to display if paste mode is enabled ''' - return text if int(vim.eval('&paste')) else None + return text if int(getbufvar(segment_info['bufnr'], '&paste')) else None -def readonly_indicator(text=u''): +@requires_segment_info +def readonly_indicator(segment_info, text=u''): '''Return a read-only indicator. :param string text: text to display if the current buffer is read-only ''' - return text if int(vim.eval('&readonly')) else None + return text if int(getbufvar(segment_info['bufnr'], '&readonly')) else None -def file_directory(shorten_home=False): +@requires_segment_info +def file_directory(segment_info, shorten_home=False): '''Return file directory (head component of the file path). :param bool shorten_home: shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. ''' - file_directory = vim_funcs['expand']('%:~:.:h') - if file_directory is None: + name = segment_info['buffer'].name + if not name: return None + file_directory = vim_funcs['fnamemodify'](name, ':~:.:h') if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory.decode('utf-8') + os.sep if file_directory else None -def file_name(display_no_file=False, no_file_text='[No file]'): +@requires_segment_info +def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): '''Return file name (tail component of the file path). :param bool display_no_file: @@ -113,19 +143,22 @@ def file_name(display_no_file=False, no_file_text='[No file]'): :param str no_file_text: the string to display if the buffer is missing a file name ''' - file_name = vim_funcs['expand']('%:~:.:t') - if not file_name and not display_no_file: - return None - if not file_name: - return [{ - 'contents': no_file_text, - 'highlight_group': ['file_name_no_file', 'file_name'], - }] + name = segment_info['buffer'].name + if not name: + if display_no_file: + return [{ + 'contents': no_file_text, + 'highlight_group': ['file_name_no_file', 'file_name'], + }] + else: + return None + file_name = vim_funcs['fnamemodify'](name, ':~:.:t') return file_name.decode('utf-8') -@memoize(2) -def file_size(suffix='B', binary_prefix=False): +@requires_segment_info +@memoize(2, additional_key=bufnr) +def file_size(segment_info, suffix='B', binary_prefix=False): '''Return file size. :param str suffix: @@ -134,45 +167,49 @@ def file_size(suffix='B', binary_prefix=False): use binary prefix, e.g. MiB instead of MB :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' - file_name = vim_funcs['expand']('%') + file_name = segment_info['buffer'].name file_size = vim_funcs['getfsize'](file_name) if file_size < 0: return None return humanize_bytes(file_size, suffix, binary_prefix) -def file_format(): +@requires_segment_info +def file_format(segment_info): '''Return file format (i.e. line ending type). :return: file format or None if unknown or missing file format ''' - return vim.eval('&fileformat') or None + return getbufvar(segment_info['bufnr'], '&fileformat') or None -def file_encoding(): +@requires_segment_info +def file_encoding(segment_info): '''Return file encoding/character set. :return: file encoding/character set or None if unknown or missing file encoding ''' - return vim.eval('&fileencoding') or None + return getbufvar(segment_info['bufnr'], '&fileencoding') or None -def file_type(): +@requires_segment_info +def file_type(segment_info): '''Return file type. :return: file type or None if unknown file type ''' - return vim.eval('&filetype') or None + return getbufvar(segment_info['bufnr'], '&filetype') or None -def line_percent(gradient=False): +@requires_segment_info +def line_percent(segment_info, gradient=False): '''Return the cursor position in the file as a percentage. :param bool gradient: highlight the percentage with a color gradient (by default a green to red gradient) ''' - line_current = vim_funcs['line']('.') - line_last = vim_funcs['line']('$') + line_current = segment_info['window'].cursor[0] + line_last = len(segment_info['buffer']) percentage = int(line_current * 100 // line_last) if not gradient: return percentage @@ -182,18 +219,23 @@ def line_percent(gradient=False): }] -def line_current(): +@requires_segment_info +def line_current(segment_info): '''Return the current cursor line.''' - return vim_funcs['line']('.') + return segment_info['window'].cursor[0] -def col_current(virtcol=True): +@requires_segment_info +def col_current(segment_info): '''Return the current cursor column. - - :param bool virtcol: - return visual column with concealed characters ingored ''' - return vim_funcs['virtcol' if virtcol else 'col']('.') + return segment_info['window'].cursor[1] + 1 + + +@window_cached +def virtcol_current(): + '''Return current visual column with concealed characters ingored''' + return vim_funcs['virtcol']('.') def modified_buffers(text=u'+', join_str=','): @@ -204,30 +246,33 @@ def modified_buffers(text=u'+', join_str=','): :param str join_str: string to use for joining the modified buffer list ''' - buffer_len = int(vim.eval('bufnr("$")')) - buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if vim.eval('getbufvar({0}, "&mod")'.format(bufnr)) == '1'] + buffer_len = vim_funcs['bufnr']('$') + buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if int(getbufvar(bufnr, '&modified'))] if buffer_mod: return u'{0} {1}'.format(text, join_str.join(buffer_mod)) return None +@requires_segment_info @memoize(2) -def branch(): +def branch(segment_info): '''Return the current working branch.''' - repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) + repo = guess(os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return repo.branch() return None # TODO Drop cache on BufWrite event -@memoize(2, additional_key=lambda: vim.current.buffer.number) -def file_vcs_status(): +@requires_segment_info +@memoize(2, additional_key=bufnr) +def file_vcs_status(segment_info): '''Return the VCS status for this buffer.''' - if vim.current.buffer.name and not vim.eval('&buftype'): - repo = guess(os.path.abspath(vim.current.buffer.name)) + name = segment_info['buffer'].name + if name and not getbufvar(segment_info['bufnr'], '&buftype'): + repo = guess(os.path.abspath(name)) if repo: - status = repo.status(os.path.relpath(vim.current.buffer.name, repo.directory)) + status = repo.status(os.path.relpath(name, repo.directory)) if not status: return None status = status.strip() @@ -242,10 +287,11 @@ def file_vcs_status(): return None +@requires_segment_info @memoize(2) -def repository_status(): +def repository_status(segment_info): '''Return the status for the current repo.''' - repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) + repo = guess(os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return repo.status() return None diff --git a/powerline/theme.py b/powerline/theme.py index 542b635a..a441e55a 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -11,6 +11,11 @@ except NameError: unicode = str +def requires_segment_info(func): + func.requires_powerline_segment_info = True + return func + + class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config, segment_info=None): self.colorscheme = colorscheme From e89e083feeb62d68fd159f8b6bb9622d238cf2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 12 Feb 2013 10:45:24 +0100 Subject: [PATCH 0366/1472] Don't use kwargs as part of the memoize key This should be fixed later (if at all possible). Also see discussion at http://stackoverflow.com/questions/6407993/how-to-memoize-kwargs Refs #205. --- powerline/lib/memoize.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 567ec6ac..89e29d6f 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -16,10 +16,13 @@ class memoize(object): @wraps(func) def decorated_function(*args, **kwargs): if self.additional_key: - key = (func.__name__, args, tuple(kwargs.items()), self.additional_key(*args, **kwargs)) + key = (func.__name__, args, self.additional_key(*args, **kwargs)) else: - key = (func.__name__, args, tuple(kwargs.items())) - cached = self._cache.get(key, None) + key = (func.__name__, args) + try: + cached = self._cache.get(key, None) + except TypeError: + return func(*args, **kwargs) if cached is None or time.time() - cached['time'] > self.timeout: cached = self._cache[key] = { 'result': func(*args, **kwargs), From 9aee288c18a1cb41b79da75cb59328107088b098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 12 Feb 2013 10:52:17 +0100 Subject: [PATCH 0367/1472] Use requires_segment_info decorator on shell segments --- powerline/segments/shell.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 29c6a562..6ac1200c 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- +from powerline.theme import requires_segment_info + +@requires_segment_info def last_status(segment_info): '''Return last exit code.''' return str(segment_info.last_exit_code) if segment_info.last_exit_code else None -last_status.requires_powerline_segment_info = True +@requires_segment_info def last_pipe_status(segment_info): '''Return last pipe status.''' if any(segment_info.last_pipe_status): @@ -14,4 +17,3 @@ def last_pipe_status(segment_info): for status in segment_info.last_pipe_status] else: return None -last_pipe_status.requires_powerline_segment_info = True From ca4466cd834d81982725f3b0d0d5f5e0fbd73635 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Feb 2013 09:15:10 +0400 Subject: [PATCH 0368/1472] Rework weather segment Allows more precise icon selection and purges out unicode characters from configuration. Note: windy icon needs reworking, I used a trigram for it. Fixes #203. --- .../colorschemes/tmux/default.json | 6 +- .../config_files/colorschemes/wm/default.json | 6 +- powerline/segments/common.py | 113 +++++++++++++++--- 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 0491b021..d701c78a 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -44,9 +44,9 @@ "weather": { "fg": "gray8", "bg": "gray0" }, "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, "weather_temp_hot": { "fg": "weather_temp_hot", "bg": "gray0" }, - "weather_condition_☼": { "fg": "weather_condition_hot", "bg": "gray0" }, - "weather_condition_❅": { "fg": "weather_condition_cold", "bg": "gray0" }, - "weather_condition_☔": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_condition_hot": { "fg": "weather_condition_hot", "bg": "gray0" }, + "weather_condition_snowy": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_condition_rainy": { "fg": "weather_condition_cold", "bg": "gray0" }, "uptime": { "fg": "gray8", "bg": "gray0" }, "external_ip": { "fg": "gray8", "bg": "gray0" }, "network_load": { "fg": "gray8", "bg": "gray0" }, diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json index bd3f3879..412e5d5b 100644 --- a/powerline/config_files/colorschemes/wm/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -44,9 +44,9 @@ "weather": { "fg": "gray8", "bg": "gray0" }, "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, "weather_temp_hot": { "fg": "weather_temp_hot", "bg": "gray0" }, - "weather_condition_☼": { "fg": "weather_condition_hot", "bg": "gray0" }, - "weather_condition_❅": { "fg": "weather_condition_cold", "bg": "gray0" }, - "weather_condition_☔": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_condition_hot": { "fg": "weather_condition_hot", "bg": "gray0" }, + "weather_condition_snowy": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_condition_rainy": { "fg": "weather_condition_cold", "bg": "gray0" }, "uptime": { "fg": "gray8", "bg": "gray0" }, "external_ip": { "fg": "gray8", "bg": "gray0" }, "network_load": { "fg": "gray8", "bg": "gray0" }, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 2d8127f0..792e6886 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -180,8 +180,83 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): return format.format(days=int(days), hours=hours, minutes=minutes) +# Weather condition code descriptions available at +# http://developer.yahoo.com/weather/#codes +weather_conditions_codes = ( + ('tornado', 'stormy'), # 0 + ('tropical_storm', 'stormy'), # 1 + ('hurricane', 'stormy'), # 2 + ('severe_thunderstorms', 'stormy'), # 3 + ('thunderstorms', 'stormy'), # 4 + ('mixed_rain_and_snow', 'rainy' ), # 5 + ('mixed_rain_and_sleet', 'rainy' ), # 6 + ('mixed_snow_and_sleet', 'snowy' ), # 7 + ('freezing_drizzle', 'rainy' ), # 8 + ('drizzle', 'rainy' ), # 9 + ('freezing_rain', 'rainy' ), # 10 + ('showers', 'rainy' ), # 11 + ('showers', 'rainy' ), # 12 + ('snow_flurries', 'snowy' ), # 13 + ('light_snow_showers', 'snowy' ), # 14 + ('blowing_snow', 'snowy' ), # 15 + ('snow', 'snowy' ), # 16 + ('hail', 'snowy' ), # 17 + ('sleet', 'snowy' ), # 18 + ('dust', 'foggy' ), # 19 + ('fog', 'foggy' ), # 20 + ('haze', 'foggy' ), # 21 + ('smoky', 'foggy' ), # 22 + ('blustery', 'foggy' ), # 23 + ('windy', ), # 24 + ('cold', 'day' ), # 25 + ('clouds', 'cloudy'), # 26 + ('mostly_cloudy_night', 'cloudy'), # 27 + ('mostly_cloudy_day', 'cloudy'), # 28 + ('partly_cloudy_night', 'cloudy'), # 29 + ('partly_cloudy_day', 'cloudy'), # 30 + ('clear_night', 'night' ), # 31 + ('sun', 'sunny' ), # 32 + ('fair_night', 'night' ), # 33 + ('fair_day', 'day' ), # 34 + ('mixed_rain_and_hail', 'rainy' ), # 35 + ('hot', 'sunny' ), # 36 + ('isolated_thunderstorms', 'stormy'), # 37 + ('scattered_thunderstorms', 'stormy'), # 38 + ('scattered_thunderstorms', 'stormy'), # 39 + ('scattered_showers', 'rainy' ), # 40 + ('heavy_snow', 'snowy' ), # 41 + ('scattered_snow_showers', 'snowy' ), # 42 + ('heavy_snow', 'snowy' ), # 43 + ('partly_cloudy', 'cloudy'), # 44 + ('thundershowers', 'rainy' ), # 45 + ('snow_showers', 'snowy' ), # 46 + ('isolated_thundershowers', 'rainy' ), # 47 +) +# ('day', (25, 34)), +# ('rainy', (5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47)), +# ('cloudy', (26, 27, 28, 29, 30, 44)), +# ('snowy', (7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46)), +# ('stormy', (0, 1, 2, 3, 4, 37, 38, 39)), +# ('foggy', (19, 20, 21, 22, 23)), +# ('sunny', (32, 36)), +# ('night', (31, 33))): +weather_conditions_icons = { + 'day': u'〇', + 'blustery': u'⚑', + 'rainy': u'☔', + 'cloudy': u'☁', + 'snowy': u'❅', + 'stormy': u'☈', + 'foggy': u'〰', + 'sunny': u'☼', + 'night': u'☾', + 'windy': u'☴', + 'not_available': u'⚠', +} + + @memoize(1800) -def weather(unit='c', location_query=None): +def weather(unit='c', location_query=None, icons=None): '''Return weather from Yahoo! Weather. Uses GeoIP lookup from http://freegeoip.net/ to automatically determine @@ -195,22 +270,11 @@ def weather(unit='c', location_query=None): temperature unit, can be one of ``F``, ``C`` or ``K`` :param str location_query: location query for your current location, e.g. ``oslo, norway`` + :param dict icons: + dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` ''' import json - # Weather condition code descriptions available at http://developer.yahoo.com/weather/#codes - weather_conditions_codes = { - u'〇': [25, 34], - u'⚑': [24], - u'☔': [5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47], - u'☁': [26, 27, 28, 29, 30, 44], - u'❅': [7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46], - u'☈': [0, 1, 2, 3, 4, 37, 38, 39], - u'〰': [19, 20, 21, 22, 23], - u'☼': [32, 36], - u'☾': [31, 33], - } - if not location_query: try: location = json.loads(urllib_read('http://freegeoip.net/json/' + external_ip())) @@ -230,14 +294,25 @@ def weather(unit='c', location_query=None): condition_code = int(condition['code']) except (KeyError, TypeError, ValueError): return None - icon = u'〇' - for icon, codes in weather_conditions_codes.items(): - if condition_code in codes: - break + try: + icon_names = weather_conditions_codes[condition_code] + except IndexError: + if condition_code == 3200: + icon_names = ('not_available',) + else: + icon_names = ('unknown') + for icon_name in icon_names: + if icons: + if icon_name in icons: + icon = icons[icon_name] + break + else: + icon = weather_conditions_icons[icon_names[-1]] + groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather'] return [ { 'contents': icon + ' ', - 'highlight_group': ['weather_condition_' + icon, 'weather_condition', 'weather'], + 'highlight_group': groups, }, { 'contents': u'{0}°{1}'.format(condition['temp'], unit.upper()), From 5202807269e35d9af6def036c8029b1e1a90cce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 15 Feb 2013 12:45:52 +0100 Subject: [PATCH 0369/1472] Make Spotify segment more error-proof --- powerline/segments/common.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 792e6886..a4795c42 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -599,14 +599,16 @@ class NowPlayingSegment(object): status = iface.Get(DBUS_IFACE_PLAYER, 'PlaybackStatus') except dbus.exceptions.DBusException: return + if not info: + return state = self._convert_state(status) return { 'state': state, 'state_symbol': self.STATE_SYMBOLS.get(state), - 'album': info['xesam:album'], - 'artist': info['xesam:artist'][0], - 'title': info['xesam:title'], - 'total': self._convert_seconds(info['mpris:length'] / 1e6), + 'album': info.get('xesam:album'), + 'artist': info.get('xesam:artist')[0], + 'title': info.get('xesam:title'), + 'total': self._convert_seconds(info.get('mpris:length') / 1e6), } def player_rhythmbox(self): From 0fddb24b9b4cc300901dbc04dfb8197a43ab1421 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Feb 2013 23:59:45 +0400 Subject: [PATCH 0370/1472] Rework memoize class - Removed *args: segments are not receiveing any non-keyword arguments so no need to bother with *args at all. - Replaced additional_cache_key with cache_key: all functions that formerly used additional_cache_key appear not to need initial arguments as cache keys. It also simplifies the code. - Made _cache be defined in __init__, it is much easier to implement cache purging this way. Ref #205 Fixes #208 --- powerline/lib/memoize.py | 22 +++++++++++----------- powerline/segments/common.py | 2 +- powerline/segments/vim.py | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 89e29d6f..3bae2dff 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -4,28 +4,28 @@ from functools import wraps import time +def default_cache_key(**kwargs): + return frozenset(kwargs.items()) + + class memoize(object): '''Memoization decorator with timeout.''' - _cache = {} - - def __init__(self, timeout, additional_key=None): + def __init__(self, timeout, cache_key=default_cache_key): self.timeout = timeout - self.additional_key = additional_key + self.cache_key = cache_key + self._cache = {} def __call__(self, func): @wraps(func) - def decorated_function(*args, **kwargs): - if self.additional_key: - key = (func.__name__, args, self.additional_key(*args, **kwargs)) - else: - key = (func.__name__, args) + def decorated_function(**kwargs): + key = self.cache_key(**kwargs) try: cached = self._cache.get(key, None) except TypeError: - return func(*args, **kwargs) + return func(**kwargs) if cached is None or time.time() - cached['time'] > self.timeout: cached = self._cache[key] = { - 'result': func(*args, **kwargs), + 'result': func(**kwargs), 'time': time.time(), } return cached['result'] diff --git a/powerline/segments/common.py b/powerline/segments/common.py index a4795c42..037506c1 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -34,7 +34,7 @@ def user(): def branch(): '''Return the current VCS branch.''' from powerline.lib.vcs import guess - repo = guess(os.path.abspath(os.getcwd())) + repo = guess(path=os.path.abspath(os.getcwd())) if repo: return repo.branch() return None diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 3490506c..3a993d96 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -47,7 +47,7 @@ mode_translations = { } -def bufnr(segment_info, *args, **kwargs): +def bufnr(segment_info, **kwargs): '''Used for cache key, returns current buffer number''' return segment_info['bufnr'] @@ -157,7 +157,7 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): @requires_segment_info -@memoize(2, additional_key=bufnr) +@memoize(2, cache_key=bufnr) def file_size(segment_info, suffix='B', binary_prefix=False): '''Return file size. @@ -254,10 +254,10 @@ def modified_buffers(text=u'+', join_str=','): @requires_segment_info -@memoize(2) +@memoize(2, cache_key=bufnr) def branch(segment_info): '''Return the current working branch.''' - repo = guess(os.path.abspath(segment_info['buffer'].name or os.getcwd())) + repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return repo.branch() return None @@ -265,12 +265,12 @@ def branch(segment_info): # TODO Drop cache on BufWrite event @requires_segment_info -@memoize(2, additional_key=bufnr) +@memoize(2, cache_key=bufnr) def file_vcs_status(segment_info): '''Return the VCS status for this buffer.''' name = segment_info['buffer'].name if name and not getbufvar(segment_info['bufnr'], '&buftype'): - repo = guess(os.path.abspath(name)) + repo = guess(path=os.path.abspath(name)) if repo: status = repo.status(os.path.relpath(name, repo.directory)) if not status: @@ -288,10 +288,10 @@ def file_vcs_status(segment_info): @requires_segment_info -@memoize(2) +@memoize(2, cache_key=bufnr) def repository_status(segment_info): '''Return the status for the current repo.''' - repo = guess(os.path.abspath(segment_info['buffer'].name or os.getcwd())) + repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return repo.status() return None From df7d0f2041faac1666492761c544a3e525c24947 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Feb 2013 08:30:07 +0400 Subject: [PATCH 0371/1472] Remove 'before' key from file_vcs_status segment Fixes #219 --- powerline/config_files/themes/vim/default.json | 1 + powerline/segments/vim.py | 1 - powerline/theme.py | 11 ++++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index ff206cf2..afbd40b9 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -33,6 +33,7 @@ }, { "name": "file_vcs_status", + "before": " ", "draw_divider": false }, { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 3a993d96..363c436c 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -282,7 +282,6 @@ def file_vcs_status(segment_info): 'contents': status, 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], }) - ret[0]['before'] = ' ' return ret return None diff --git a/powerline/theme.py b/powerline/theme.py index a441e55a..4ca0289e 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -63,8 +63,17 @@ class Theme(object): if contents is None: continue if isinstance(contents, list): + segment_base = copy(segment) + if contents: + for key in ('before', 'after'): + try: + contents[0][key] = segment_base.pop(key) + segment_base[key] = '' + except KeyError: + pass + for subsegment in contents: - segment_copy = copy(segment) + segment_copy = copy(segment_base) segment_copy.update(subsegment) parsed_segments.append(segment_copy) else: From c45747a3b859fc24232175fe4448b989206a38a5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Feb 2013 21:06:08 +0400 Subject: [PATCH 0372/1472] Ease E858/E860 errors debugging E858: Eval did not return a valid python object E860: Eval did not return a valid python 3 object --- .../source/installation/troubleshooting-common.rst | 14 ++++++++++++++ powerline/bindings/vim/plugin/powerline.vim | 8 ++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 5570bfc3..80c95a3c 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -30,3 +30,17 @@ My vim statusline is hidden/only appears in split windows! ---------------------------------------------------------- * Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. + +I get E858/E860 error in vim (Eval did not return a valid python object) +-------------------------------------------------------------------------- + +* You need to make ``pyeval()`` display python stack trace. There is currently + a patch for this, but it was not merged into main vim tree, thus you will have + to use different approach: reproduce the error with + + .. code-block:: sh + + vim --cmd "let g:powerline_debugging_pyeval=1" ... + + and then use the stack trace to search for existing issues or to create a new + one. diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 8cf4c32c..82dd082f 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -38,13 +38,13 @@ catch endtry exec s:powerline_pycmd 'powerline = Powerline("vim", segment_info={})' -if exists('*'. s:powerline_pyeval) +if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval) let s:pyeval = function(s:powerline_pyeval) else exec s:powerline_pycmd 'import json, vim' - function! s:pyeval(e) - exec s:powerline_pycmd 'vim.command("return " + json.dumps(eval(vim.eval("a:e"))))' - endfunction + exec "function! s:pyeval(e)\n". + \ s:powerline_pycmd." vim.command('return ' + json.dumps(eval(vim.eval('a:e'))))\n". + \"endfunction" endif function! Powerline(winnr, current) From 6fc7d6acc9518ebdef43b09519ae6e43d186d6a6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Feb 2013 22:40:08 +0400 Subject: [PATCH 0373/1472] Purge cache on BufWrite Instead of much simpler solution define an API that may be used for anybody willing to add cache purging on events with a few keystrokes --- powerline/bindings/vim/plugin/powerline.vim | 7 ++++ powerline/lib/memoize.py | 13 ++++--- powerline/segments/vim.py | 40 ++++++++++++++++++--- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 82dd082f..388721e9 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -67,6 +67,13 @@ function! s:UpdateWindows(use_last_current_window_id) endfor endfunction +function! PowerlineRegisterCachePurgerEvent(event) + exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent' + augroup Powerline + exec 'autocmd!' a:event '*' s:powerline_pycmd.' powerline_launchevent("'.a:event.'")' + augroup END +endfunction + let s:last_current_window_id = '' augroup Powerline autocmd! diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 3bae2dff..016e36e5 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -10,21 +10,26 @@ def default_cache_key(**kwargs): class memoize(object): '''Memoization decorator with timeout.''' - def __init__(self, timeout, cache_key=default_cache_key): + def __init__(self, timeout, cache_key=default_cache_key, cache_reg_func=None): self.timeout = timeout self.cache_key = cache_key - self._cache = {} + self.cache = {} + self.cache_reg_func = cache_reg_func def __call__(self, func): @wraps(func) def decorated_function(**kwargs): + if self.cache_reg_func: + self.cache_reg_func(self.cache) + self.cache_reg_func = None + key = self.cache_key(**kwargs) try: - cached = self._cache.get(key, None) + cached = self.cache.get(key, None) except TypeError: return func(**kwargs) if cached is None or time.time() - cached['time'] > self.timeout: - cached = self._cache[key] = { + cached = self.cache[key] = { 'result': func(**kwargs), 'time': time.time(), } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 363c436c..c7a6dcda 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -12,10 +12,12 @@ from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info from powerline.lib import memoize, humanize_bytes from powerline.lib.vcs import guess +from collections import defaultdict vim_funcs = { 'virtcol': vim_get_func('virtcol', rettype=int), 'fnamemodify': vim_get_func('fnamemodify'), + 'expand': vim_get_func('expand'), 'getfsize': vim_get_func('getfsize', rettype=int), 'bufnr': vim_get_func('bufnr', rettype=int), } @@ -47,6 +49,35 @@ mode_translations = { } +eventcaches = defaultdict(lambda : []) +bufeventcaches = defaultdict(lambda : []) +def purgeonevents_reg(events, eventcaches=bufeventcaches): + def cache_reg_func(cache): + for event in events: + if event not in eventcaches: + vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")') + eventcaches[event].append(cache) + + return cache_reg_func + +purgeall_on_shell = purgeonevents_reg(('ShellCmdPost', 'ShellFilterPost', 'FocusGained'), eventcaches=eventcaches) +purgebuf_on_shell_and_write = purgeonevents_reg(('BufWritePost', 'ShellCmdPost', 'ShellFilterPost', 'FocusGained')) + + +def launchevent(event): + global eventcaches + global bufeventcaches + for cache in eventcaches[event]: + cache.clear() + if bufeventcaches[event]: + buf = int(vim_funcs['expand']('')) + for cache in bufeventcaches[event]: + try: + cache.pop(buf) + except KeyError: + pass + + def bufnr(segment_info, **kwargs): '''Used for cache key, returns current buffer number''' return segment_info['bufnr'] @@ -157,7 +188,7 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): @requires_segment_info -@memoize(2, cache_key=bufnr) +@memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write) def file_size(segment_info, suffix='B', binary_prefix=False): '''Return file size. @@ -254,7 +285,7 @@ def modified_buffers(text=u'+', join_str=','): @requires_segment_info -@memoize(2, cache_key=bufnr) +@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell) def branch(segment_info): '''Return the current working branch.''' repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) @@ -263,9 +294,8 @@ def branch(segment_info): return None -# TODO Drop cache on BufWrite event @requires_segment_info -@memoize(2, cache_key=bufnr) +@memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write) def file_vcs_status(segment_info): '''Return the VCS status for this buffer.''' name = segment_info['buffer'].name @@ -287,7 +317,7 @@ def file_vcs_status(segment_info): @requires_segment_info -@memoize(2, cache_key=bufnr) +@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell) def repository_status(segment_info): '''Return the status for the current repo.''' repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) From 2a3911dbe6006941c4dfca857880a65b732c99c1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Feb 2013 01:47:41 +0400 Subject: [PATCH 0374/1472] Make spaces around the divider configurable Fixes #225 --- powerline/config_files/config.json | 11 ++++++----- powerline/renderer.py | 7 +++---- powerline/theme.py | 4 ++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 93ac8b4c..e2ae7c13 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -3,14 +3,15 @@ "term_24bit_colors": false, "dividers": { "left": { - "hard": "", - "soft": "" + "hard": " ", + "soft": " " }, "right": { - "hard": "", - "soft": "" + "hard": " ", + "soft": " " } - } + }, + "spaces": 1 }, "ext": { "ipython": { diff --git a/powerline/renderer.py b/powerline/renderer.py index 8c95721f..75ec745a 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -104,6 +104,7 @@ class Renderer(object): divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' divider_raw = theme.get_divider(segment['side'], divider_type) + divider_spaces = theme.get_spaces() divider_highlighted = '' contents_raw = segment['contents'] contents_highlighted = '' @@ -111,11 +112,9 @@ class Renderer(object): # Pad segments first if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'): if segment['side'] == 'left': - contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + ' ' - divider_raw = divider_raw + ' ' + contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + ((divider_spaces + segment['_space_right']) * ' ') else: - contents_raw = ' ' + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding - divider_raw = ' ' + divider_raw + contents_raw = ((divider_spaces + segment['_space_left']) * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding else: if segment['side'] == 'left': contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') diff --git a/powerline/theme.py b/powerline/theme.py index 4ca0289e..965ef81b 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -20,6 +20,7 @@ class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config, segment_info=None): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) + self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = { 'left': [], 'right': [], @@ -37,6 +38,9 @@ class Theme(object): '''Return segment divider.''' return self.dividers[side][type] + def get_spaces(self): + return self.spaces + def add_highlight(self, segment): segment['highlight'] = self.colorscheme.get_group_highlighting(segment['highlight_group']) if segment['divider_highlight_group']: From b8b7cfbbea917734d2ade232d78528e2753dda7a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Feb 2013 02:12:23 +0400 Subject: [PATCH 0375/1472] Fix icon_names for unknown weather code --- powerline/segments/common.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 037506c1..04196924 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -251,7 +251,8 @@ weather_conditions_icons = { 'sunny': u'☼', 'night': u'☾', 'windy': u'☴', - 'not_available': u'⚠', + 'not_available': u'�', + 'unknown': u'⚠', } @@ -294,13 +295,12 @@ def weather(unit='c', location_query=None, icons=None): condition_code = int(condition['code']) except (KeyError, TypeError, ValueError): return None + try: icon_names = weather_conditions_codes[condition_code] except IndexError: - if condition_code == 3200: - icon_names = ('not_available',) - else: - icon_names = ('unknown') + icon_names = (('not_available' if condition_code == 3200 else 'unknown'),) + for icon_name in icon_names: if icons: if icon_name in icons: @@ -308,6 +308,7 @@ def weather(unit='c', location_query=None, icons=None): break else: icon = weather_conditions_icons[icon_names[-1]] + groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather'] return [ { From baaf6cde1d93ff0ac93238488237f30d91797532 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Feb 2013 02:04:39 +0400 Subject: [PATCH 0376/1472] Switch from `status` + `ls-files` to `status --ignored` --- docs/source/overview.rst | 1 + powerline/lib/vcs/git.py | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 3728c973..f066741d 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -70,6 +70,7 @@ recommended for optimal performance and extra features: * ``pygit2`` * ``mercurial`` * ``psutil`` +* ``git`` version 1.7.2 and later. Not needed if you have ``pygit2``. Installation ============ diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index d44a69c9..d2dc8fc3 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -107,13 +107,9 @@ except ImportError: def status(self, path=None): if path: try: - return next(self._gitcmd('status', '--porcelain', '--', path))[:2] + return next(self._gitcmd('status', '--porcelain', '--ignored', '--', path))[:2] except StopIteration: - try: - next(self._gitcmd('ls-files', '--ignored', '--exclude-standard', '--others', '--', path)) - return '!!' - except StopIteration: - return None + return None else: wt_column = ' ' index_column = ' ' From ea56f4c26b136418b19aa921d2940812e0d83a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 18 Feb 2013 19:38:42 +0100 Subject: [PATCH 0377/1472] Split optional dependencies to python packages and other apps Closes #227. --- docs/source/overview.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index f066741d..18d520cd 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -64,12 +64,19 @@ if your terminal emulator supports it. Optional dependencies --------------------- -The following Python packages are not required by all segments, but -recommended for optimal performance and extra features: +The following software is not required by all segments, but recommended for +optimal performance and extra features: + +Python packages +^^^^^^^^^^^^^^^ * ``pygit2`` * ``mercurial`` * ``psutil`` + +Other applications +^^^^^^^^^^^^^^^^^^ + * ``git`` version 1.7.2 and later. Not needed if you have ``pygit2``. Installation From e14f5e4208e3290bcc55ac6ca4d35d1ea34dd7e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Feb 2013 00:26:59 +0400 Subject: [PATCH 0378/1472] Fix problem with wrong window ID/winnr after some window operations Removed autocommands as well. Powerline() function is now responsible both for displaying powerline and for updating statuslines. No cmdwin events not triggered workaround nonsense anymore. Fixes #237 Fixes #236 Fixes #222 Fixes #232 --- powerline/bindings/vim/plugin/powerline.vim | 37 ++++++++++----------- powerline/renderers/vim.py | 6 ++-- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 388721e9..c6d6423b 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -47,26 +47,28 @@ else \"endfunction" endif -function! Powerline(winnr, current) - return s:pyeval('powerline.renderer.render('. a:winnr .', '. a:current .')') +function! s:GetWinID(winnr) + let r = getwinvar(a:winnr, 'window_id') + if empty(r) + let r = s:pyeval('str(uuid.uuid4())') + call setwinvar(a:winnr, 'window_id', r) + call setwinvar(a:winnr, '&statusline', '%!Powerline("'.r.'")') + endif + return r endfunction -function! s:UpdateWindows(use_last_current_window_id) - if ! exists('w:window_id') - let w:window_id = s:pyeval('str(uuid.uuid4())') - endif - for winnr in range(1, winnr('$')) - let current = 0 - if w:window_id == getwinvar(winnr, 'window_id') || (a:use_last_current_window_id && getwinvar(winnr, 'window_id') == s:last_current_window_id) - let current = 1 - if bufname(winbufnr(winnr)) isnot# '[Command Line]' - let s:last_current_window_id = getwinvar(winnr, 'window_id') - endif - endif - call setwinvar(winnr, '&statusline', '%!Powerline('. winnr .', '. current .')') - endfor +function! Powerline(window_id) + let winidx = index(map(range(1, winnr('$')), 's:GetWinID(v:val)'), a:window_id) + let current = w:window_id is# a:window_id + return s:pyeval('powerline.renderer.render("'. a:window_id .'", '. winidx .', '. current .')') endfunction +function! PowerlineNew() + return Powerline(s:GetWinID(winnr())) +endfunction + +set statusline=%!PowerlineNew() + function! PowerlineRegisterCachePurgerEvent(event) exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent' augroup Powerline @@ -74,10 +76,7 @@ function! PowerlineRegisterCachePurgerEvent(event) augroup END endfunction -let s:last_current_window_id = '' augroup Powerline autocmd! - autocmd BufEnter,BufWinEnter,WinEnter,CmdwinEnter * call s:UpdateWindows(0) | redrawstatus - autocmd CmdwinLeave * call s:UpdateWindows(1) autocmd ColorScheme * exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' augroup END diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index fabd535b..fdb0bcf8 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -20,21 +20,19 @@ class VimRenderer(Renderer): super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} - def render(self, winnr, current): + def render(self, window_id, winidx, current): '''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. ''' - window_id = vim_getwinvar(winnr, 'window_id') if current: mode = vim_mode() else: mode = 'nc' segment_info = { - 'window': vim.windows[winnr - 1], - 'winnr': winnr, + 'window': vim.windows[winidx], 'mode': mode, 'window_id': window_id, } From ee51b8b76c1091c7abac3cdab2421d729b7bfdd8 Mon Sep 17 00:00:00 2001 From: Martin Preisler Date: Tue, 19 Feb 2013 21:11:28 +0100 Subject: [PATCH 0379/1472] Handle non-existant CWDs gracefully --- powerline/segments/common.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 04196924..03aad401 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -52,9 +52,17 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): ''' import re try: - cwd = os.getcwdu() - except AttributeError: - cwd = os.getcwd() + try: + cwd = os.getcwdu() + except AttributeError: + cwd = os.getcwd() + except OSError as e: + if e.errno == 2: + # user most probably deleted the directory + # this happens when removing files from Mercurial repos for example + cwd = "[not found]" + else: + raise home = os.environ.get('HOME') if home: cwd = re.sub('^' + re.escape(home), '~', cwd, 1) From 282cb43eefc5a13f9a9f2960e9a059a369784d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 20 Feb 2013 13:47:54 +0100 Subject: [PATCH 0380/1472] Fix various minor formatting issues Closes #242. --- powerline/bindings/ipython/pre_0_11.py | 2 +- powerline/colorscheme.py | 2 +- powerline/lib/vcs/__init__.py | 7 +- powerline/renderer.py | 2 +- powerline/renderers/shell.py | 2 +- powerline/segments/common.py | 96 +++++++++++++------------- powerline/segments/vim.py | 9 +-- powerline/theme.py | 3 +- setup.py | 3 +- 9 files changed, 65 insertions(+), 61 deletions(-) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index f48e71a9..18fa8384 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -15,7 +15,7 @@ class PowerlinePrompt(BasePrompt): def auto_rewrite(self): # TODO color this - return '%s>%s' % ('-'*self.prompt_text_len, ' '*self.nrspaces) + return '%s>%s' % ('-' * self.prompt_text_len, ' ' * self.nrspaces) def setup(prompt='1'): diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 4fbcfe8e..0f499a8b 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -5,8 +5,8 @@ ATTR_BOLD = 1 ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 -class Colorscheme(object): +class Colorscheme(object): def __init__(self, colorscheme): '''Initialize a colorscheme.''' self.colors = {} diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index ce3a9d96..7bf396b9 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -3,6 +3,10 @@ import os from powerline.lib.memoize import memoize +vcs_props = (('git', '.git', os.path.exists), + ('mercurial', '.hg', os.path.isdir)) + + def generate_directories(path): yield path while True: @@ -13,9 +17,6 @@ def generate_directories(path): yield path -vcs_props = (('git', '.git', os.path.exists), - ('mercurial', '.hg', os.path.isdir)) - @memoize(100) def guess(path): for directory in generate_directories(path): diff --git a/powerline/renderer.py b/powerline/renderer.py index 75ec745a..17b7a738 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -42,7 +42,7 @@ class Renderer(object): theme.segment_info.update(segment_info) # Handle excluded/included segments for the current mode - segments = [segment for segment in segments\ + segments = [segment for segment in segments if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] segments = [segment for segment in self._render_segments(mode, theme, segments)] diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index a322ce4e..5c969756 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -43,7 +43,7 @@ class ShellRenderer(Renderer): if attr & ATTR_BOLD: ansi += [1] elif attr & ATTR_ITALIC: - # Note: is likely not to work or even be inverse in place of + # Note: is likely not to work or even be inverse in place of # italic. Omit using this in colorschemes. ansi += [3] elif attr & ATTR_UNDERLINE: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 03aad401..6f458d01 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -191,54 +191,54 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): # Weather condition code descriptions available at # http://developer.yahoo.com/weather/#codes weather_conditions_codes = ( - ('tornado', 'stormy'), # 0 - ('tropical_storm', 'stormy'), # 1 - ('hurricane', 'stormy'), # 2 - ('severe_thunderstorms', 'stormy'), # 3 - ('thunderstorms', 'stormy'), # 4 - ('mixed_rain_and_snow', 'rainy' ), # 5 - ('mixed_rain_and_sleet', 'rainy' ), # 6 - ('mixed_snow_and_sleet', 'snowy' ), # 7 - ('freezing_drizzle', 'rainy' ), # 8 - ('drizzle', 'rainy' ), # 9 - ('freezing_rain', 'rainy' ), # 10 - ('showers', 'rainy' ), # 11 - ('showers', 'rainy' ), # 12 - ('snow_flurries', 'snowy' ), # 13 - ('light_snow_showers', 'snowy' ), # 14 - ('blowing_snow', 'snowy' ), # 15 - ('snow', 'snowy' ), # 16 - ('hail', 'snowy' ), # 17 - ('sleet', 'snowy' ), # 18 - ('dust', 'foggy' ), # 19 - ('fog', 'foggy' ), # 20 - ('haze', 'foggy' ), # 21 - ('smoky', 'foggy' ), # 22 - ('blustery', 'foggy' ), # 23 - ('windy', ), # 24 - ('cold', 'day' ), # 25 - ('clouds', 'cloudy'), # 26 - ('mostly_cloudy_night', 'cloudy'), # 27 - ('mostly_cloudy_day', 'cloudy'), # 28 - ('partly_cloudy_night', 'cloudy'), # 29 - ('partly_cloudy_day', 'cloudy'), # 30 - ('clear_night', 'night' ), # 31 - ('sun', 'sunny' ), # 32 - ('fair_night', 'night' ), # 33 - ('fair_day', 'day' ), # 34 - ('mixed_rain_and_hail', 'rainy' ), # 35 - ('hot', 'sunny' ), # 36 - ('isolated_thunderstorms', 'stormy'), # 37 - ('scattered_thunderstorms', 'stormy'), # 38 - ('scattered_thunderstorms', 'stormy'), # 39 - ('scattered_showers', 'rainy' ), # 40 - ('heavy_snow', 'snowy' ), # 41 - ('scattered_snow_showers', 'snowy' ), # 42 - ('heavy_snow', 'snowy' ), # 43 - ('partly_cloudy', 'cloudy'), # 44 - ('thundershowers', 'rainy' ), # 45 - ('snow_showers', 'snowy' ), # 46 - ('isolated_thundershowers', 'rainy' ), # 47 + ('tornado', 'stormy'), # 0 + ('tropical_storm', 'stormy'), # 1 + ('hurricane', 'stormy'), # 2 + ('severe_thunderstorms', 'stormy'), # 3 + ('thunderstorms', 'stormy'), # 4 + ('mixed_rain_and_snow', 'rainy' ), # 5 + ('mixed_rain_and_sleet', 'rainy' ), # 6 + ('mixed_snow_and_sleet', 'snowy' ), # 7 + ('freezing_drizzle', 'rainy' ), # 8 + ('drizzle', 'rainy' ), # 9 + ('freezing_rain', 'rainy' ), # 10 + ('showers', 'rainy' ), # 11 + ('showers', 'rainy' ), # 12 + ('snow_flurries', 'snowy' ), # 13 + ('light_snow_showers', 'snowy' ), # 14 + ('blowing_snow', 'snowy' ), # 15 + ('snow', 'snowy' ), # 16 + ('hail', 'snowy' ), # 17 + ('sleet', 'snowy' ), # 18 + ('dust', 'foggy' ), # 19 + ('fog', 'foggy' ), # 20 + ('haze', 'foggy' ), # 21 + ('smoky', 'foggy' ), # 22 + ('blustery', 'foggy' ), # 23 + ('windy', ), # 24 + ('cold', 'day' ), # 25 + ('clouds', 'cloudy'), # 26 + ('mostly_cloudy_night', 'cloudy'), # 27 + ('mostly_cloudy_day', 'cloudy'), # 28 + ('partly_cloudy_night', 'cloudy'), # 29 + ('partly_cloudy_day', 'cloudy'), # 30 + ('clear_night', 'night' ), # 31 + ('sun', 'sunny' ), # 32 + ('fair_night', 'night' ), # 33 + ('fair_day', 'day' ), # 34 + ('mixed_rain_and_hail', 'rainy' ), # 35 + ('hot', 'sunny' ), # 36 + ('isolated_thunderstorms', 'stormy'), # 37 + ('scattered_thunderstorms', 'stormy'), # 38 + ('scattered_thunderstorms', 'stormy'), # 39 + ('scattered_showers', 'rainy' ), # 40 + ('heavy_snow', 'snowy' ), # 41 + ('scattered_snow_showers', 'snowy' ), # 42 + ('heavy_snow', 'snowy' ), # 43 + ('partly_cloudy', 'cloudy'), # 44 + ('thundershowers', 'rainy' ), # 45 + ('snow_showers', 'snowy' ), # 46 + ('isolated_thundershowers', 'rainy' ), # 47 ) # ('day', (25, 34)), # ('rainy', (5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47)), diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c7a6dcda..d9c7f68c 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -6,7 +6,7 @@ import os try: import vim except ImportError: - vim = {} + vim = {} # NOQA from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info @@ -49,15 +49,16 @@ mode_translations = { } -eventcaches = defaultdict(lambda : []) -bufeventcaches = defaultdict(lambda : []) +eventcaches = defaultdict(lambda: []) +bufeventcaches = defaultdict(lambda: []) + + def purgeonevents_reg(events, eventcaches=bufeventcaches): def cache_reg_func(cache): for event in events: if event not in eventcaches: vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")') eventcaches[event].append(cache) - return cache_reg_func purgeall_on_shell = purgeonevents_reg(('ShellCmdPost', 'ShellFilterPost', 'FocusGained'), eventcaches=eventcaches) diff --git a/powerline/theme.py b/powerline/theme.py index 965ef81b..187c87d1 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -5,8 +5,9 @@ from copy import copy from .segment import Segment + try: - unicode() + unicode() # NOQA except NameError: unicode = str diff --git a/setup.py b/setup.py index 29101ea6..5600c5c2 100755 --- a/setup.py +++ b/setup.py @@ -11,7 +11,8 @@ try: except IOError: README = '' -setup(name='Powerline', +setup( + name='Powerline', version='beta', description='The ultimate statusline/prompt utility.', long_description=README, From b347d53b6d9f6cede4fab7ba6ae5f2af8d077c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 20 Feb 2013 14:07:26 +0100 Subject: [PATCH 0381/1472] Remove obsolete Vundle warning from docs --- docs/source/installation/linux.rst | 3 --- docs/source/installation/osx.rst | 3 --- 2 files changed, 6 deletions(-) diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index 0e3128e6..ad099318 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -30,9 +30,6 @@ Plugin installation project is currently unavailable on the PyPI due to a naming conflict with an unrelated project. -.. warning:: Installing Powerline with a vim plugin manager like Vundle is - not recommended and not officially supported. - Font installation ================= diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index 55058744..ad45548a 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -26,9 +26,6 @@ Python package project is currently unavailable on the PyPI due to a naming conflict with an unrelated project. -.. warning:: Installing Powerline with a vim plugin manager like Vundle is - not recommended and not officially supported. - Vim installation ---------------- From 861f7dc1797fb38b3fb503cf621f30e44791f586 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Feb 2013 20:57:44 +0400 Subject: [PATCH 0382/1472] Fix &stl reset to global value when new file is loaded Fixes #241 Ref #240 --- powerline/bindings/vim/plugin/powerline.vim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index c6d6423b..d2344b8c 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -52,6 +52,9 @@ function! s:GetWinID(winnr) if empty(r) let r = s:pyeval('str(uuid.uuid4())') call setwinvar(a:winnr, 'window_id', r) + endif + " Without this condition it triggers unneeded statusline redraw + if getwinvar(a:winnr, '&statusline') isnot# '%!Powerline("'.r.'")' call setwinvar(a:winnr, '&statusline', '%!Powerline("'.r.'")') endif return r @@ -64,9 +67,11 @@ function! Powerline(window_id) endfunction function! PowerlineNew() - return Powerline(s:GetWinID(winnr())) + call map(range(1, winnr('$')), 's:GetWinID(v:val)') endfunction +" Is immediately changed when Powerline() function is run. Good for global +" value. set statusline=%!PowerlineNew() function! PowerlineRegisterCachePurgerEvent(event) From 3c62e060be8b319dd00457cd3800d3cec9267f4b Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Feb 2013 18:21:12 +0400 Subject: [PATCH 0383/1472] Don't use UUID for window IDs This fix uses an incremented number instead. Fixes #180 --- powerline/bindings/vim/plugin/powerline.vim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index d2344b8c..a538c9fc 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -18,7 +18,6 @@ endif let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', 'py'), '\v^(py)%[thon](3?)$', '\1\2', '') let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') -exec s:powerline_pycmd 'import uuid' try exec s:powerline_pycmd 'from powerline.core import Powerline' catch @@ -47,15 +46,17 @@ else \"endfunction" endif +let s:last_window_id = 0 function! s:GetWinID(winnr) let r = getwinvar(a:winnr, 'window_id') if empty(r) - let r = s:pyeval('str(uuid.uuid4())') + let r = s:last_window_id + let s:last_window_id += 1 call setwinvar(a:winnr, 'window_id', r) endif " Without this condition it triggers unneeded statusline redraw - if getwinvar(a:winnr, '&statusline') isnot# '%!Powerline("'.r.'")' - call setwinvar(a:winnr, '&statusline', '%!Powerline("'.r.'")') + if getwinvar(a:winnr, '&statusline') isnot# '%!Powerline('.r.')' + call setwinvar(a:winnr, '&statusline', '%!Powerline('.r.')') endif return r endfunction @@ -63,14 +64,14 @@ endfunction function! Powerline(window_id) let winidx = index(map(range(1, winnr('$')), 's:GetWinID(v:val)'), a:window_id) let current = w:window_id is# a:window_id - return s:pyeval('powerline.renderer.render("'. a:window_id .'", '. winidx .', '. current .')') + return s:pyeval('powerline.renderer.render('. a:window_id .', '. winidx .', '. current .')') endfunction function! PowerlineNew() call map(range(1, winnr('$')), 's:GetWinID(v:val)') endfunction -" Is immediately changed when Powerline() function is run. Good for global +" Is immediately changed when PowerlineNew() function is run. Good for global " value. set statusline=%!PowerlineNew() From 2fa64c42d168c3116f246496ccb9087f06f22ab7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Feb 2013 15:29:31 +0400 Subject: [PATCH 0384/1472] Move translation of modes into renderers/vim.py Fixes #147 --- powerline/renderers/vim.py | 5 +++++ powerline/segments/vim.py | 6 ------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index fdb0bcf8..145e180e 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -11,6 +11,10 @@ import vim vim_mode = vim_get_func('mode') vim_getwinvar = vim_get_func('getwinvar') vim_setwinvar = vim_get_func('setwinvar') +mode_translations = { + chr(ord('V') - 0x40): '^V', + chr(ord('S') - 0x40): '^S', +} class VimRenderer(Renderer): @@ -29,6 +33,7 @@ class VimRenderer(Renderer): ''' if current: mode = vim_mode() + mode = mode_translations.get(mode, mode) else: mode = 'nc' segment_info = { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index d9c7f68c..f7dfa1d1 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -43,11 +43,6 @@ vim_modes = { '!': u'SHELL', } -mode_translations = { - chr(ord('V') - 0x40): '^V', - chr(ord('S') - 0x40): '^S', -} - eventcaches = defaultdict(lambda: []) bufeventcaches = defaultdict(lambda: []) @@ -111,7 +106,6 @@ def mode(segment_info, override=None): mode = segment_info['mode'] if mode == 'nc': return None - mode = mode_translations.get(mode, mode) if not override: return vim_modes[mode] try: From 186ad2d423677de0d95cc9a1346c3b671d94e053 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Feb 2013 08:31:00 +0400 Subject: [PATCH 0385/1472] Remove useless Segment class It was used solely for Segment.get (the only place where it was used just saved its .get() method, throwing away instance itself) and looks more like abusal of the purpose of the classes --- powerline/segment.py | 55 ++++++++++++++++++++++++++------------------ powerline/theme.py | 4 ++-- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index a843a46b..999195e1 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -4,35 +4,44 @@ from importlib import import_module import sys -class Segment(object): - def __init__(self, ext, path, default_module=None): - self.default_module = default_module or 'powerline.segments.{0}'.format(ext) - self.path = path +def get_function(data, segment): + oldpath = sys.path + sys.path = data['path'] + sys.path + segment_module = str(segment.get('module', data['default_module'])) + try: + return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) + finally: + sys.path = oldpath - def get_function(self, segment): - oldpath = sys.path - sys.path = self.path + sys.path - segment_module = str(segment.get('module', self.default_module)) - try: - return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) - finally: - sys.path = oldpath - @staticmethod - def get_string(segment): - return segment.get('contents'), None, None +def get_string(data, segment): + return segment.get('contents'), None, None - @staticmethod - def get_filler(segment): - return None, None, None - def get(self, segment, side): +def get_filler(data, segment): + return None, None, None + + +segment_getters = { + "function": get_function, + "string": get_string, + "filler": get_filler, + } + + +def gen_segment_getter(ext, path, default_module=None): + data = { + "default_module": default_module or 'powerline.segments.'+ext, + "path": path + } + + def get(segment, side): segment_type = segment.get('type', 'function') try: - get_segment_info = getattr(self, 'get_{0}'.format(segment_type)) - except AttributeError: + get_segment_info = segment_getters[segment_type] + except KeyError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) - contents, contents_func, key = get_segment_info(segment) + contents, contents_func, key = get_segment_info(data, segment) highlight_group = segment.get('highlight_group', segment.get('name')) divider_highlight_group = segment.get('divider_highlight_group') return { @@ -58,3 +67,5 @@ class Segment(object): '_space_left': 0, '_space_right': 0, } + + return get diff --git a/powerline/theme.py b/powerline/theme.py index 187c87d1..d6d98fe6 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -3,7 +3,7 @@ from collections import defaultdict from copy import copy -from .segment import Segment +from .segment import gen_segment_getter try: @@ -31,7 +31,7 @@ class Theme(object): 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) } self.segment_info = segment_info - get_segment = Segment(ext, common_config['paths'], theme_config.get('default_module')).get + get_segment = gen_segment_getter(ext, common_config['paths'], theme_config.get('default_module')) for side in ['left', 'right']: self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) From c86b047ed43513533340faa36ee400b86c7ed7ac Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Feb 2013 09:23:40 +0400 Subject: [PATCH 0386/1472] Add ability to specify some segment keys once in top theme --- docs/source/configuration.rst | 25 ++++++++++++ .../config_files/themes/vim/default.json | 21 +++++++--- powerline/config_files/themes/vim/help.json | 4 +- powerline/renderer.py | 3 +- powerline/segment.py | 38 ++++++++++++++----- powerline/theme.py | 7 +++- 6 files changed, 77 insertions(+), 21 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index e1d3f59a..25cf05fb 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -145,9 +145,13 @@ Common configuration is a subdictionary that is a value of ``ext`` key in Defines the colorscheme used for this extension. ``theme`` + .. _config-ext-theme: + Defines the theme used for this extension. ``local_themes`` + .. _config-ext-local_themes: + Defines themes used when certain conditions are met, e.g. for buffer-specific statuslines in vim. Requires a custom matcher and theme. @@ -219,6 +223,21 @@ Themes ``default_module`` Python module where segments will be looked by default. +.. _config-themes-segment_data: + +``segment_data`` + A dict where keys are segment names or strings ``{module}.{name}``. Used to + specify default values for various keys: + :ref:`after `, + :ref:`before `, + :ref:`contents ` (only for string segments + if :ref:`name ` is defined), + :ref:`args ` (only for function segments). When + using :ref:`local themes ` values of these keys are + first searched in the segment description, then in ``segment_data`` key of + a local theme, then in ``segment_data`` key of a :ref:`default theme + `. + ``segments`` A dict with a ``left`` and a ``right`` list, consisting of segment dicts. Each segment has the following options: @@ -257,9 +276,13 @@ Themes available in the colorscheme is used. ``before`` + .. _config-themes-seg-before: + A string which will be prepended to the segment contents. ``after`` + .. _config-themes-seg-after: + A string which will be appended to the segment contents. ``contents`` @@ -268,6 +291,8 @@ Themes Segment contents, only required for ``string`` segments. ``args`` + .. _config-themes-seg-args: + A dict of arguments to be passed to a ``function`` segment. ``align`` diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index afbd40b9..543d717e 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -1,4 +1,19 @@ { + "segment_data": { + "branch": { + "before": " " + }, + "modified_indicator": { + "args": { "text": "+" } + }, + "line_percent": { + "args": { "gradient": true }, + "after": "%" + }, + "line_current_symbol": { + "contents": " " + } + }, "segments": { "left": [ { @@ -14,7 +29,6 @@ "name": "branch", "exclude_modes": ["nc"], "priority": 60, - "before": " ", "divider_highlight_group": "branch:divider" }, { @@ -38,7 +52,6 @@ }, { "name": "modified_indicator", - "args": { "text": "+" }, "before": " " }, { @@ -70,15 +83,13 @@ }, { "name": "line_percent", - "args": { "gradient": true }, "priority": 30, - "after": "%", "width": 4, "align": "r" }, { "type": "string", - "contents": " ", + "name": "line_current_symbol", "highlight_group": ["line_current_symbol", "line_current"] }, { diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index 83961cba..9fb5320c 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -15,15 +15,13 @@ "right": [ { "name": "line_percent", - "args": { "gradient": true }, "priority": 30, - "after": "%", "width": 4, "align": "r" }, { "type": "string", - "contents": " ", + "name": "line_current_symbol", "highlight_group": ["line_current_symbol", "line_current"] }, { diff --git a/powerline/renderer.py b/powerline/renderer.py index 17b7a738..684b702e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -7,6 +7,7 @@ from powerline.theme import Theme class Renderer(object): def __init__(self, theme_config, local_themes, theme_kwargs, **options): self.__dict__.update(options) + self.theme_config = theme_config self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs @@ -21,7 +22,7 @@ class Renderer(object): if matcher(matcher_info): match = self.local_themes[matcher] if 'config' in match: - match['theme'] = Theme(theme_config=match.pop('config'), **self.theme_kwargs) + match['theme'] = Theme(theme_config=match.pop('config'), top_theme_config=self.theme_config, **self.theme_kwargs) return match['theme'] else: return self.theme diff --git a/powerline/segment.py b/powerline/segment.py index 999195e1..d8fd8170 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -4,18 +4,34 @@ from importlib import import_module import sys +def get_segment_key(segment, theme_configs, key, module=None, default=None): + try: + return segment[key] + except KeyError: + if 'name' in segment: + name = segment['name'] + for theme_config in theme_configs: + if 'segment_data' in theme_config: + for segment_key in ((module+'.'+name, name) if module else (name,)): + try: + return theme_config['segment_data'][segment_key][key] + except KeyError: + pass + return default + + def get_function(data, segment): oldpath = sys.path sys.path = data['path'] + sys.path segment_module = str(segment.get('module', data['default_module'])) try: - return None, getattr(import_module(segment_module), segment['name']), '{0}.{1}'.format(segment_module, segment['name']) + return None, getattr(import_module(segment_module), segment['name']), segment_module finally: sys.path = oldpath def get_string(data, segment): - return segment.get('contents'), None, None + return data['get_key'](segment, None, 'contents'), None, None def get_filler(data, segment): @@ -29,11 +45,14 @@ segment_getters = { } -def gen_segment_getter(ext, path, default_module=None): +def gen_segment_getter(ext, path, theme_configs, default_module=None): data = { - "default_module": default_module or 'powerline.segments.'+ext, - "path": path + 'default_module': default_module or 'powerline.segments.'+ext, + 'path': path, } + def get_key(segment, module, key, default=None): + return get_segment_key(segment, theme_configs, key, module, default) + data['get_key'] = get_key def get(segment, side): segment_type = segment.get('type', 'function') @@ -41,19 +60,18 @@ def gen_segment_getter(ext, path, default_module=None): get_segment_info = segment_getters[segment_type] except KeyError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) - contents, contents_func, key = get_segment_info(data, segment) + contents, contents_func, module = get_segment_info(data, segment) highlight_group = segment.get('highlight_group', segment.get('name')) divider_highlight_group = segment.get('divider_highlight_group') return { - 'key': key, 'type': segment_type, 'highlight_group': highlight_group, 'divider_highlight_group': divider_highlight_group, - 'before': segment.get('before', ''), - 'after': segment.get('after', ''), + 'before': get_key(segment, module, 'before', ''), + 'after': get_key(segment, module, 'after', ''), 'contents_func': contents_func, 'contents': contents, - 'args': segment.get('args', {}), + 'args': get_key(segment, module, 'args', {}) if segment_type == 'function' else {}, 'priority': segment.get('priority', -1), 'draw_divider': segment.get('draw_divider', True), 'side': side, diff --git a/powerline/theme.py b/powerline/theme.py index d6d98fe6..db503866 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -18,7 +18,7 @@ def requires_segment_info(func): class Theme(object): - def __init__(self, ext, colorscheme, theme_config, common_config, segment_info=None): + def __init__(self, ext, colorscheme, theme_config, common_config, top_theme_config=None, segment_info=None): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) @@ -31,7 +31,10 @@ class Theme(object): 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) } self.segment_info = segment_info - get_segment = gen_segment_getter(ext, common_config['paths'], theme_config.get('default_module')) + theme_configs = [theme_config] + if top_theme_config: + theme_configs.append(top_theme_config) + get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module')) for side in ['left', 'right']: self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) From f211bb6c74a994f3d0fc1ebc1d2c2450ea1247f2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Feb 2013 20:11:45 +0400 Subject: [PATCH 0387/1472] Add ability to override configuration options Related changes: - Moved all non-ASCII symbols into `segment_data` - Added --config_path, replaced nargs='*' with better action='append' - Added g:powerline_config_path vim option - Added ipython overrides (via additional arguments to setup() or c.Powerline) TODO: support for non-string scalars in vim overrides. Fixes #231 --- docs/source/configuration.rst | 78 ++++++++++++++++- powerline/__init__.py | 81 +++++++++++++++++ powerline/bindings/ipython/post_0_11.py | 19 +++- powerline/bindings/ipython/pre_0_11.py | 14 ++- powerline/bindings/qtile/widget.py | 2 +- powerline/bindings/vim/plugin/powerline.vim | 8 +- powerline/bindings/zsh/__init__.py | 22 ++++- .../config_files/themes/shell/default.json | 26 ++++-- .../themes/shell/default_leftonly.json | 23 +++-- .../config_files/themes/tmux/default.json | 26 ++++-- powerline/core.py | 86 ------------------- powerline/ipython.py | 27 ++++++ powerline/lib/__init__.py | 9 ++ powerline/matcher.py | 13 ++- powerline/shell.py | 42 +++++++++ powerline/vim.py | 72 ++++++++++++++++ scripts/powerline | 24 +++++- 17 files changed, 437 insertions(+), 135 deletions(-) delete mode 100644 powerline/core.py create mode 100644 powerline/ipython.py create mode 100644 powerline/shell.py create mode 100644 powerline/vim.py diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 25cf05fb..bbefffec 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -132,8 +132,8 @@ Common configuration is a subdictionary that is a value of ``common`` key in ``paths`` Defines additional paths which will be searched for modules when using - :ref:`module segment option `. Paths defined - here have priority when searching for modules. + :ref:`module segment option `. Paths defined here + have priority when searching for modules. Extension-specific configuration -------------------------------- @@ -236,7 +236,8 @@ Themes using :ref:`local themes ` values of these keys are first searched in the segment description, then in ``segment_data`` key of a local theme, then in ``segment_data`` key of a :ref:`default theme - `. + `. For the :ref:`default theme ` itself + step 2 is obviously avoided. ``segments`` A dict with a ``left`` and a ``right`` list, consisting of segment @@ -354,3 +355,74 @@ A segment function must return one of the following values: * A list of dicts consisting of a ``contents`` string, and a ``highlight_group`` list. This is useful for providing a particular highlighting group depending on the segment contents. + +Local configuration +=================== + +Depending on the application used it is possible to override configuration. Here +is the list: + +Vim overrides +------------- + +Vim configuration can be overridden using the following options: + +``g:powerline_config_overrides`` + Dictionary, recursively merged with contents of + :file:`powerline/config.json`. + +``g:powerline_theme_overrides__{theme_name}`` + Dictionary, recursively merged with contents of + :file:`powerline/themes/vim/{theme_name}.json`. Note that this way you can’t + redefine some value (e.g. segment) in list, only the whole list itself: only + dictionaries are merged recursively. + +``g:powerline_config_path`` + Path (must be expanded, ``~`` shortcut is not supported). Points to the + directory which will be searched for configuration. When this option is + present, none of the other locations are searched. + +Powerline script overrides +-------------------------- + +Powerline script has a number of options controlling powerline behavior. Here +``VALUE`` always means “some JSON object”. + +``-c KEY.NESTED_KEY=VALUE`` or ``--config=KEY.NESTED_KEY=VALUE`` + Overrides options from :file:`powerline/config.json`. + ``KEY.KEY2.KEY3=VALUE`` is a shortcut for ``KEY={"KEY2": {"KEY3": VALUE}}``. + Multiple options (i.e. ``-c K1=V1 -c K2=V2``) are allowed, result (in the + example: ``{"K1": V1, "K2": V2}``) is recursively merged with the contents + of the file. + +``-t THEME_NAME.KEY.NESTED_KEY=VALUE`` or ``--theme_option=THEME_NAME.KEY.NESTED_KEY=VALUE`` + Overrides options from :file:`powerline/themes/{ext}/{THEME_NAME}.json`. + ``KEY.NESTED_KEY=VALUE`` is processed like described above, ``{ext}`` is the + first argument to powerline script. May be passed multiple times. + +``-p PATH`` or ``--config_path=PATH`` + Sets directory where configuration should be read from. If present, no + default locations are searched for configuration. No expansions are + performed by powerline script itself, but ``-p ~/.powerline`` will likely be + expanded by the shell to something like ``-p /home/user/.powerline``. + +Ipython overrides +----------------- + +Ipython overrides depend on ipython version. Before ipython-0.11 you should pass +additional keyword arguments to setup() function. After ipython-0.11 you should +use ``c.Powerline.KEY``. Supported ``KEY`` strings or keyword argument names: + +``config_overrides`` + Overrides options from :file:`powerline/config.json`. Should be a dictionary + that will be recursively merged with the contents of the file. + +``theme_overrides`` + Overrides options from :file:`powerline/themes/ipython/*.json`. Should be + a dictionary where keys are theme names and values are dictionaries which + will be recursively merged with the contents of the given theme. + +``path`` + Sets directory where configuration should be read from. If present, no + default locations are searched for configuration. No expansions are + performed thus you cannot use paths starting with ``~/``. diff --git a/powerline/__init__.py b/powerline/__init__.py index e69de29b..5275a53c 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +import importlib +import json +import os +import sys + +from powerline.colorscheme import Colorscheme +from powerline.lib import underscore_to_camelcase + + +def load_json_config(search_paths, config_file): + config_file += '.json' + for path in search_paths: + config_file_path = os.path.join(path, config_file) + if os.path.isfile(config_file_path): + with open(config_file_path, 'r') as config_file_fp: + return json.load(config_file_fp) + raise IOError('Config file not found in search path: {0}'.format(config_file)) + + +class Powerline(object): + def __init__(self, ext, renderer_module=None): + self.config_paths = self.get_config_paths() + + # Load main config file + config = self.load_main_config() + common_config = config['common'] + ext_config = config['ext'][ext] + self.ext = ext + + # Load and initialize colorscheme + colorscheme_config = self.load_colorscheme_config(ext_config['colorscheme']) + colorscheme = Colorscheme(colorscheme_config) + + # Load and initialize extension theme + theme_config = self.load_theme_config(ext_config.get('theme', 'default')) + common_config['paths'] = [os.path.expanduser(path) for path in common_config.get('paths', [])] + self.import_paths = common_config['paths'] + theme_kwargs = { + 'ext': ext, + 'colorscheme': colorscheme, + 'common_config': common_config, + 'segment_info': self.get_segment_info(), + } + local_themes = self.get_local_themes(ext_config.get('local_themes', {})) + + # Load and initialize extension renderer + renderer_module_name = renderer_module or ext + renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) + renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) + try: + Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) + except ImportError as e: + sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) + sys.exit(1) + options = {'term_truecolor': common_config.get('term_24bit_colors', False)} + self.renderer = Renderer(theme_config, local_themes, theme_kwargs, **options) + + def get_config_paths(self): + config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) + config_path = os.path.join(config_home, 'powerline') + plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') + return [config_path, plugin_path] + + def load_theme_config(self, name): + return load_json_config(self.config_paths, os.path.join('themes', self.ext, name)) + + def load_main_config(self): + return load_json_config(self.config_paths, 'config') + + def load_colorscheme_config(self, name): + return load_json_config(self.config_paths, os.path.join('colorschemes', self.ext, name)) + + @staticmethod + def get_local_themes(local_themes): + return {} + + @staticmethod + def get_segment_info(): + return None diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 439793d9..89c0ba69 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,4 +1,4 @@ -from powerline.core import Powerline +from powerline.ipython import IpythonPowerline from IPython.core.prompts import PromptManager @@ -19,8 +19,23 @@ class PowerlinePromptManager(PromptManager): return res if color else res_nocolor +class ConfigurableIpythonPowerline(IpythonPowerline): + def __init__(self, ip): + config = ip.config.Powerline + self.config_overrides = config.get('config_overrides') + self.theme_overrides = config.get('theme_overrides', {}) + self.path = config.get('path') + super(ConfigurableIpythonPowerline, self).__init__() + + def load_ipython_extension(ip): - powerline = Powerline('ipython') + global old_prompt_manager + + old_prompt_manager = ip.prompt_manager + powerline = ConfigurableIpythonPowerline(ip) ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell, config=ip.prompt_manager.config) + +def unload_ipython_extension(ip): + ip.prompt_manager = old_prompt_manager diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 18fa8384..0943025a 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,4 +1,4 @@ -from powerline.core import Powerline +from powerline.ipython import IpythonPowerline from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython @@ -18,10 +18,18 @@ class PowerlinePrompt(BasePrompt): return '%s>%s' % ('-' * self.prompt_text_len, ' ' * self.nrspaces) -def setup(prompt='1'): +class ConfigurableIpythonPowerline(IpythonPowerline): + def __init__(self, config_overrides=None, theme_overrides={}, path=None): + self.config_overrides = config_overrides + self.theme_overrides = theme_overrides + self.path = path + super(ConfigurableIpythonPowerline, self).__init__() + + +def setup(prompt='1', **kwargs): ip = get_ipython() - powerline = Powerline('ipython') + powerline = ConfigurableIpythonPowerline(**kwargs) attr = 'prompt' + prompt diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index c9ca5751..dc4132aa 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -3,7 +3,7 @@ from libqtile import bar from libqtile.widget import base -from powerline.core import Powerline as PowerlineCore +from powerline import Powerline as PowerlineCore class Powerline(base._TextBox): diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index a538c9fc..947db8b4 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -18,15 +18,16 @@ endif let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', 'py'), '\v^(py)%[thon](3?)$', '\1\2', '') let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') +let s:import_cmd = 'from powerline.vim import VimPowerline' try - exec s:powerline_pycmd 'from powerline.core import Powerline' + exec s:powerline_pycmd s:import_cmd catch " An error occured while importing the module, it could be installed " outside of Python's module search paths. Update sys.path and try again. exec s:powerline_pycmd 'import sys, vim' exec s:powerline_pycmd 'sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' try - exec s:powerline_pycmd 'from powerline.core import Powerline' + exec s:powerline_pycmd s:import_cmd catch call s:CriticalError('An error occured while importing the Powerline package. \ This could be caused by an invalid sys.path setting, or by an incompatible @@ -35,7 +36,8 @@ catch finish endtry endtry -exec s:powerline_pycmd 'powerline = Powerline("vim", segment_info={})' +exec s:powerline_pycmd 'powerline = VimPowerline()' +exec s:powerline_pycmd 'del VimPowerline' if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval) let s:pyeval = function(s:powerline_pyeval) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 75b36658..b0e3ffe4 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,8 +1,18 @@ import zsh -from powerline.core import Powerline +from powerline.shell import ShellPowerline + + +def get_var_config(var): + try: + return dict(((k, json.loads(v)) for k, v in zsh.getvalue(var).items())) + except: + return None class Args(object): + ext = ['shell'] + renderer_module = 'zsh_prompt' + @property def last_exit_code(self): return zsh.last_exit_code() @@ -11,6 +21,14 @@ class Args(object): def last_pipe_status(self): return zsh.pipestatus() + @property + def config(self): + return get_var_config('POWERLINE_CONFIG') + + @property + def theme_option(self): + return get_var_config('POWERLINE_THEME_CONFIG') + class Prompt(object): __slots__ = ('render', 'side', 'savedpsvar', 'savedps') @@ -38,6 +56,6 @@ def set_prompt(powerline, psvar, side): def setup(): - powerline = Powerline(ext='shell', renderer_module='zsh_prompt', segment_info=Args()) + powerline = ShellPowerline(Args()) set_prompt(powerline, 'PS1', 'left') set_prompt(powerline, 'RPS1', 'right') diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 29946684..a2fb4c53 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -1,20 +1,29 @@ { "default_module": "powerline.segments.common", + "segment_data": { + "hostname": { + "before": " ", + "args": { + "only_if_ssh": true + } + }, + "virtualenv": { + "before": "ⓔ " + }, + "branch": { + "before": " " + } + }, "segments": { "left": [ { - "name": "hostname", - "before": " ", - "args": { - "only_if_ssh": true - } + "name": "hostname" }, { "name": "user" }, { - "name": "virtualenv", - "before": "ⓔ " + "name": "virtualenv" }, { "name": "cwd", @@ -31,8 +40,7 @@ "highlight_group": "exit_fail" }, { - "name": "branch", - "before": " " + "name": "branch" } ] } diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index a81c9658..46e8c392 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -1,21 +1,32 @@ { "default_module": "powerline.segments.common", + "segment_data": { + "hostname": { + "before": " ", + "args": { + "only_if_ssh": true + } + }, + "virtualenv": { + "before": "ⓔ " + }, + "branch": { + "before": " " + } + }, "segments": { "left": [ { - "name": "hostname", - "before": " " + "name": "hostname" }, { "name": "user" }, { - "name": "virtualenv", - "before": "ⓔ " + "name": "virtualenv" }, { - "name": "branch", - "before": " " + "name": "branch" }, { "name": "cwd", diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 2c1ad292..841a75f7 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -1,16 +1,32 @@ { "default_module": "powerline.segments.common", + "segment_data": { + "uptime": { + "before": "⇑ " + }, + "external_ip": { + "before": "ⓦ " + }, + "date": { + "before": "⌚ " + }, + "email_imap_alert": { + "before": "✉ ", + "args": { + "username": "", + "password": "" + } + } + }, "segments": { "right": [ { "name": "uptime", - "before": "⇑ ", "priority": 50, "divider_highlight_group": "background:divider" }, { "name": "external_ip", - "before": "ⓦ ", "priority": 50, "divider_highlight_group": "background:divider" }, @@ -35,18 +51,12 @@ { "name": "date", "args": {"format": "%H:%M"}, - "before": "⌚ ", "highlight_group": ["time", "date"], "divider_highlight_group": "time:divider" }, { "name": "email_imap_alert", - "before": "✉ ", "priority": 10, - "args": { - "username": "", - "password": "" - } }, { "name": "hostname" diff --git a/powerline/core.py b/powerline/core.py deleted file mode 100644 index 6f4d5fe7..00000000 --- a/powerline/core.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -*- - -import importlib -import json -import os -import sys - -from powerline.colorscheme import Colorscheme -from powerline.matcher import Matcher -from powerline.lib import underscore_to_camelcase - - -class Powerline(object): - def __init__(self, ext, renderer_module=None, segment_info=None, renderer_options={}): - config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) - config_path = os.path.join(config_home, 'powerline') - plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') - self.search_paths = [config_path, plugin_path] - sys.path[:0] = self.search_paths - - # 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', ext, self.config_ext['colorscheme'])) - colorscheme = Colorscheme(colorscheme_config) - - # Load and initialize extension theme - theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) - self.config['paths'] = [os.path.expanduser(path) for path in self.config.get('paths', [])] - self.get_matcher = Matcher(ext, self.config['paths']).get - theme_kwargs = { - 'ext': ext, - 'colorscheme': colorscheme, - 'common_config': self.config, - 'segment_info': segment_info, - } - local_themes = {} - for key, local_theme_name in self.config_ext.get('local_themes', {}).items(): - key = self.get_matcher(key) - local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} - - # Load and initialize extension renderer - renderer_module_name = renderer_module or ext - renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) - renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) - try: - Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) - except ImportError as e: - sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) - sys.exit(1) - options = {'term_truecolor': self.config.get('term_24bit_colors', False)} - options.update(renderer_options) - self.renderer = Renderer(theme_config, local_themes, theme_kwargs, **options) - - def add_local_theme(self, key, config): - '''Add local themes at runtime (e.g. during vim session). - - Accepts key as first argument (same as keys in config.json: - ext/*/local_themes) and configuration dictionary as the second (has - format identical to themes/*/*.json) - - Returns True if theme was added successfully and False if theme with - the same matcher already exists - ''' - key = self.get_matcher(key) - try: - self.renderer.add_local_theme(key, {'config': config}) - except KeyError: - return False - else: - return True - - def _load_theme_config(self, ext, name): - return self._load_json_config(os.path.join('themes', ext, name)) - - 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, 'r') 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/ipython.py b/powerline/ipython.py new file mode 100644 index 00000000..498ca42b --- /dev/null +++ b/powerline/ipython.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +from powerline import Powerline +from powerline.lib import mergedicts + + +class IpythonPowerline(Powerline): + def __init__(self): + super(IpythonPowerline, self).__init__('ipython') + + def get_config_paths(self): + if self.path: + return [self.path] + else: + return super(IpythonPowerline, self).get_config_paths() + + def load_main_config(self): + r = super(IpythonPowerline, self).load_main_config() + if self.config_overrides: + mergedicts(r, self.config_overrides) + return r + + def load_theme_config(self, name): + r = super(IpythonPowerline, self).load_theme_config(name) + if name in self.theme_overrides: + mergedicts(r, self.theme_overrides[name]) + return r diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 5877f65b..ed4eaafb 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -6,3 +6,12 @@ from powerline.lib.url import urllib_read, urllib_urlencode # NOQA def underscore_to_camelcase(string): '''Return a underscore_separated_string as CamelCase.''' return ''.join(word.capitalize() or '_' for word in string.split('_')) + +def mergedicts(d1, d2): + '''Recursively merge two dictionaries. First dictionary is modified in-place. + ''' + for k in d2: + if k in d1 and type(d1[k]) is dict and type(d2[k]) is dict: + mergedicts(d1[k], d2[k]) + else: + d1[k] = d2[k] diff --git a/powerline/matcher.py b/powerline/matcher.py index e61b5615..31a0969c 100644 --- a/powerline/matcher.py +++ b/powerline/matcher.py @@ -4,19 +4,16 @@ from importlib import import_module import sys -class Matcher(object): - def __init__(self, ext, path): - self.ext = ext - self.path = path - - def get(self, match_name): +def gen_matcher_getter(ext, import_paths): + def get(match_name): match_module, separator, match_function = match_name.rpartition('.') if not separator: - match_module = 'powerline.matchers.{0}'.format(self.ext) + match_module = 'powerline.matchers.{0}'.format(ext) match_function = match_name oldpath = sys.path - sys.path = self.path + sys.path + sys.path = import_paths + sys.path try: return getattr(import_module(match_module), match_function) finally: sys.path = oldpath + return get diff --git a/powerline/shell.py b/powerline/shell.py new file mode 100644 index 00000000..7e11ac99 --- /dev/null +++ b/powerline/shell.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +from powerline import Powerline +from powerline.lib import mergedicts + + +def mergeargs(argvalue): + if not argvalue: + return None + l = argvalue + r = dict([argvalue[0]]) + for subval in argvalue[1:]: + mergedicts(r, dict([subval])) + return r + + +class ShellPowerline(Powerline): + def __init__(self, args): + self.args = args + self.theme_option = mergeargs(args.theme_option) or {} + super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module) + + def get_segment_info(self): + return self.args + + def load_main_config(self): + r = super(ShellPowerline, self).load_main_config() + if self.args.config: + mergedicts(r, mergeargs(self.args.config)) + return r + + def load_theme_config(self, name): + r = super(ShellPowerline, self).load_theme_config(name) + if name in self.theme_option: + mergedicts(r, self.theme_option[name]) + return r + + def get_config_paths(self): + if self.args.config_path: + return [self.args.config_path] + else: + return super(ShellPowerline, self).get_config_paths() diff --git a/powerline/vim.py b/powerline/vim.py new file mode 100644 index 00000000..89d3254a --- /dev/null +++ b/powerline/vim.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from powerline.bindings.vim import vim_get_func +from powerline import Powerline +from powerline.lib import mergedicts +from powerline.matcher import gen_matcher_getter +import vim + + +vim_exists = vim_get_func('exists', rettype=int) + +def _override_from(config, override_varname): + if vim_exists(override_varname): + # FIXME vim.eval has problem with numeric types, vim.bindeval may be + # absent (and requires converting values to python built-in types), + # vim.eval with typed call like the one I implemented in frawor is slow. + # Maybe eval(vime.eval('string({0})'.format(override_varname)))? + overrides = vim.eval(override_varname) + mergedicts(config, overrides) + return config + + +class VimPowerline(Powerline): + def __init__(self): + super(VimPowerline, self).__init__('vim') + + def add_local_theme(self, key, config): + '''Add local themes at runtime (during vim session). + + Accepts key as first argument (same as keys in config.json: + ext/*/local_themes) and configuration dictionary as the second (has + format identical to themes/*/*.json) + + Returns True if theme was added successfully and False if theme with + the same matcher already exists + ''' + key = self.get_matcher(key) + try: + self.renderer.add_local_theme(key, {'config': config}) + except KeyError: + return False + else: + return True + + def load_main_config(self): + return _override_from(super(VimPowerline, self).load_main_config(), 'g:powerline_config_overrides') + + def load_theme_config(self, name): + # Note: themes with non-[a-zA-Z0-9_] names are impossible to override + # (though as far as I know exists() won’t throw). Won’t fix, use proper + # theme names. + return _override_from(super(VimPowerline, self).load_theme_config(name), 'g:powerline_theme_overrides__'+name) + + def get_local_themes(self, local_themes): + self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) + r = {} + for key, local_theme_name in local_themes.items(): + key = self.get_matcher(key) + r[key] = {'config': self.load_theme_config(local_theme_name)} + return r + + def get_config_paths(self): + if vim_exists('g:powerline_config_path'): + return [vim.eval('g:powerline_config_path')] + else: + return super(VimPowerline, self).get_config_paths() + + @staticmethod + def get_segment_info(): + return {} diff --git a/scripts/powerline b/scripts/powerline index acdcf577..2e75825f 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -6,11 +6,11 @@ import sys import json try: - from powerline.core import Powerline + from powerline.shell import ShellPowerline except ImportError: import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from powerline.core import Powerline # NOQA + from powerline.shell import ShellPowerline # NOQA def oval(s): if '=' not in s: @@ -22,6 +22,20 @@ def oval(s): val = json.loads(s[idx+1:]) return (o, val) +def odotval(s): + o, val = oval(s) + keys = o.split('.') + if len(keys) > 1: + r = (keys[0], {}) + rcur = r[1] + for key in keys[1:-1]: + rcur[key] = {} + rcur = rcur[key] + rcur[keys[-1]] = val + return r + else: + return (o, val) + parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('ext', nargs=1) parser.add_argument('side', nargs='?', choices=('left', 'right')) @@ -29,11 +43,13 @@ parser.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) parser.add_argument('-w', '--width', type=int) parser.add_argument('--last_exit_code', metavar='INT', type=int) parser.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) -parser.add_argument('-o', '--renderer_option', nargs='*', metavar='OPTION=VALUE', type=oval) +parser.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', type=odotval, action='append') +parser.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', type=odotval, action='append') +parser.add_argument('-p', '--config_path', metavar='PATH') if __name__ == '__main__': args = parser.parse_args() - powerline = Powerline(ext=args.ext[0], renderer_module=args.renderer_module, segment_info=args, renderer_options=dict(args.renderer_option or {})) + powerline = ShellPowerline(args) rendered = powerline.renderer.render(width=args.width, side=args.side) try: sys.stdout.write(rendered) From 696cd97e1213bd55301eedb316214febb3664dfb Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Feb 2013 00:06:09 +0400 Subject: [PATCH 0388/1472] Replace getfsize with os.stat --- powerline/segments/vim.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f7dfa1d1..228e1ddc 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -18,7 +18,6 @@ vim_funcs = { 'virtcol': vim_get_func('virtcol', rettype=int), 'fnamemodify': vim_get_func('fnamemodify'), 'expand': vim_get_func('expand'), - 'getfsize': vim_get_func('getfsize', rettype=int), 'bufnr': vim_get_func('bufnr', rettype=int), } @@ -194,8 +193,9 @@ def file_size(segment_info, suffix='B', binary_prefix=False): :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' file_name = segment_info['buffer'].name - file_size = vim_funcs['getfsize'](file_name) - if file_size < 0: + try: + file_size = os.stat(file_name).st_size + except: return None return humanize_bytes(file_size, suffix, binary_prefix) From d9943e3222f78a1d725bf5d00a1f51ce0eb18ca3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Feb 2013 18:38:44 +0400 Subject: [PATCH 0389/1472] Fix various flake8 errors --- powerline/bindings/zsh/__init__.py | 1 + powerline/lib/__init__.py | 1 + powerline/segment.py | 7 ++++--- powerline/shell.py | 1 - powerline/vim.py | 4 +++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index b0e3ffe4..13795fbe 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,4 +1,5 @@ import zsh +import json from powerline.shell import ShellPowerline diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index ed4eaafb..fbdacfca 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -7,6 +7,7 @@ def underscore_to_camelcase(string): '''Return a underscore_separated_string as CamelCase.''' return ''.join(word.capitalize() or '_' for word in string.split('_')) + def mergedicts(d1, d2): '''Recursively merge two dictionaries. First dictionary is modified in-place. ''' diff --git a/powerline/segment.py b/powerline/segment.py index d8fd8170..0175ff04 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -12,7 +12,7 @@ def get_segment_key(segment, theme_configs, key, module=None, default=None): name = segment['name'] for theme_config in theme_configs: if 'segment_data' in theme_config: - for segment_key in ((module+'.'+name, name) if module else (name,)): + for segment_key in ((module + '.' + name, name) if module else (name,)): try: return theme_config['segment_data'][segment_key][key] except KeyError: @@ -47,9 +47,10 @@ segment_getters = { def gen_segment_getter(ext, path, theme_configs, default_module=None): data = { - 'default_module': default_module or 'powerline.segments.'+ext, + 'default_module': default_module or 'powerline.segments.' + ext, 'path': path, } + def get_key(segment, module, key, default=None): return get_segment_key(segment, theme_configs, key, module, default) data['get_key'] = get_key @@ -68,7 +69,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): 'highlight_group': highlight_group, 'divider_highlight_group': divider_highlight_group, 'before': get_key(segment, module, 'before', ''), - 'after': get_key(segment, module, 'after', ''), + 'after': get_key(segment, module, 'after', ''), 'contents_func': contents_func, 'contents': contents, 'args': get_key(segment, module, 'args', {}) if segment_type == 'function' else {}, diff --git a/powerline/shell.py b/powerline/shell.py index 7e11ac99..d394b7ec 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -7,7 +7,6 @@ from powerline.lib import mergedicts def mergeargs(argvalue): if not argvalue: return None - l = argvalue r = dict([argvalue[0]]) for subval in argvalue[1:]: mergedicts(r, dict([subval])) diff --git a/powerline/vim.py b/powerline/vim.py index 89d3254a..968aa510 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -11,6 +11,7 @@ import vim vim_exists = vim_get_func('exists', rettype=int) + def _override_from(config, override_varname): if vim_exists(override_varname): # FIXME vim.eval has problem with numeric types, vim.bindeval may be @@ -51,7 +52,8 @@ class VimPowerline(Powerline): # Note: themes with non-[a-zA-Z0-9_] names are impossible to override # (though as far as I know exists() won’t throw). Won’t fix, use proper # theme names. - return _override_from(super(VimPowerline, self).load_theme_config(name), 'g:powerline_theme_overrides__'+name) + return _override_from(super(VimPowerline, self).load_theme_config(name), + 'g:powerline_theme_overrides__' + name) def get_local_themes(self, local_themes): self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) From 41ffd8cf5afade8904d5baac103f2c8b42a4f5ed Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Feb 2013 23:22:00 +0400 Subject: [PATCH 0390/1472] Rename colorscheme variable to colorscheme_config --- powerline/colorscheme.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 0f499a8b..335cc3b9 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -7,20 +7,20 @@ ATTR_UNDERLINE = 4 class Colorscheme(object): - def __init__(self, colorscheme): + def __init__(self, colorscheme_config): '''Initialize a colorscheme.''' self.colors = {} self.modes_groups = {DEFAULT_MODE_KEY: {}} # Create a dict of color tuples with both a cterm and hex value - for color_name, color in colorscheme['colors'].items(): + for color_name, color in colorscheme_config['colors'].items(): try: self.colors[color_name] = (color[0], int(color[1], 16)) 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(): + for group_name, group_props in colorscheme_config['groups'].items(): group_attr_flag = self._get_attr_flag(group_props.get('attr', [])) self.modes_groups[DEFAULT_MODE_KEY][group_name] = { 'fg': self.colors[group_props['fg']], @@ -29,7 +29,7 @@ class Colorscheme(object): } # Create mode-specific highlighting for this group - for mode, translations in colorscheme.get('mode_translations', {}).items(): + for mode, translations in colorscheme_config.get('mode_translations', {}).items(): if not mode in self.modes_groups: self.modes_groups[mode] = {} if group_name in translations.get('groups', {}): From b8b0518e9bf958e808e170b7da2577ee076c731e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Feb 2013 00:49:48 +0400 Subject: [PATCH 0391/1472] Replace 'NONE' with None for guifg/guibg. No need in having string keys if we are in any case checking this for equality later instead of embedding them as-is like cterm* --- powerline/renderers/vim.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 145e180e..6d31fa92 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -68,9 +68,9 @@ class VimRenderer(Renderer): if not (fg, bg, attr) in self.hl_groups: hl_group = { 'ctermfg': 'NONE', - 'guifg': 'NONE', + 'guifg': None, 'ctermbg': 'NONE', - 'guibg': 'NONE', + 'guibg': None, 'attr': ['NONE'], 'name': '', } @@ -98,9 +98,9 @@ class VimRenderer(Renderer): 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', + guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] is not None else 'NONE', ctermbg=hl_group['ctermbg'], - guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] != 'NONE' else 'NONE', + guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE', attr=','.join(hl_group['attr']), )) return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' From 01b34a7893411a1c39fd350e85ba9f1a77f2881f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Feb 2013 01:05:18 +0400 Subject: [PATCH 0392/1472] Split colorschemes and add better gradient support - Splitted colorschemes into colors definitions file (TODO: remove non-colors definitions like weather_condition_hot and base00) and actual colorscheme. - Removed dictionary containing groups definitions for all groups for all modes, now colorscheme is queried for this each time. - Moved determination of colors from theme to renderer. - Added gradients definitions (actually, only one) to new colors file. - Made line_percent with gradient=True use new gradients. --- docs/source/configuration.rst | 35 ++-- powerline/__init__.py | 9 +- powerline/colorscheme.py | 151 ++++++++++-------- powerline/config_files/colors.json | 82 ++++++++++ .../colorschemes/ipython/default.json | 18 --- .../colorschemes/shell/default.json | 53 ------ .../colorschemes/shell/solarized.json | 18 --- .../colorschemes/tmux/default.json | 33 ---- .../colorschemes/vim/default.json | 74 +-------- .../colorschemes/vim/solarized.json | 18 --- .../config_files/colorschemes/wm/default.json | 33 ---- powerline/config_files/config.json | 3 +- powerline/renderer.py | 36 +++-- powerline/segment.py | 8 +- powerline/segments/vim.py | 3 +- powerline/theme.py | 14 +- 16 files changed, 229 insertions(+), 359 deletions(-) create mode 100644 powerline/config_files/colors.json diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index bbefffec..98111267 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -155,15 +155,12 @@ Common configuration is a subdictionary that is a value of ``ext`` key in Defines themes used when certain conditions are met, e.g. for buffer-specific statuslines in vim. Requires a custom matcher and theme. -Colorschemes -============ +Color definitions +================= -:Location: :file:`powerline/colorschemes/{extension}/{name}.json` +:Location: :file:`powerline/colors.json` -``name`` - Name of the colorscheme. - -.. _config-colorscheme-colors: +.. _config-colors-colors: ``colors`` Color definitions, consisting of a dict where the key is the name of the @@ -174,6 +171,22 @@ Colorschemes "aabbcc"]``). This is useful for colorschemes that use colors that aren't available in color terminals. +``gradients`` + Gradient definitions, consisting of a dict where the key is the name of the + gradient, and the value is a list containing one or two items, second item + is optional: + + * A list of cterm color indicies. + * A list of hex color strings. + +Colorschemes +============ + +:Location: :file:`powerline/colorschemes/{extension}/{name}.json` + +``name`` + Name of the colorscheme. + .. _config-colorscheme-groups: ``groups`` @@ -184,11 +197,11 @@ Colorschemes ``fg`` Foreground color. Must be defined in :ref:`colors - `. + `. ``bg`` Background color. Must be defined in :ref:`colors - `. + `. ``attr`` Optional list of attributes. Valid values are one or more of @@ -203,8 +216,8 @@ Colorschemes ``colors`` A dict where the key is the color to be translated in this mode, and - the value is the new color. Both the key and the value must be - defined in :ref:`colors `. + the value is the new color. Both the key and the value must be defined + in :ref:`colors `. ``groups`` Segment highlighting groups for this mode. Same syntax as the main diff --git a/powerline/__init__.py b/powerline/__init__.py index 5275a53c..5296cde9 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -31,7 +31,8 @@ class Powerline(object): # Load and initialize colorscheme colorscheme_config = self.load_colorscheme_config(ext_config['colorscheme']) - colorscheme = Colorscheme(colorscheme_config) + colors_config = self.load_colors_config() + colorscheme = Colorscheme(colorscheme_config, colors_config) # Load and initialize extension theme theme_config = self.load_theme_config(ext_config.get('theme', 'default')) @@ -39,7 +40,6 @@ class Powerline(object): self.import_paths = common_config['paths'] theme_kwargs = { 'ext': ext, - 'colorscheme': colorscheme, 'common_config': common_config, 'segment_info': self.get_segment_info(), } @@ -55,7 +55,7 @@ class Powerline(object): sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) options = {'term_truecolor': common_config.get('term_24bit_colors', False)} - self.renderer = Renderer(theme_config, local_themes, theme_kwargs, **options) + self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) def get_config_paths(self): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) @@ -72,6 +72,9 @@ class Powerline(object): def load_colorscheme_config(self, name): return load_json_config(self.config_paths, os.path.join('colorschemes', self.ext, name)) + def load_colors_config(self): + return load_json_config(self.config_paths, 'colors') + @staticmethod def get_local_themes(local_themes): return {} diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 335cc3b9..7fd85422 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,95 +1,108 @@ # -*- coding: utf-8 -*- +from copy import copy + + DEFAULT_MODE_KEY = None ATTR_BOLD = 1 ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 +def get_attr_flag(attributes): + '''Convert an attribute array to a renderer flag.''' + attr_flag = 0 + if 'bold' in attributes: + attr_flag |= ATTR_BOLD + if 'italic' in attributes: + attr_flag |= ATTR_ITALIC + if 'underline' in attributes: + attr_flag |= ATTR_UNDERLINE + return attr_flag + + +def pick_gradient_value(grad_list, gradient_level): + '''Given a list of colors and gradient percent, return a color that should be used. + + Note: gradient level is not checked for being inside [0, 100] interval. + ''' + return grad_list[int(round(gradient_level * (len(grad_list) - 1) / 100))] + + class Colorscheme(object): - def __init__(self, colorscheme_config): + def __init__(self, colorscheme_config, colors_config): '''Initialize a colorscheme.''' self.colors = {} - self.modes_groups = {DEFAULT_MODE_KEY: {}} + self.gradients = {} + + self.groups = colorscheme_config['groups'] + self.translations = colorscheme_config.get('mode_translations', {}) # Create a dict of color tuples with both a cterm and hex value - for color_name, color in colorscheme_config['colors'].items(): + for color_name, color in colors_config['colors'].items(): try: self.colors[color_name] = (color[0], int(color[1], 16)) except TypeError: self.colors[color_name] = (color, cterm_to_hex[color]) - # Create highlighting groups for all modes - for group_name, group_props in colorscheme_config['groups'].items(): - group_attr_flag = self._get_attr_flag(group_props.get('attr', [])) - self.modes_groups[DEFAULT_MODE_KEY][group_name] = { - 'fg': self.colors[group_props['fg']], - 'bg': self.colors[group_props['bg']], - 'attr': group_attr_flag, - } + # Create a dict of gradient names with two lists: for cterm and hex + # values. Two lists in place of one list of pairs were chosen because + # true colors allow more precise gradients. + for gradient_name, gradient in colors_config['gradients'].items(): + if len(gradient) == 2: + self.gradients[gradient_name] = ( + (gradient[0], [int(color, 16) for color in gradient[1]])) + else: + self.gradients[gradient_name] = ( + (gradient[0], [cterm_to_hex[color] for color in gradient[0]])) - # Create mode-specific highlighting for this group - for mode, translations in colorscheme_config.get('mode_translations', {}).items(): - if not mode in self.modes_groups: - self.modes_groups[mode] = {} - if group_name in translations.get('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.get('colors', {}).get(group_props['fg'], group_props['fg'])], - 'bg': self.colors[translations.get('colors', {}).get(group_props['bg'], group_props['bg'])], - 'attr': group_attr_flag, - } + def get_gradient(self, gradient, gradient_level): + if gradient in self.gradients: + return tuple((pick_gradient_value(grad_list, gradient_level) for grad_list in self.gradients[gradient])) + else: + return self.colors[gradient] - 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, groups, mode, gradient_level=None): + trans = self.translations.get(mode, {}) + for group in groups: + if 'groups' in trans and group in trans['groups']: + try: + group_props = trans['groups'][group] + except KeyError: + continue + break - def get_highlighting(self, group, mode=None): - '''Return highlighting information for a highlighting group and mode. + else: + try: + group_props = copy(self.groups[group]) + except KeyError: + continue - 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 = 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] + try: + ctrans = trans['colors'] + for key in ('fg', 'bg'): + try: + group_props[key] = ctrans[group_props[key]] + except KeyError: + pass + except KeyError: + pass + + break + else: + raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups)) + + if gradient_level is None: + pick_color = self.colors.__getitem__ + else: + pick_color = lambda gradient : self.get_gradient(gradient, gradient_level) + + return { + 'fg': pick_color(group_props['fg']), + 'bg': pick_color(group_props['bg']), + 'attr': get_attr_flag(group_props.get('attr', [])), + } - def _get_attr_flag(self, attributes): - '''Convert an attribute array to a renderer flag.''' - attr_flag = 0 - if 'bold' in attributes: - attr_flag |= ATTR_BOLD - if 'italic' in attributes: - attr_flag |= ATTR_ITALIC - if 'underline' in attributes: - attr_flag |= ATTR_UNDERLINE - return attr_flag cterm_to_hex = { 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json new file mode 100644 index 00000000..e4636a93 --- /dev/null +++ b/powerline/config_files/colors.json @@ -0,0 +1,82 @@ +{ + "colors": { + "black": 16, + "white": 231, + + "green": [2, "719e07"], + "darkestgreen": 22, + "darkgreen": 28, + "mediumgreen": 70, + "brightgreen": 148, + + "cyan": [6, "2aa198"], + "darkestcyan": 23, + "darkcyan": 74, + "mediumcyan": 117, + "brightcyan": 159, + + "blue": [4, "268bd2"], + "darkestblue": 24, + "darkblue": 31, + + "red": [1, "dc322f"], + "darkestred": 52, + "darkred": 88, + "mediumred": 124, + "brightred": 160, + "brightestred": 196, + + "magenta": [5, "d33682"], + + "darkestpurple": 55, + "mediumpurple": 98, + "brightpurple": 189, + + "violet": [13, "6c71c4"], + + "orange": [9, "cb4b16"], + "darkorange": 94, + "mediumorange": 166, + "brightorange": 208, + "brightestorange": 214, + + "yellow": [3, "b58900"], + "brightyellow": 220, + + "gray0": 233, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 252, + + "base03": [8, "002b36"], + "base02": [0, "073642"], + "base01": [10, "586e75"], + "base00": [11, "657b83"], + "base0": [12, "839496"], + "base1": [14, "93a1a1"], + "base2": [7, "eee8d5"], + "base3": [15, "fdf6e3"], + + "system_load_good": 106, + "system_load_bad": 178, + "system_load_ugly": 202, + + "weather_temp_cold": 67, + "weather_temp_hot": 166, + "weather_condition_cold": 117, + "weather_condition_hot": 228 + }, + "gradients": { + "green_yellow_red": [ + [190, 184, 178, 172, 166, 160], + ["8ae71c", "c2e821", "e9d926", "eaa72b", "eb7830", "ec4b35"] + ] + } +} diff --git a/powerline/config_files/colorschemes/ipython/default.json b/powerline/config_files/colorschemes/ipython/default.json index 3a9f7355..b45bed45 100644 --- a/powerline/config_files/colorschemes/ipython/default.json +++ b/powerline/config_files/colorschemes/ipython/default.json @@ -1,23 +1,5 @@ { "name": "Default color scheme for IPython prompt", - "colors": { - "black": 16, - "white": 231, - - "darkcyan": 74, - - "gray0": 233, - "gray1": 235, - "gray2": 236, - "gray3": 239, - "gray4": 240, - "gray5": 241, - "gray6": 244, - "gray7": 245, - "gray8": 247, - "gray9": 250, - "gray10": 252 - }, "groups": { "virtualenv": { "fg": "white", "bg": "darkcyan" }, "prompt": { "fg": "gray9", "bg": "gray4" } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 57cff291..0320d807 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -1,58 +1,5 @@ { "name": "Default color scheme for shell prompts", - "colors": { - "black": 16, - "white": 231, - - "darkestgreen": 22, - "darkgreen": 28, - "mediumgreen": 70, - "brightgreen": 148, - - "darkestcyan": 23, - "darkcyan": 74, - "mediumcyan": 117, - "brightcyan": 159, - - "darkestblue": 24, - "darkblue": 31, - - "darkestred": 52, - "darkred": 88, - "mediumred": 124, - "brightred": 160, - "brightestred": 196, - - "darkestpurple": 55, - "mediumpurple": 98, - "brightpurple": 189, - - "darkorange": 94, - "mediumorange": 166, - "brightorange": 208, - "brightestorange": 214, - - "brightyellow": 220, - - "gray0": 233, - "gray1": 235, - "gray2": 236, - "gray3": 239, - "gray4": 240, - "gray5": 241, - "gray6": 244, - "gray7": 245, - "gray8": 247, - "gray9": 250, - "gray10": 252, - - "gradient1": 190, - "gradient2": 184, - "gradient3": 178, - "gradient4": 172, - "gradient5": 166, - "gradient6": 160 - }, "groups": { "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index b850b34f..fe3216cd 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,23 +1,5 @@ { "name": "Solarized Dark", - "colors": { - "base03": [8, "002b36"], - "base02": [0, "073642"], - "base01": [10, "586e75"], - "base00": [11, "657b83"], - "base0": [12, "839496"], - "base1": [14, "93a1a1"], - "base2": [7, "eee8d5"], - "base3": [15, "fdf6e3"], - "yellow": [3, "b58900"], - "orange": [9, "cb4b16"], - "red": [1, "dc322f"], - "magenta": [5, "d33682"], - "violet": [13, "6c71c4"], - "blue": [4, "268bd2"], - "cyan": [6, "2aa198"], - "green": [2, "719e07"] - }, "groups": { "user": { "fg": "base3", "bg": "blue", "attr": ["bold"] }, "superuser": { "fg": "base3", "bg": "red", "attr": ["bold"] }, diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index d701c78a..37146011 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -1,38 +1,5 @@ { "name": "Default color scheme for terminal prompts", - "colors": { - "black": 16, - "white": 231, - - "brightred": 160, - - "darkestblue": 24, - "darkblue": 31, - "mediumblue": 38, - "brightblue": 117, - "brightestblue": 153, - - "gray0": 234, - "gray1": 235, - "gray2": 236, - "gray3": 239, - "gray4": 240, - "gray5": 241, - "gray6": 244, - "gray7": 245, - "gray8": 247, - "gray9": 250, - "gray10": 254, - - "system_load_good": 106, - "system_load_bad": 178, - "system_load_ugly": 202, - - "weather_temp_cold": 67, - "weather_temp_hot": 166, - "weather_condition_cold": 117, - "weather_condition_hot": 228 - }, "groups": { "background:divider": { "fg": "gray5", "bg": "gray0" }, "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 5e602bcb..42b69d6a 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -1,58 +1,5 @@ { "name": "Default color scheme", - "colors": { - "black": 16, - "white": 231, - - "darkestgreen": 22, - "darkgreen": 28, - "mediumgreen": 70, - "brightgreen": 148, - - "darkestcyan": 23, - "darkcyan": 74, - "mediumcyan": 117, - "brightcyan": 159, - - "darkestblue": 24, - "darkblue": 31, - - "darkestred": 52, - "darkred": 88, - "mediumred": 124, - "brightred": 160, - "brightestred": 196, - - "darkestpurple": 55, - "mediumpurple": 98, - "brightpurple": 189, - - "darkorange": 94, - "mediumorange": 166, - "brightorange": 208, - "brightestorange": 214, - - "brightyellow": 220, - - "gray0": 233, - "gray1": 235, - "gray2": 236, - "gray3": 239, - "gray4": 240, - "gray5": 241, - "gray6": 244, - "gray7": 245, - "gray8": 247, - "gray9": 250, - "gray10": 252, - - "gradient1": [190, "8ae71c"], - "gradient2": [184, "c2e821"], - "gradient3": [178, "e9d926"], - "gradient4": [172, "eaa72b"], - "gradient5": [166, "eb7830"], - "gradient6": [160, "ec4b35"] - }, "groups": { "background": { "fg": "white", "bg": "gray2" }, "background:divider": { "fg": "gray6", "bg": "gray2" }, @@ -74,12 +21,7 @@ "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4" }, "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, "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_percent_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, "col_current": { "fg": "gray6", "bg": "gray10" }, @@ -102,12 +44,7 @@ "gray9": "gray4", "gray10": "gray5", "white": "gray6", - "gradient1": "gray5", - "gradient2": "gray5", - "gradient3": "gray5", - "gradient4": "gray5", - "gradient5": "gray5", - "gradient6": "gray5" + "green_yellow_red": "gray5" } }, "i": { @@ -123,12 +60,7 @@ "gray8": "mediumcyan", "gray9": "mediumcyan", "gray10": "mediumcyan", - "gradient1": "mediumcyan", - "gradient2": "mediumcyan", - "gradient3": "mediumcyan", - "gradient4": "mediumcyan", - "gradient5": "mediumcyan", - "gradient6": "mediumcyan" + "green_yellow_red": "gray5" }, "groups": { "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }, diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 2cad7df0..825a4272 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,23 +1,5 @@ { "name": "Solarized Dark", - "colors": { - "base03": [8, "002b36"], - "base02": [0, "073642"], - "base01": [10, "586e75"], - "base00": [11, "657b83"], - "base0": [12, "839496"], - "base1": [14, "93a1a1"], - "base2": [7, "eee8d5"], - "base3": [15, "fdf6e3"], - "yellow": [3, "b58900"], - "orange": [9, "cb4b16"], - "red": [1, "dc322f"], - "magenta": [5, "d33682"], - "violet": [13, "6c71c4"], - "blue": [4, "268bd2"], - "cyan": [6, "2aa198"], - "green": [2, "719e07"] - }, "groups": { "background": { "fg": "base3", "bg": "base02" }, "background:divider": { "fg": "base00", "bg": "base02" }, diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json index 412e5d5b..210b4e4b 100644 --- a/powerline/config_files/colorschemes/wm/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -1,38 +1,5 @@ { "name": "Default color scheme for window managers", - "colors": { - "black": 16, - "white": 231, - - "brightred": 160, - - "darkestblue": 24, - "darkblue": 31, - "mediumblue": 38, - "brightblue": 117, - "brightestblue": 153, - - "gray0": 234, - "gray1": 235, - "gray2": 236, - "gray3": 239, - "gray4": 240, - "gray5": 241, - "gray6": 244, - "gray7": 245, - "gray8": 247, - "gray9": 250, - "gray10": 254, - - "system_load_good": 106, - "system_load_bad": 178, - "system_load_ugly": 202, - - "weather_temp_cold": 67, - "weather_temp_hot": 166, - "weather_condition_cold": 117, - "weather_condition_hot": 228 - }, "groups": { "background:divider": { "fg": "gray5", "bg": "gray0" }, "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index e2ae7c13..4c8ad85f 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -11,7 +11,8 @@ "soft": " " } }, - "spaces": 1 + "spaces": 1, + "colors": "default" }, "ext": { "ipython": { diff --git a/powerline/renderer.py b/powerline/renderer.py index 684b702e..9fdb7015 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -from powerline.colorscheme import DEFAULT_MODE_KEY from powerline.theme import Theme class Renderer(object): - def __init__(self, theme_config, local_themes, theme_kwargs, **options): + def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, **options): self.__dict__.update(options) self.theme_config = theme_config self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs + self.colorscheme = colorscheme def add_local_theme(self, matcher, theme): if matcher in self.local_themes: @@ -27,6 +27,14 @@ class Renderer(object): else: return self.theme + def get_highlighting(self, segment, mode): + segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) + if segment['divider_highlight_group']: + segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode) + else: + segment['divider_highlight'] = None + return segment + def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. @@ -43,10 +51,10 @@ class Renderer(object): theme.segment_info.update(segment_info) # Handle excluded/included segments for the current mode - segments = [segment for segment in segments + segments = [self.get_highlighting(segment, mode) for segment in segments if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] - segments = [segment for segment in self._render_segments(mode, theme, segments)] + segments = [segment for segment in self._render_segments(theme, segments)] if not width: # No width specified, so we don't need to crop or pad anything @@ -73,11 +81,11 @@ class Renderer(object): segment['_space_right'] += space_side segments_spacers[0]['_space_right'] += distribute_len_remainder - rendered_highlighted = u''.join([segment['_rendered_hl'] for segment in self._render_segments(mode, theme, segments)]) + self.hlstyle() + rendered_highlighted = u''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() return self._returned_value(rendered_highlighted, segments, output_raw) - def _render_segments(self, mode, theme, segments, render_highlighted=True): + def _render_segments(self, theme, segments, render_highlighted=True): '''Internal segment rendering method. This method loops through the segment array and compares the @@ -89,10 +97,6 @@ class Renderer(object): statusline if render_highlighted is True. ''' segments_len = len(segments) - try: - mode = mode if mode in segments[0]['highlight'] else DEFAULT_MODE_KEY - except IndexError: - pass for index, segment in enumerate(segments): segment['_rendered_raw'] = u'' @@ -102,7 +106,7 @@ class Renderer(object): next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT compare_segment = next_segment if segment['side'] == 'left' else prev_segment outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' - divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' + divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' divider_raw = theme.get_divider(segment['side'], divider_type) divider_spaces = theme.get_spaces() @@ -130,13 +134,13 @@ class Renderer(object): if render_highlighted: if divider_type == 'soft': divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' - divider_fg = segment[divider_highlight_group_key][mode]['fg'] - divider_bg = segment[divider_highlight_group_key][mode]['bg'] + divider_fg = segment[divider_highlight_group_key]['fg'] + divider_bg = segment[divider_highlight_group_key]['bg'] else: - divider_fg = segment['highlight'][mode]['bg'] - divider_bg = compare_segment['highlight'][mode]['bg'] + divider_fg = segment['highlight']['bg'] + divider_bg = compare_segment['highlight']['bg'] divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'][mode]) + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) # Append padded raw and highlighted segments to the rendered segment variables if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'): diff --git a/powerline/segment.py b/powerline/segment.py index 0175ff04..0efd3949 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -45,6 +45,10 @@ segment_getters = { } +def scalar_to_list(value): + return value if type(value) is list else [value] + + def gen_segment_getter(ext, path, theme_configs, default_module=None): data = { 'default_module': default_module or 'powerline.segments.' + ext, @@ -66,8 +70,8 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): divider_highlight_group = segment.get('divider_highlight_group') return { 'type': segment_type, - 'highlight_group': highlight_group, - 'divider_highlight_group': divider_highlight_group, + 'highlight_group': scalar_to_list(highlight_group), + 'divider_highlight_group': scalar_to_list(divider_highlight_group) if divider_highlight_group else None, 'before': get_key(segment, module, 'before', ''), 'after': get_key(segment, module, 'after', ''), 'contents_func': contents_func, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 228e1ddc..91f80e25 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -241,7 +241,8 @@ def line_percent(segment_info, gradient=False): return percentage return [{ 'contents': percentage, - 'highlight_group': ['line_percent_gradient' + str(int(5 * percentage // 100) + 1), 'line_percent'], + 'highlight_group': ['line_percent_gradient', 'line_percent'], + 'gradient_level': percentage, }] diff --git a/powerline/theme.py b/powerline/theme.py index db503866..14a79021 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -18,8 +18,7 @@ def requires_segment_info(func): class Theme(object): - def __init__(self, ext, colorscheme, theme_config, common_config, top_theme_config=None, segment_info=None): - self.colorscheme = colorscheme + def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = { @@ -28,7 +27,7 @@ class Theme(object): } self.EMPTY_SEGMENT = { 'contents': None, - 'highlight': defaultdict(lambda: {'fg': False, 'bg': False, 'attr': 0}) + 'highlight': {'fg': False, 'bg': False, 'attr': 0} } self.segment_info = segment_info theme_configs = [theme_config] @@ -45,14 +44,6 @@ class Theme(object): def get_spaces(self): return self.spaces - def add_highlight(self, segment): - segment['highlight'] = self.colorscheme.get_group_highlighting(segment['highlight_group']) - if segment['divider_highlight_group']: - segment['divider_highlight'] = self.colorscheme.get_group_highlighting(segment['divider_highlight_group']) - else: - segment['divider_highlight'] = None - return segment - def get_segments(self, side=None): '''Return all segments. @@ -92,7 +83,6 @@ class Theme(object): else: continue for segment in parsed_segments: - segment = self.add_highlight(segment) segment['contents'] = segment['before'] + unicode(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents if segment['width'] and segment['width'] != 'auto': From d0fafe7679c26e195246f988c1b5b864573950e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Feb 2013 13:57:45 +0400 Subject: [PATCH 0393/1472] Replace non-color names with a bit more descriptive names --- powerline/config_files/colors.json | 50 ++++---- .../colorschemes/shell/solarized.json | 20 ++-- .../colorschemes/tmux/default.json | 16 +-- .../colorschemes/vim/solarized.json | 111 +++++++++--------- .../config_files/colorschemes/wm/default.json | 16 +-- powerline/theme.py | 1 - 6 files changed, 106 insertions(+), 108 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index e4636a93..9a9c2e16 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -3,44 +3,34 @@ "black": 16, "white": 231, - "green": [2, "719e07"], "darkestgreen": 22, "darkgreen": 28, "mediumgreen": 70, "brightgreen": 148, - "cyan": [6, "2aa198"], "darkestcyan": 23, "darkcyan": 74, "mediumcyan": 117, "brightcyan": 159, - "blue": [4, "268bd2"], "darkestblue": 24, "darkblue": 31, - "red": [1, "dc322f"], "darkestred": 52, "darkred": 88, "mediumred": 124, "brightred": 160, "brightestred": 196, - "magenta": [5, "d33682"], - "darkestpurple": 55, "mediumpurple": 98, "brightpurple": 189, - "violet": [13, "6c71c4"], - - "orange": [9, "cb4b16"], "darkorange": 94, "mediumorange": 166, "brightorange": 208, "brightestorange": 214, - "yellow": [3, "b58900"], "brightyellow": 220, "gray0": 233, @@ -55,23 +45,33 @@ "gray9": 250, "gray10": 252, - "base03": [8, "002b36"], - "base02": [0, "073642"], - "base01": [10, "586e75"], - "base00": [11, "657b83"], - "base0": [12, "839496"], - "base1": [14, "93a1a1"], - "base2": [7, "eee8d5"], - "base3": [15, "fdf6e3"], + "gray61": [14, "93a1a1"], + "gray13": [8, "002b36"], - "system_load_good": 106, - "system_load_bad": 178, - "system_load_ugly": 202, + "royalblue5": [0, "073642"], + "darkgreencopper": [10, "586e75"], + "lightskyblue4": [11, "657b83"], + "azure4": [12, "839496"], + "lightyellow": [7, "eee8d5"], + "oldlace": [15, "fdf6e3"], - "weather_temp_cold": 67, - "weather_temp_hot": 166, - "weather_condition_cold": 117, - "weather_condition_hot": 228 + "green": [2, "719e07"], + "cyan": [6, "2aa198"], + "blue": [4, "268bd2"], + "red": [1, "dc322f"], + "magenta": [5, "d33682"], + "violet": [13, "6c71c4"], + "orange": [9, "cb4b16"], + "yellow": [3, "b58900"], + + "lightyellowgreen": 106, + "gold3": 178, + "orangered": 202, + + "steelblue": 67, + "darkorange3": 166, + "skyblue1": 117, + "khaki1": 228 }, "gradients": { "green_yellow_red": [ diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index fe3216cd..d847cef6 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,15 +1,15 @@ { "name": "Solarized Dark", "groups": { - "user": { "fg": "base3", "bg": "blue", "attr": ["bold"] }, - "superuser": { "fg": "base3", "bg": "red", "attr": ["bold"] }, - "virtualenv": { "fg": "base3", "bg": "green" }, - "branch": { "fg": "base1", "bg": "base02" }, - "cwd": { "fg": "base2", "bg": "base01" }, - "cwd:current_folder": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, - "cwd:divider": { "fg": "base1", "bg": "base01" }, - "hostname": { "fg": "base3", "bg": "base01" }, - "exit_fail": { "fg": "base3", "bg": "red" }, - "exit_success": { "fg": "base3", "bg": "green" } + "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, + "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, + "virtualenv": { "fg": "oldlace", "bg": "green" }, + "branch": { "fg": "gray61", "bg": "royalblue5" }, + "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, + "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, + "exit_fail": { "fg": "oldlace", "bg": "red" }, + "exit_success": { "fg": "oldlace", "bg": "green" } } } diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 37146011..67be9993 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -9,17 +9,17 @@ "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, - "weather_temp_hot": { "fg": "weather_temp_hot", "bg": "gray0" }, - "weather_condition_hot": { "fg": "weather_condition_hot", "bg": "gray0" }, - "weather_condition_snowy": { "fg": "weather_condition_cold", "bg": "gray0" }, - "weather_condition_rainy": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_temp_cold": { "fg": "steelblue", "bg": "gray0" }, + "weather_temp_hot": { "fg": "darkorange3", "bg": "gray0" }, + "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, + "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, + "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, "uptime": { "fg": "gray8", "bg": "gray0" }, "external_ip": { "fg": "gray8", "bg": "gray0" }, "network_load": { "fg": "gray8", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_good": { "fg": "system_load_good", "bg": "gray0" }, - "system_load_bad": { "fg": "system_load_bad", "bg": "gray0" }, - "system_load_ugly": { "fg": "system_load_ugly", "bg": "gray0" } + "system_load_good": { "fg": "lightyellowgreen", "bg": "gray0" }, + "system_load_bad": { "fg": "gold3", "bg": "gray0" }, + "system_load_ugly": { "fg": "orangered", "bg": "gray0" } } } diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 825a4272..6d724b65 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,82 +1,81 @@ { "name": "Solarized Dark", "groups": { - "background": { "fg": "base3", "bg": "base02" }, - "background:divider": { "fg": "base00", "bg": "base02" }, - "mode": { "fg": "base3", "bg": "green", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "base01", "attr": ["bold"] }, - "paste_indicator": { "fg": "base3", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "base01" }, - "branch": { "fg": "base2", "bg": "base01" }, - "branch:divider": { "fg": "base1", "bg": "base01" }, - "file_directory": { "fg": "base2", "bg": "base01" }, - "file_name": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, - "file_size": { "fg": "base3", "bg": "base01" }, - "file_name_no_file": { "fg": "base3", "bg": "base01", "attr": ["bold"] }, - "file_name_empty": { "fg": "base3", "bg": "base01" }, - "file_format": { "fg": "base1", "bg": "base02" }, - "file_encoding": { "fg": "base1", "bg": "base02" }, - "file_type": { "fg": "base1", "bg": "base02" }, - "file_vcs_status": { "fg": "red", "bg": "base01" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "base01" }, - "file_vcs_status_A": { "fg": "green", "bg": "base01" }, - "line_percent": { "fg": "base3", "bg": "base00" }, - "line_current": { "fg": "base03", "bg": "base2", "attr": ["bold"] }, - "line_current_symbol": { "fg": "base03", "bg": "base2" }, - "col_current": { "fg": "base0", "bg": "base2" } + "background": { "fg": "oldlace", "bg": "royalblue5" }, + "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5" }, + "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, + "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "darkgreencopper" }, + "branch": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "branch:divider": { "fg": "gray61", "bg": "darkgreencopper" }, + "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "file_size": { "fg": "oldlace", "bg": "darkgreencopper" }, + "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper" }, + "file_format": { "fg": "gray61", "bg": "royalblue5" }, + "file_encoding": { "fg": "gray61", "bg": "royalblue5" }, + "file_type": { "fg": "gray61", "bg": "royalblue5" }, + "file_vcs_status": { "fg": "red", "bg": "darkgreencopper" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper" }, + "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper" }, + "line_percent": { "fg": "oldlace", "bg": "lightskyblue4" }, + "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, + "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, + "col_current": { "fg": "azure4", "bg": "lightyellow" } }, "mode_translations": { "nc": { "colors": { - "base02": "base02", - "base01": "base02", - "base00": "base02", - "base0": "base01", - "base1": "base00", - "base2": "base0", - "base3": "base1" + "darkgreencopper": "royalblue5", + "lightskyblue4": "royalblue5", + "azure4": "darkgreencopper", + "gray61": "lightskyblue4", + "lightyellow": "azure4", + "oldlace": "gray61" } }, "i": { "groups": { - "background": { "fg": "base3", "bg": "base01" }, - "background:divider": { "fg": "base2", "bg": "base01" }, - "mode": { "fg": "base3", "bg": "blue", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "base2", "attr": ["bold"] }, - "paste_indicator": { "fg": "base3", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "base2" }, - "branch": { "fg": "base01", "bg": "base2" }, - "branch:divider": { "fg": "base00", "bg": "base2" }, - "file_directory": { "fg": "base01", "bg": "base2" }, - "file_name": { "fg": "base02", "bg": "base2", "attr": ["bold"] }, - "file_size": { "fg": "base02", "bg": "base2" }, - "file_name_no_file": { "fg": "base02", "bg": "base2", "attr": ["bold"] }, - "file_name_empty": { "fg": "base02", "bg": "base2" }, - "file_format": { "fg": "base2", "bg": "base01" }, - "file_encoding": { "fg": "base2", "bg": "base01" }, - "file_type": { "fg": "base2", "bg": "base01" }, - "file_vcs_status": { "fg": "red", "bg": "base2" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "base2" }, - "file_vcs_status_A": { "fg": "green", "bg": "base2" }, - "line_percent": { "fg": "base3", "bg": "base1" }, - "line_current": { "fg": "base03", "bg": "base3", "attr": ["bold"] }, - "line_current_symbol": { "fg": "base03", "bg": "base3" }, - "col_current": { "fg": "base0", "bg": "base3" } + "background": { "fg": "oldlace", "bg": "darkgreencopper" }, + "background:divider": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, + "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "lightyellow" }, + "branch": { "fg": "darkgreencopper", "bg": "lightyellow" }, + "branch:divider": { "fg": "lightskyblue4", "bg": "lightyellow" }, + "file_directory": { "fg": "darkgreencopper", "bg": "lightyellow" }, + "file_name": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, + "file_size": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_name_no_file": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, + "file_name_empty": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_format": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "file_encoding": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "file_type": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "file_vcs_status": { "fg": "red", "bg": "lightyellow" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow" }, + "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, + "line_percent": { "fg": "oldlace", "bg": "gray61" }, + "line_current": { "fg": "gray13", "bg": "oldlace", "attr": ["bold"] }, + "line_current_symbol": { "fg": "gray13", "bg": "oldlace" }, + "col_current": { "fg": "azure4", "bg": "oldlace" } } }, "v": { "groups": { - "mode": { "fg": "base3", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "beige", "bg": "orange", "attr": ["bold"] } } }, "V": { "groups": { - "mode": { "fg": "base3", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "beige", "bg": "orange", "attr": ["bold"] } } }, "R": { "groups": { - "mode": { "fg": "base3", "bg": "red", "attr": ["bold"] } + "mode": { "fg": "beige", "bg": "red", "attr": ["bold"] } } } } diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json index 210b4e4b..90bd6cc4 100644 --- a/powerline/config_files/colorschemes/wm/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -9,17 +9,17 @@ "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_cold": { "fg": "weather_temp_cold", "bg": "gray0" }, - "weather_temp_hot": { "fg": "weather_temp_hot", "bg": "gray0" }, - "weather_condition_hot": { "fg": "weather_condition_hot", "bg": "gray0" }, - "weather_condition_snowy": { "fg": "weather_condition_cold", "bg": "gray0" }, - "weather_condition_rainy": { "fg": "weather_condition_cold", "bg": "gray0" }, + "weather_temp_cold": { "fg": "steelblue", "bg": "gray0" }, + "weather_temp_hot": { "fg": "darkorange3", "bg": "gray0" }, + "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, + "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, + "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, "uptime": { "fg": "gray8", "bg": "gray0" }, "external_ip": { "fg": "gray8", "bg": "gray0" }, "network_load": { "fg": "gray8", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_good": { "fg": "system_load_good", "bg": "gray0" }, - "system_load_bad": { "fg": "system_load_bad", "bg": "gray0" }, - "system_load_ugly": { "fg": "system_load_ugly", "bg": "gray0" } + "system_load_good": { "fg": "lightyellowgreen", "bg": "gray0" }, + "system_load_bad": { "fg": "gold3", "bg": "gray0" }, + "system_load_ugly": { "fg": "orangered", "bg": "gray0" } } } diff --git a/powerline/theme.py b/powerline/theme.py index 14a79021..03e11f48 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from collections import defaultdict from copy import copy from .segment import gen_segment_getter From 045d60fbc4a621a399549992cd2c4bd3ddbf22db Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Feb 2013 14:22:44 +0400 Subject: [PATCH 0394/1472] Optimize cterm_to_hex Dictionary is an overkill here. Tuple is faster. Also now the whole tuple can be seen on the 1/4 of my screen (32x119 characters) and colors are easier to find (useful only when seeking for normal color names). Again, it would be goot to ignore flake8 in this case. Without comments it complains about it is not easy to find requested color by hand --- powerline/colorscheme.py | 71 ++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 7fd85422..f1c1b1b5 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -104,45 +104,32 @@ class Colorscheme(object): } -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, - 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, - 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, - 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, - 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, - 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, - 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, - 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, - 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, - 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, - 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, - 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, - 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, - 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, - 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, - 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, - 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, - 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, - 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, - 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, - 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, - 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, - 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, - 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, - 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, - 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, - 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, - 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, - 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, - 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, - 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, - 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, - 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, - 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, - 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, - 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, - 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, - 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, - 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, - } +# 0 1 2 3 4 5 6 7 8 9 +cterm_to_hex = ( + 0x000000, 0xc00000, 0x008000, 0x804000, 0x0000c0, 0xc000c0, 0x008080, 0xc0c0c0, 0x808080, 0xff6060, # 0 + 0x00ff00, 0xffff00, 0x8080ff, 0xff40ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, 0x000087, 0x0000af, # 1 + 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, # 2 + 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, # 3 + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, # 4 + 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, # 5 + 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, # 6 + 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, # 7 + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, # 8 + 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, # 9 + 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, # 10 + 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, # 11 + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, # 12 + 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, # 13 + 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, # 14 + 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, # 15 + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, # 16 + 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, # 17 + 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, # 18 + 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, # 19 + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, # 20 + 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, # 21 + 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, # 22 + 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, # 23 + 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, # 24 + 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee # 25 +) From f319ddc33b870e0997e2d8a4646c51e81251147e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Feb 2013 15:04:15 +0400 Subject: [PATCH 0395/1472] Ignore highlight definitions for function segments Also removed divider_highlight_group from configuration: it is actually used only in function segments. Fixes #215 --- docs/source/configuration.rst | 3 ++- .../config_files/themes/shell/default.json | 3 +-- .../themes/shell/default_leftonly.json | 3 +-- .../config_files/themes/tmux/default.json | 22 ++++++++----------- .../config_files/themes/vim/default.json | 12 ++++------ powerline/config_files/themes/wm/default.json | 12 +++++----- powerline/lib/__init__.py | 18 +++++++++++++++ powerline/renderer.py | 2 +- powerline/segment.py | 5 ++--- powerline/segments/common.py | 17 +++++++++++--- powerline/segments/vim.py | 14 +++++++++--- 11 files changed, 69 insertions(+), 42 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 98111267..5bc8ba93 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -90,7 +90,6 @@ segments that you may want to customize right away: { "name": "weather", "priority": 50, - "divider_highlight_group": "background:divider" "args": { "unit": "f", "location_query": "oslo, norway" @@ -289,6 +288,8 @@ Themes of highlighting groups, where the first highlighting group that is available in the colorscheme is used. + Ignored for segments that have ``function`` type. + ``before`` .. _config-themes-seg-before: diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index a2fb4c53..fe34dd89 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -29,8 +29,7 @@ "name": "cwd", "args": { "dir_limit_depth": 3 - }, - "divider_highlight_group": "cwd:divider" + } } ], "right": [ diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 46e8c392..85570107 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -32,8 +32,7 @@ "name": "cwd", "args": { "dir_limit_depth": 3 - }, - "divider_highlight_group": "cwd:divider" + } }, { "name": "last_status", diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 841a75f7..d4f12d43 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -22,37 +22,33 @@ "right": [ { "name": "uptime", - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "external_ip", - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "network_load", - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "system_load", - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "weather", - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "date" }, { "name": "date", - "args": {"format": "%H:%M"}, - "highlight_group": ["time", "date"], - "divider_highlight_group": "time:divider" + "args": { + "format": "%H:%M", + "istime": true + } }, { "name": "email_imap_alert", diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 543d717e..3d64018c 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -28,8 +28,7 @@ { "name": "branch", "exclude_modes": ["nc"], - "priority": 60, - "divider_highlight_group": "branch:divider" + "priority": 60 }, { "name": "readonly_indicator", @@ -66,20 +65,17 @@ "name": "file_format", "draw_divider": false, "exclude_modes": ["nc"], - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "file_encoding", "exclude_modes": ["nc"], - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "file_type", "exclude_modes": ["nc"], - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "line_percent", diff --git a/powerline/config_files/themes/wm/default.json b/powerline/config_files/themes/wm/default.json index dd834975..c1cee4b7 100644 --- a/powerline/config_files/themes/wm/default.json +++ b/powerline/config_files/themes/wm/default.json @@ -4,18 +4,18 @@ "right": [ { "name": "weather", - "priority": 50, - "divider_highlight_group": "background:divider" + "priority": 50 }, { "name": "date" }, { "name": "date", - "args": {"format": "%H:%M"}, - "before": "⌚ ", - "highlight_group": ["time", "date"], - "divider_highlight_group": "time:divider" + "args": { + "format": "%H:%M", + "istime": true + }, + "before": "⌚ " }, { "name": "email_imap_alert", diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index fbdacfca..8cdf9d2d 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,3 +1,5 @@ +from functools import wraps + from powerline.lib.memoize import memoize # NOQA from powerline.lib.humanize_bytes import humanize_bytes # NOQA from powerline.lib.url import urllib_read, urllib_urlencode # NOQA @@ -16,3 +18,19 @@ def mergedicts(d1, d2): mergedicts(d1[k], d2[k]) else: d1[k] = d2[k] + + +def add_divider_highlight_group(highlight_group): + def dec(func): + @wraps(func) + def f(**kwargs): + r = func(**kwargs) + if r: + return [{ + 'contents': r, + 'divider_highlight_group': highlight_group, + }] + else: + return None + return f + return dec diff --git a/powerline/renderer.py b/powerline/renderer.py index 9fdb7015..e9238204 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -30,7 +30,7 @@ class Renderer(object): def get_highlighting(self, segment, mode): segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) if segment['divider_highlight_group']: - segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode) + segment['divider_highlight'] = self.colorscheme.get_highlighting([segment['divider_highlight_group']], mode) else: segment['divider_highlight'] = None return segment diff --git a/powerline/segment.py b/powerline/segment.py index 0efd3949..bb7eacca 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -66,12 +66,11 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): except KeyError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) contents, contents_func, module = get_segment_info(data, segment) - highlight_group = segment.get('highlight_group', segment.get('name')) - divider_highlight_group = segment.get('divider_highlight_group') + highlight_group = segment_type != 'function' and segment.get('highlight_group') or segment.get('name') return { 'type': segment_type, 'highlight_group': scalar_to_list(highlight_group), - 'divider_highlight_group': scalar_to_list(divider_highlight_group) if divider_highlight_group else None, + 'divider_highlight_group': None, 'before': get_key(segment, module, 'before', ''), 'after': get_key(segment, module, 'after', ''), 'contents_func': contents_func, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 6f458d01..9b82e97a 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -3,7 +3,8 @@ import os import sys -from powerline.lib import memoize, urllib_read, urllib_urlencode +from powerline.lib import memoize, urllib_read, urllib_urlencode, add_divider_highlight_group +from functools import wraps def hostname(only_if_ssh=False): @@ -80,19 +81,24 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): continue ret.append({ 'contents': part, + 'divider_highlight_group': 'cwd:divider', }) ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] return ret -def date(format='%Y-%m-%d'): +def date(format='%Y-%m-%d', istime=False): '''Return the current date. :param str format: strftime-style date format string ''' from datetime import datetime - return datetime.now().strftime(format) + return [{ + 'contents': datetime.now().strftime(format), + 'highlight_group': (['time'] if istime else []) + ['date'], + 'divider_highlight_group': 'time:divider' if istime else None, + }] def fuzzy_time(): @@ -148,6 +154,7 @@ def fuzzy_time(): @memoize(600) +@add_divider_highlight_group('background:divider') def external_ip(query_url='http://ipv4.icanhazip.com/'): '''Return external IP address. @@ -163,6 +170,7 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): return urllib_read(query_url).strip() +@add_divider_highlight_group('background:divider') def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): '''Return system uptime. @@ -265,6 +273,7 @@ weather_conditions_icons = { @memoize(1800) +@add_divider_highlight_group('background:divider') def weather(unit='c', location_query=None, icons=None): '''Return weather from Yahoo! Weather. @@ -360,6 +369,7 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): 'contents': format.format(avg=avg), 'highlight_group': [hl, 'system_load'], 'draw_divider': False, + 'divider_highlight_group': 'background:divider', }) ret[0]['draw_divider'] = True ret[0]['contents'] += ' ' @@ -383,6 +393,7 @@ def cpu_load_percent(measure_interval=.5): return u'{0}%'.format(cpu_percent) +@add_divider_highlight_group('background:divider') def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_prefix=False): '''Return the network load. diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 91f80e25..3e6ba129 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -10,7 +10,7 @@ except ImportError: from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info -from powerline.lib import memoize, humanize_bytes +from powerline.lib import memoize, humanize_bytes, add_divider_highlight_group from powerline.lib.vcs import guess from collections import defaultdict @@ -201,6 +201,7 @@ def file_size(segment_info, suffix='B', binary_prefix=False): @requires_segment_info +@add_divider_highlight_group('background:divider') def file_format(segment_info): '''Return file format (i.e. line ending type). @@ -210,6 +211,7 @@ def file_format(segment_info): @requires_segment_info +@add_divider_highlight_group('background:divider') def file_encoding(segment_info): '''Return file encoding/character set. @@ -219,6 +221,7 @@ def file_encoding(segment_info): @requires_segment_info +@add_divider_highlight_group('background:divider') def file_type(segment_info): '''Return file type. @@ -262,7 +265,9 @@ def col_current(segment_info): @window_cached def virtcol_current(): '''Return current visual column with concealed characters ingored''' - return vim_funcs['virtcol']('.') + return [{'contents': vim_funcs['virtcol']('.'), + 'highlight_group': ['virtcol_current', 'col_current'], + }] def modified_buffers(text=u'+', join_str=','): @@ -286,7 +291,10 @@ def branch(segment_info): '''Return the current working branch.''' repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: - return repo.branch() + return [{ + 'contents': repo.branch(), + 'divider_highlight_group': 'branch:divider', + }] return None From c8c713321d92456752ee161a8c1c2647421dcd3c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Feb 2013 21:16:51 +0400 Subject: [PATCH 0396/1472] Use smoother gradient for true color --- powerline/config_files/colors.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 9a9c2e16..5975572c 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -76,7 +76,7 @@ "gradients": { "green_yellow_red": [ [190, 184, 178, 172, 166, 160], - ["8ae71c", "c2e821", "e9d926", "eaa72b", "eb7830", "ec4b35"] + ["8ae71c", "8ce71c", "8fe71c", "92e71c", "95e71d", "98e71d", "9ae71d", "9de71d", "a0e71e", "a3e71e", "a6e71e", "a8e71e", "abe71f", "aee71f", "b1e71f", "b4e71f", "b6e720", "b9e720", "bce720", "bfe720", "c2e821", "c3e721", "c5e621", "c7e521", "c9e522", "cbe422", "cde322", "cfe222", "d1e223", "d3e123", "d5e023", "d7df23", "d9df24", "dbde24", "dddd24", "dfdc24", "e1dc25", "e3db25", "e5da25", "e7d925", "e9d926", "e9d626", "e9d426", "e9d126", "e9cf27", "e9cc27", "e9ca27", "e9c727", "e9c528", "e9c228", "e9c028", "e9bd28", "e9bb29", "e9b829", "e9b629", "e9b329", "e9b12a", "e9ae2a", "e9ac2a", "e9a92a", "eaa72b", "eaa42b", "eaa22b", "ea9f2b", "ea9d2c", "ea9b2c", "ea982c", "ea962c", "ea942d", "ea912d", "ea8f2d", "ea8d2d", "ea8a2e", "ea882e", "ea862e", "ea832e", "ea812f", "ea7f2f", "ea7c2f", "ea7a2f", "eb7830", "eb7530", "eb7330", "eb7130", "eb6f31", "eb6c31", "eb6a31", "eb6831", "eb6632", "eb6332", "eb6132", "eb5f32", "eb5d33", "eb5a33", "eb5833", "eb5633", "eb5434", "eb5134", "eb4f34", "eb4d34", "ec4b35"] ] } } From 168f4854c1ebb64a7dbe3c08d6d8f52b4883cf6d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Feb 2013 13:37:50 +0400 Subject: [PATCH 0397/1472] Fix various flake8 errors --- powerline/bindings/ipython/post_0_11.py | 4 ++ powerline/colorscheme.py | 54 ++++++++++++------------- powerline/segments/common.py | 1 - powerline/segments/vim.py | 3 +- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 89c0ba69..f1b8729d 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -28,6 +28,9 @@ class ConfigurableIpythonPowerline(IpythonPowerline): super(ConfigurableIpythonPowerline, self).__init__() +old_prompt_manager = None + + def load_ipython_extension(ip): global old_prompt_manager @@ -37,5 +40,6 @@ def load_ipython_extension(ip): ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell, config=ip.prompt_manager.config) + def unload_ipython_extension(ip): ip.prompt_manager = old_prompt_manager diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index f1c1b1b5..7d7e1b5e 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -95,7 +95,7 @@ class Colorscheme(object): if gradient_level is None: pick_color = self.colors.__getitem__ else: - pick_color = lambda gradient : self.get_gradient(gradient, gradient_level) + pick_color = lambda gradient: self.get_gradient(gradient, gradient_level) return { 'fg': pick_color(group_props['fg']), @@ -106,30 +106,30 @@ class Colorscheme(object): # 0 1 2 3 4 5 6 7 8 9 cterm_to_hex = ( - 0x000000, 0xc00000, 0x008000, 0x804000, 0x0000c0, 0xc000c0, 0x008080, 0xc0c0c0, 0x808080, 0xff6060, # 0 - 0x00ff00, 0xffff00, 0x8080ff, 0xff40ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, 0x000087, 0x0000af, # 1 - 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, # 2 - 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, # 3 - 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, # 4 - 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, # 5 - 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, # 6 - 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, # 7 - 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, # 8 - 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, # 9 - 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, # 10 - 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, # 11 - 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, # 12 - 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, # 13 - 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, # 14 - 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, # 15 - 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, # 16 - 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, # 17 - 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, # 18 - 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, # 19 - 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, # 20 - 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, # 21 - 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, # 22 - 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, # 23 - 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, # 24 - 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee # 25 + 0x000000, 0xc00000, 0x008000, 0x804000, 0x0000c0, 0xc000c0, 0x008080, 0xc0c0c0, 0x808080, 0xff6060, # 0 + 0x00ff00, 0xffff00, 0x8080ff, 0xff40ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, 0x000087, 0x0000af, # 1 + 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, # 2 + 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, # 3 + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, # 4 + 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, # 5 + 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, # 6 + 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, # 7 + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, # 8 + 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, # 9 + 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, # 10 + 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, # 11 + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, # 12 + 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, # 13 + 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, # 14 + 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, # 15 + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, # 16 + 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, # 17 + 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, # 18 + 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, # 19 + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, # 20 + 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, # 21 + 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, # 22 + 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, # 23 + 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, # 24 + 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee # 25 ) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 9b82e97a..ff124adc 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -4,7 +4,6 @@ import os import sys from powerline.lib import memoize, urllib_read, urllib_urlencode, add_divider_highlight_group -from functools import wraps def hostname(only_if_ssh=False): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 3e6ba129..bdf8898b 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -266,8 +266,7 @@ def col_current(segment_info): def virtcol_current(): '''Return current visual column with concealed characters ingored''' return [{'contents': vim_funcs['virtcol']('.'), - 'highlight_group': ['virtcol_current', 'col_current'], - }] + 'highlight_group': ['virtcol_current', 'col_current']}] def modified_buffers(text=u'+', join_str=','): From dfaf381040b0f4913ea8bcb0526923fb6f8c6102 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Feb 2013 12:21:58 +0400 Subject: [PATCH 0398/1472] Fix various segment issues - Fixed user segment (windows): there is no os.geteuid() here - Fixed dir_limit_depth in cwd segment (it was not really accepting None value) - Made paste_indicator not use getbufvar for global option - Made file_size use buffer name for cache key - Made a number of segments coerce returned integer values to strings - Fixed modified_buffers (getbufvar() returns empty string for deleted buffers, but int() does not accept empty string) - Fixed file_directory and file_name segments: they were not working in python-3.3 because str() object has no attribute decode() - Made powerline.lib.vcs.git.Repository.status() always return 3-characters string - Made it always check both for dirty index and dirty wt for all files - Fixed binary prefix handling, renamed it to si_prefix Previously there was the following when binary_prefix was False: div = 1024 unit = kB (unit is inconsistent with div). Now it is the following when si_prefix is False: div = 1024 unit = KiB (according to https://en.wikipedia.org/wiki/Binary_prefix IEC prefix kibi is abbreviated as Ki, not ki (note the case)). This means that in segments prefixes has changed, but not displayed values. --- powerline/lib/humanize_bytes.py | 8 +++---- powerline/lib/vcs/git.py | 21 ++++++++++++------- powerline/segments/common.py | 18 +++++++++------- powerline/segments/vim.py | 37 +++++++++++++++++++-------------- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index 5d82c8b7..d69726fa 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -4,19 +4,19 @@ from math import log unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2])) -def humanize_bytes(num, suffix='B', binary_prefix=False): +def humanize_bytes(num, suffix='B', si_prefix=False): '''Return a human friendly byte representation. Modified version from http://stackoverflow.com/questions/1094841 ''' if num == 0: return '0 ' + suffix - div = 1000 if binary_prefix else 1024 + div = 1000 if si_prefix else 1024 exponent = min(int(log(num, div)) if num else 0, len(unit_list) - 1) quotient = float(num) / div ** exponent unit, decimals = unit_list[exponent] - if unit and binary_prefix: - unit += 'i' + if unit and not si_prefix: + unit = unit.upper() + 'i' return '{{quotient:.{decimals}f}} {{unit}}{{suffix}}'\ .format(decimals=decimals)\ .format(quotient=quotient, unit=unit, suffix=suffix) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index d2dc8fc3..2c92c45a 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -60,15 +60,18 @@ try: index_column = ' ' untracked_column = ' ' for status in self._repo().status().values(): + if status & git.GIT_STATUS_WT_NEW: + untracked_column = 'U' + continue + if status & (git.GIT_STATUS_WT_DELETED | git.GIT_STATUS_WT_MODIFIED): wt_column = 'D' - elif status & (git.GIT_STATUS_INDEX_NEW + + if status & (git.GIT_STATUS_INDEX_NEW | git.GIT_STATUS_INDEX_MODIFIED | git.GIT_STATUS_INDEX_DELETED): index_column = 'I' - elif status & git.GIT_STATUS_WT_NEW: - untracked_column = 'U' return wt_column + index_column + untracked_column def branch(self): @@ -117,14 +120,18 @@ except ImportError: for line in self._gitcmd('status', '--porcelain'): if line[0] == '?': untracked_column = 'U' + continue elif line[0] == '!': - pass - elif line[0] != ' ': + continue + + if line[0] != ' ': index_column = 'I' - elif line[1] != ' ': + + if line[1] != ' ': wt_column = 'D' + r = wt_column + index_column + untracked_column - return None if r == ' ' else r + return r def branch(self): for line in self._gitcmd('branch', '-l'): diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ff124adc..54ae73b4 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -24,7 +24,11 @@ def user(): Highlights the user with the ``superuser`` if the effective user ID is 0. ''' user = os.environ.get('USER') - euid = os.geteuid() + try: + euid = os.geteuid() + except AttributeError: + # os.geteuid is not available on windows + euid = 1 return [{ 'contents': user, 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], @@ -68,7 +72,7 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) cwd_split_len = len(cwd_split) - if cwd_split_len > dir_limit_depth + 1: + if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: del(cwd_split[0:-dir_limit_depth]) cwd_split.insert(0, u'⋯') cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] @@ -393,7 +397,7 @@ def cpu_load_percent(measure_interval=.5): @add_divider_highlight_group('background:divider') -def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_prefix=False): +def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=False): '''Return the network load. Uses the ``psutil`` module if available for multi-platform compatibility, @@ -406,8 +410,8 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref interval used to measure the network load (in seconds) :param str suffix: string appended to each load string - :param bool binary_prefix: - use binary prefix, e.g. MiB instead of MB + :param bool si_prefix: + use SI prefix, e.g. MB instead of MiB ''' import time from powerline.lib import humanize_bytes @@ -436,8 +440,8 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', binary_pref time.sleep(measure_interval) b2 = get_bytes() return u'⬇ {rx_diff} ⬆ {tx_diff}'.format( - rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, binary_prefix).rjust(8), - tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, binary_prefix).rjust(8), + rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8), + tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8), ) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index bdf8898b..c18e76aa 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -78,6 +78,11 @@ def bufnr(segment_info, **kwargs): return segment_info['bufnr'] +def bufname(segment_info, **kwargs): + '''Used for cache key, returns current buffer name''' + return segment_info['buffer'].name + + # TODO Remove cache when needed def window_cached(func): cache = {} @@ -130,7 +135,7 @@ def paste_indicator(segment_info, text='PASTE'): :param string text: text to display if paste mode is enabled ''' - return text if int(getbufvar(segment_info['bufnr'], '&paste')) else None + return text if int(vim.eval('&paste')) else None @requires_segment_info @@ -156,7 +161,7 @@ def file_directory(segment_info, shorten_home=False): file_directory = vim_funcs['fnamemodify'](name, ':~:.:h') if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] - return file_directory.decode('utf-8') + os.sep if file_directory else None + return file_directory + os.sep if file_directory else None @requires_segment_info @@ -178,18 +183,18 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): else: return None file_name = vim_funcs['fnamemodify'](name, ':~:.:t') - return file_name.decode('utf-8') + return file_name @requires_segment_info -@memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write) -def file_size(segment_info, suffix='B', binary_prefix=False): +@memoize(2, cache_key=bufname, cache_reg_func=purgebuf_on_shell_and_write) +def file_size(segment_info, suffix='B', si_prefix=False): '''Return file size. :param str suffix: string appended to the file size - :param bool binary_prefix: - use binary prefix, e.g. MiB instead of MB + :param bool si_prefix: + use SI prefix, e.g. MB instead of MiB :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' file_name = segment_info['buffer'].name @@ -197,7 +202,7 @@ def file_size(segment_info, suffix='B', binary_prefix=False): file_size = os.stat(file_name).st_size except: return None - return humanize_bytes(file_size, suffix, binary_prefix) + return humanize_bytes(file_size, suffix, si_prefix) @requires_segment_info @@ -241,9 +246,9 @@ def line_percent(segment_info, gradient=False): line_last = len(segment_info['buffer']) percentage = int(line_current * 100 // line_last) if not gradient: - return percentage + return str(percentage) return [{ - 'contents': percentage, + 'contents': str(percentage), 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': percentage, }] @@ -252,24 +257,24 @@ def line_percent(segment_info, gradient=False): @requires_segment_info def line_current(segment_info): '''Return the current cursor line.''' - return segment_info['window'].cursor[0] + return str(segment_info['window'].cursor[0]) @requires_segment_info def col_current(segment_info): '''Return the current cursor column. ''' - return segment_info['window'].cursor[1] + 1 + return str(segment_info['window'].cursor[1] + 1) @window_cached def virtcol_current(): '''Return current visual column with concealed characters ingored''' - return [{'contents': vim_funcs['virtcol']('.'), + return [{'contents': str(vim_funcs['virtcol']('.')), 'highlight_group': ['virtcol_current', 'col_current']}] -def modified_buffers(text=u'+', join_str=','): +def modified_buffers(text=u'+ ', join_str=u','): '''Return a comma-separated list of modified buffers. :param str text: @@ -278,9 +283,9 @@ def modified_buffers(text=u'+', join_str=','): string to use for joining the modified buffer list ''' buffer_len = vim_funcs['bufnr']('$') - buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if int(getbufvar(bufnr, '&modified'))] + buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if int(getbufvar(bufnr, '&modified') or 0)] if buffer_mod: - return u'{0} {1}'.format(text, join_str.join(buffer_mod)) + return text + join_str.join(buffer_mod) return None From 43d9639534aa2c0163f20017e1ee73deb2319992 Mon Sep 17 00:00:00 2001 From: Foo Date: Mon, 25 Feb 2013 00:45:40 +0400 Subject: [PATCH 0399/1472] Fix zsh/zpython issue --- docs/source/configuration.rst | 27 ++++++++++++++++++++++++++ powerline/bindings/zsh/__init__.py | 21 ++++++++++++++++---- powerline/colorscheme.py | 10 +++++++++- powerline/lib/__init__.py | 31 ++++++++++++++++++++++++++++++ powerline/renderer.py | 2 +- powerline/segment.py | 6 +----- scripts/powerline | 29 +++------------------------- 7 files changed, 89 insertions(+), 37 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 5bc8ba93..83a218e6 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -420,6 +420,33 @@ Powerline script has a number of options controlling powerline behavior. Here performed by powerline script itself, but ``-p ~/.powerline`` will likely be expanded by the shell to something like ``-p /home/user/.powerline``. +Zsh/zpython overrides +--------------------- + +Here overrides are controlled by similarly to the powerline script, but values +are taken from zsh variables. + +``POWERLINE_CONFIG`` + Overrides options from :file:`powerline/config.json`. Should be a zsh + associative array with keys equal to ``KEY.NESTED_KEY`` and values being + JSON strings. Pair ``KEY.KEY1 VALUE`` is equivalent to ``{"KEY": {"KEY1": + VALUE}}``. All pairs are then recursively merged into one dictionary and + this dictionary is recursively merged with the contents of the file. + +``POWERLINE_THEME_CONFIG`` + Overrides options from :file:`powerline/themes/shell/*.json`. Should be + a zsh associative array with keys equal to ``THEME_NAME.KEY.NESTED_KEY`` and + values being JSON strings. Is processed like the above ``POWERLINE_CONFIG``, + but only subdictionaries for ``THEME_NAME`` key are merged with theme + configuration when theme with given name is requested. + +``POWERLINE_CONFIG_PATH`` + Sets directory where configuration should be read from. If present, no + default locations are searched for configuration. No expansions are + performed by powerline script itself, but zsh usually performs them on its + own if you set variable without quotes: ``POWERLINE_CONFIG_PATH=~/example``. + Expansion depends on zsh configuration. + Ipython overrides ----------------- diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 13795fbe..75f7f309 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,11 +1,11 @@ import zsh -import json from powerline.shell import ShellPowerline +from powerline.lib import parsedotval def get_var_config(var): try: - return dict(((k, json.loads(v)) for k, v in zsh.getvalue(var).items())) + return [parsedotval(i) for i in zsh.getvalue(var).items()] except: return None @@ -24,11 +24,24 @@ class Args(object): @property def config(self): - return get_var_config('POWERLINE_CONFIG') + try: + return get_var_config('POWERLINE_CONFIG') + except IndexError: + return None @property def theme_option(self): - return get_var_config('POWERLINE_THEME_CONFIG') + try: + return get_var_config('POWERLINE_THEME_CONFIG') + except IndexError: + return None + + @property + def config_path(self): + try: + return zsh.getvalue('POWERLINE_CONFIG_PATH') + except IndexError: + return None class Prompt(object): diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 7d7e1b5e..e8877856 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -29,6 +29,14 @@ def pick_gradient_value(grad_list, gradient_level): return grad_list[int(round(gradient_level * (len(grad_list) - 1) / 100))] +def hl_iter(value): + if type(value) is list: + for v in value: + yield v + else: + yield value + + class Colorscheme(object): def __init__(self, colorscheme_config, colors_config): '''Initialize a colorscheme.''' @@ -64,7 +72,7 @@ class Colorscheme(object): def get_highlighting(self, groups, mode, gradient_level=None): trans = self.translations.get(mode, {}) - for group in groups: + for group in hl_iter(groups): if 'groups' in trans and group in trans['groups']: try: group_props = trans['groups'][group] diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 8cdf9d2d..6aa5a2db 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,4 +1,5 @@ from functools import wraps +import json from powerline.lib.memoize import memoize # NOQA from powerline.lib.humanize_bytes import humanize_bytes # NOQA @@ -34,3 +35,33 @@ def add_divider_highlight_group(highlight_group): return None return f return dec + + +def keyvaluesplit(s): + if '=' not in s: + raise TypeError('Option must look like option=json_value') + if s[0] == '_': + raise ValueError('Option names must not start with `_\'') + idx = s.index('=') + o = s[:idx] + val = json.loads(s[idx+1:]) + return (o, val) + + +def parsedotval(s): + if type(s) is tuple: + o, val = s + else: + o, val = keyvaluesplit(s) + + keys = o.split('.') + if len(keys) > 1: + r = (keys[0], {}) + rcur = r[1] + for key in keys[1:-1]: + rcur[key] = {} + rcur = rcur[key] + rcur[keys[-1]] = val + return r + else: + return (o, val) diff --git a/powerline/renderer.py b/powerline/renderer.py index e9238204..9fdb7015 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -30,7 +30,7 @@ class Renderer(object): def get_highlighting(self, segment, mode): segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) if segment['divider_highlight_group']: - segment['divider_highlight'] = self.colorscheme.get_highlighting([segment['divider_highlight_group']], mode) + segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode) else: segment['divider_highlight'] = None return segment diff --git a/powerline/segment.py b/powerline/segment.py index bb7eacca..59dc8bbf 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -45,10 +45,6 @@ segment_getters = { } -def scalar_to_list(value): - return value if type(value) is list else [value] - - def gen_segment_getter(ext, path, theme_configs, default_module=None): data = { 'default_module': default_module or 'powerline.segments.' + ext, @@ -69,7 +65,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): highlight_group = segment_type != 'function' and segment.get('highlight_group') or segment.get('name') return { 'type': segment_type, - 'highlight_group': scalar_to_list(highlight_group), + 'highlight_group': highlight_group, 'divider_highlight_group': None, 'before': get_key(segment, module, 'before', ''), 'after': get_key(segment, module, 'after', ''), diff --git a/scripts/powerline b/scripts/powerline index 2e75825f..f43b7bab 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -4,6 +4,7 @@ import argparse import sys import json +from powerline.lib import parsedotval try: from powerline.shell import ShellPowerline @@ -12,30 +13,6 @@ except ImportError: sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from powerline.shell import ShellPowerline # NOQA -def oval(s): - if '=' not in s: - raise TypeError('Option must look like option=json_value') - if s[0] == '_': - raise ValueError('Option names must not start with `_\'') - idx = s.index('=') - o = s[:idx] - val = json.loads(s[idx+1:]) - return (o, val) - -def odotval(s): - o, val = oval(s) - keys = o.split('.') - if len(keys) > 1: - r = (keys[0], {}) - rcur = r[1] - for key in keys[1:-1]: - rcur[key] = {} - rcur = rcur[key] - rcur[keys[-1]] = val - return r - else: - return (o, val) - parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('ext', nargs=1) parser.add_argument('side', nargs='?', choices=('left', 'right')) @@ -43,8 +20,8 @@ parser.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) parser.add_argument('-w', '--width', type=int) parser.add_argument('--last_exit_code', metavar='INT', type=int) parser.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) -parser.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', type=odotval, action='append') -parser.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', type=odotval, action='append') +parser.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', type=parsedotval, action='append') +parser.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', type=parsedotval, action='append') parser.add_argument('-p', '--config_path', metavar='PATH') if __name__ == '__main__': From af020d932951c6301623df729f1befef6ffbc3aa Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Sun, 24 Feb 2013 21:27:59 +0200 Subject: [PATCH 0400/1472] Make vim visual block selection orange Fixes #147 --- powerline/config_files/colorschemes/vim/default.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 42b69d6a..14a3e780 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -78,6 +78,11 @@ "mode": { "fg": "darkorange", "bg": "brightestorange", "attr": ["bold"] } } }, + "^V": { + "groups": { + "mode": { "fg": "darkorange", "bg": "brightestorange", "attr": ["bold"] } + } + }, "R": { "groups": { "mode": { "fg": "white", "bg": "brightred", "attr": ["bold"] } From 0dcd23f5bf2059392f3ebd00f6bcb6456f5e61f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 24 Feb 2013 23:15:12 +0100 Subject: [PATCH 0401/1472] Fix syntax error in default tmux theme Ref #150 --- powerline/config_files/themes/tmux/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index d4f12d43..eb5d7e6d 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -52,7 +52,7 @@ }, { "name": "email_imap_alert", - "priority": 10, + "priority": 10 }, { "name": "hostname" From 9a7b5f212b2df1b70db58da50438e7cec1da876d Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sun, 24 Feb 2013 23:07:24 -0800 Subject: [PATCH 0402/1472] Use oldlace for visual mode foreground in vim Fixes gh-261. --- powerline/config_files/colorschemes/vim/solarized.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 6d724b65..0a441994 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -65,17 +65,17 @@ }, "v": { "groups": { - "mode": { "fg": "beige", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } } }, "V": { "groups": { - "mode": { "fg": "beige", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } } }, "R": { "groups": { - "mode": { "fg": "beige", "bg": "red", "attr": ["bold"] } + "mode": { "fg": "oldlace", "bg": "red", "attr": ["bold"] } } } } From a4b7c31bc336627459d58dc35d25e86aac398831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 25 Feb 2013 16:11:10 +0100 Subject: [PATCH 0403/1472] Move divider hl group to return dicts in weather segment Fixes #264 --- powerline/segments/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 54ae73b4..cb1d0bb8 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -276,7 +276,6 @@ weather_conditions_icons = { @memoize(1800) -@add_divider_highlight_group('background:divider') def weather(unit='c', location_query=None, icons=None): '''Return weather from Yahoo! Weather. @@ -334,11 +333,13 @@ def weather(unit='c', location_query=None, icons=None): { 'contents': icon + ' ', 'highlight_group': groups, + 'divider_highlight_group': 'background:divider', }, { 'contents': u'{0}°{1}'.format(condition['temp'], unit.upper()), 'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], 'draw_divider': False, + 'divider_highlight_group': 'background:divider', }, ] From 83f4236a2c2dd4300e9592e6f33ad437ab0d5608 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Feb 2013 00:14:24 +0400 Subject: [PATCH 0404/1472] Replace unicode() with u() in theme.py Fixes #271 --- powerline/theme.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/powerline/theme.py b/powerline/theme.py index 03e11f48..4d8ffd61 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -6,11 +6,18 @@ from .segment import gen_segment_getter try: - unicode() # NOQA -except NameError: + from __builtin__ import unicode +except ImportError: unicode = str +def u(s): + if type(s) is unicode: + return s + else: + return unicode(s, 'utf-8') + + def requires_segment_info(func): func.requires_powerline_segment_info = True return func @@ -82,7 +89,7 @@ class Theme(object): else: continue for segment in parsed_segments: - segment['contents'] = segment['before'] + unicode(segment['contents'] if segment['contents'] is not None else '') + segment['after'] + segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents if segment['width'] and segment['width'] != 'auto': if segment['align'] == 'l': From a8ccfd4350b969215d6503abf26767aee8a3b284 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Feb 2013 07:35:08 +0400 Subject: [PATCH 0405/1472] Add ambiwidth problems information to troubleshooting section --- docs/source/installation/osx.rst | 7 +++++++ docs/source/installation/troubleshooting-common.rst | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index ad45548a..3849e48d 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -86,6 +86,13 @@ The colors look weird in iTerm2! contrast" slider in the color tab of your OS X settings. * Please disable background transparency to resolve this issue. +Statusline is getting wrapped to the next line in iTerm2 +-------------------------------------------------------- + +* Turn off “Treat ambigious-width characters as double width” in `Preferences + --> Text`. +* Alternative: remove fancy dividers and other fancy symbols from configuration. + I receive a ``NameError`` when trying to use Powerline with MacVim! ------------------------------------------------------------------- diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 80c95a3c..ef3da240 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -44,3 +44,9 @@ I get E858/E860 error in vim (Eval did not return a valid python object) and then use the stack trace to search for existing issues or to create a new one. + +My vim statusline is not displayed completely and has too much spaces +--------------------------------------------------------------------- + +* Be sure you have ``ambiwidth`` option set to ``single``. +* Alternative: remove fancy dividers and other fancy symbols from configuration. From c2cf679c6f7195ad1845acb39c04bfe092a94f66 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Feb 2013 00:20:48 +0400 Subject: [PATCH 0406/1472] Do not throw away python stack trace Errors when loading powerline fails are almost never giving any useful information for debugging. --- powerline/bindings/vim/plugin/powerline.vim | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 947db8b4..9ce6b484 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -10,7 +10,7 @@ function! s:CriticalError(message) endfunction if ! has('python') && ! has('python3') - call s:CriticalError('You need vim compiled with Python 2.7 or 3.3+ support + call s:CriticalError('You need vim compiled with Python 2.7 or 3.2+ support \ for Powerline to work. Please consult the documentation for more details.') finish endif @@ -28,12 +28,15 @@ catch exec s:powerline_pycmd 'sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' try exec s:powerline_pycmd s:import_cmd - catch - call s:CriticalError('An error occured while importing the Powerline package. - \ This could be caused by an invalid sys.path setting, or by an incompatible - \ Python version (Powerline requires Python 2.7 or 3.3+ to work). Please consult - \ the troubleshooting section in the documentation for possible solutions.') - finish + let s:launched = 1 + finally + if !exists('s:launched') + call s:CriticalError('An error occured while importing the Powerline package. + \ This could be caused by an invalid sys.path setting, or by an incompatible + \ Python version (Powerline requires Python 2.7 or 3.2+ to work). Please consult + \ the troubleshooting section in the documentation for possible solutions.') + finish + endif endtry endtry exec s:powerline_pycmd 'powerline = VimPowerline()' From f3462768deef7cb031af30b3781be514ff53e985 Mon Sep 17 00:00:00 2001 From: ZyX-I Date: Wed, 27 Feb 2013 00:26:38 +0400 Subject: [PATCH 0407/1472] Fix minimal python versions s/3\.2/3.3 --- powerline/bindings/vim/plugin/powerline.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 9ce6b484..43a2f0f2 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -10,7 +10,7 @@ function! s:CriticalError(message) endfunction if ! has('python') && ! has('python3') - call s:CriticalError('You need vim compiled with Python 2.7 or 3.2+ support + call s:CriticalError('You need vim compiled with Python 2.7 or 3.3+ support \ for Powerline to work. Please consult the documentation for more details.') finish endif @@ -33,7 +33,7 @@ catch if !exists('s:launched') call s:CriticalError('An error occured while importing the Powerline package. \ This could be caused by an invalid sys.path setting, or by an incompatible - \ Python version (Powerline requires Python 2.7 or 3.2+ to work). Please consult + \ Python version (Powerline requires Python 2.7 or 3.3+ to work). Please consult \ the troubleshooting section in the documentation for possible solutions.') finish endif From 16a5b7897a76c9a53e60d78baf7fb94fcc59b220 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Feb 2013 01:06:11 +0400 Subject: [PATCH 0408/1472] Use exit_fail hl group for last_status segment Fixes #270 --- powerline/segments/shell.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 6ac1200c..2473bb8f 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -6,7 +6,9 @@ from powerline.theme import requires_segment_info @requires_segment_info def last_status(segment_info): '''Return last exit code.''' - return str(segment_info.last_exit_code) if segment_info.last_exit_code else None + if not segment_info.last_exit_code: + return None + return [{'contents': str(segment_info.last_exit_code), 'highlight_group': 'exit_fail'}] @requires_segment_info From f495c49aa1ba0032e270e360bb4b7e4709331a1b Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Feb 2013 01:18:16 +0400 Subject: [PATCH 0409/1472] Remove obsolete highlight_group keys from configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are no no-op for function segments, so don’t confuse users --- powerline/config_files/themes/shell/default.json | 3 +-- powerline/config_files/themes/shell/default_leftonly.json | 1 - powerline/config_files/themes/vim/default.json | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index fe34dd89..6246a964 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -35,8 +35,7 @@ "right": [ { "module": "powerline.segments.shell", - "name": "last_pipe_status", - "highlight_group": "exit_fail" + "name": "last_pipe_status" }, { "name": "branch" diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 85570107..16af9755 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -36,7 +36,6 @@ }, { "name": "last_status", - "highlight_group": ["exit_fail"], "module": "powerline.segments.shell" } ] diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 3d64018c..3eeae293 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -100,7 +100,6 @@ "priority": 30, "before": ":", "width": 3, - "highlight_group": ["col_current"], "align": "l" } ] From 2f17510db89667ad31679e22ae14db75f671f8db Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Feb 2013 00:58:45 +0400 Subject: [PATCH 0410/1472] Also use hl_iter in exception message Ref #270 --- powerline/colorscheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index e8877856..42fbb60d 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -98,7 +98,7 @@ class Colorscheme(object): break else: - raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups)) + raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(hl_iter(groups))) if gradient_level is None: pick_color = self.colors.__getitem__ From 307dfd721263d91f04e4c099c146bb851b638b0a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 00:34:55 +0400 Subject: [PATCH 0411/1472] Improve documentation --- docs/source/configuration.rst | 21 ++++++++--- docs/source/installation/linux.rst | 2 +- docs/source/overview.rst | 4 +-- powerline/__init__.py | 57 ++++++++++++++++++++++++++++++ powerline/vim.py | 19 +++++++--- 5 files changed, 91 insertions(+), 12 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 83a218e6..55cb28ad 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -96,6 +96,8 @@ segments that you may want to customize right away: } }, +.. _config-main: + Main configuration ================== @@ -111,7 +113,7 @@ Common configuration Common configuration is a subdictionary that is a value of ``common`` key in :file:`powerline/config.json` file. -.. _config-term_24bit_colors: +.. _config-common-term_24bit_colors: ``term_24bit_colors`` Defines whether to output cterm indices (8-bit) or RGB colors (24-bit) @@ -152,7 +154,14 @@ Common configuration is a subdictionary that is a value of ``ext`` key in .. _config-ext-local_themes: Defines themes used when certain conditions are met, e.g. for - buffer-specific statuslines in vim. Requires a custom matcher and theme. + buffer-specific statuslines in vim. Value depends on extension used. For vim + it is a dictionary ``{matcher_name : theme_name}``, where ``matcher_name`` + is either ``matcher_module.module_attribute`` or ``module_attribute`` + (``matcher_module`` defaults to ``powerline.matchers.vim``) and + ``module_attribute`` should point to a function that returns boolean value + indicating that current buffer has (not) matched conditions. + +.. _config-colors: Color definitions ================= @@ -178,6 +187,8 @@ Color definitions * A list of cterm color indicies. * A list of hex color strings. +.. _config-colorschemes: + Colorschemes ============ @@ -186,7 +197,7 @@ Colorschemes ``name`` Name of the colorscheme. -.. _config-colorscheme-groups: +.. _config-colorschemes-groups: ``groups`` Segment highlighting groups, consisting of a dict where the key is the @@ -220,7 +231,9 @@ Colorschemes ``groups`` Segment highlighting groups for this mode. Same syntax as the main - :ref:`groups ` option. + :ref:`groups ` option. + +.. _config-themes: Themes ====== diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index ad099318..cffd2a7c 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -21,7 +21,7 @@ follow the installation guide below: Plugin installation =================== -1. Install Python 3.3+ or Python 2.7. +1. Install Python 3.2+ or Python 2.7. 2. Install Powerline using the following command:: pip install --user git+git://github.com/Lokaltog/powerline diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 18d520cd..a550f32e 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -31,8 +31,8 @@ custom symbols for developers. This requires that you either have a symbol font or a patched font on your system. Your terminal emulator must also support either patched fonts or fontconfig for Powerline to work properly. -You can also enable :ref:`24-bit color support ` -if your terminal emulator supports it. +You can also enable :ref:`24-bit color support +` if your terminal emulator supports it. .. table:: Application/terminal emulator feature support matrix :name: term-feature-support-matrix diff --git a/powerline/__init__.py b/powerline/__init__.py index 5296cde9..434310e5 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -20,6 +20,19 @@ def load_json_config(search_paths, config_file): class Powerline(object): + '''Main powerline class, entrance point for all powerline uses. Sets + powerline up and loads the configuration. + + :param str ext: + extension used. Determines where configuration files will + searched and what renderer module will be used. Affected: used ``ext`` + dictionary from :file:`powerline/config.json`, location of themes and + colorschemes, render module (``powerline.renders.{ext}``). + :param str renderer_module: + Overrides renderer module (defaults to ``ext``). Should be the name of + the package imported like this: ``powerline.renders.{render_module}``. + ''' + def __init__(self, ext, renderer_module=None): self.config_paths = self.get_config_paths() @@ -58,27 +71,71 @@ class Powerline(object): self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) def get_config_paths(self): + '''Get configuration paths. + + :return: list of paths + ''' config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) config_path = os.path.join(config_home, 'powerline') plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') return [config_path, plugin_path] def load_theme_config(self, name): + '''Get theme configuration. + + :param str name: + Name of the theme to load. + + :return: dictionary with :ref:`theme configuration ` + ''' return load_json_config(self.config_paths, os.path.join('themes', self.ext, name)) def load_main_config(self): + '''Get top-level configuration. + + :return: dictionary with :ref:`top-level configuration `. + ''' return load_json_config(self.config_paths, 'config') def load_colorscheme_config(self, name): + '''Get colorscheme. + + :param str name: + Name of the colorscheme to load. + + :return: dictionary with :ref:`colorscheme configuration `. + ''' return load_json_config(self.config_paths, os.path.join('colorschemes', self.ext, name)) def load_colors_config(self): + '''Get colorscheme. + + :return: dictionary with :ref:`colors configuration `. + ''' return load_json_config(self.config_paths, 'colors') @staticmethod def get_local_themes(local_themes): + '''Get local themes. No-op here, to be overridden in subclasses if + required. + + :param dict local_themes: + Usually accepts ``{matcher_name : theme_name}``. + + :return: + anything accepted by ``self.renderer.get_theme`` and processable by + ``self.renderer.add_local_theme``. Renderer module is determined by + ``__init__`` arguments, refer to its documentation. + ''' return {} @staticmethod def get_segment_info(): + '''Get information for segments that require ``segment_info`` argument. + To be overridden in subclasses. + + :return: + anything accepted by segments as ``segment_info`` argument. Depends + on the segments used, refer to Powerline subclass documentation. + ''' return None diff --git a/powerline/vim.py b/powerline/vim.py index 968aa510..2e234d97 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -30,12 +30,21 @@ class VimPowerline(Powerline): def add_local_theme(self, key, config): '''Add local themes at runtime (during vim session). - Accepts key as first argument (same as keys in config.json: - ext/*/local_themes) and configuration dictionary as the second (has - format identical to themes/*/*.json) + :param str key: + Matcher name (in format ``{matcher_module}.{module_attribute}`` or + ``{module_attribute}`` if ``{matcher_module}`` is + ``powerline.matchers.vim``). Function pointed by + ``{module_attribute}`` should be hashable and accept a dictionary + with information about current buffer and return boolean value + indicating whether current window matched conditions. See also + :ref:`local_themes key description `. - Returns True if theme was added successfully and False if theme with - the same matcher already exists + :param dict config: + :ref:`Theme ` dictionary. + + :return: + ``True`` if theme was added successfully and ``False`` if theme with + the same matcher already exists. ''' key = self.get_matcher(key) try: From de33eb8140ad6c3b8b72990b3552aeda230c0591 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Mar 2013 19:31:42 +0400 Subject: [PATCH 0412/1472] Add _external_ip for use in weather segment --- powerline/segments/common.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index cb1d0bb8..52a0ccd1 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -157,7 +157,10 @@ def fuzzy_time(): @memoize(600) -@add_divider_highlight_group('background:divider') +def _external_ip(query_url='http://ipv4.icanhazip.com/'): + return urllib_read(query_url).strip() + + def external_ip(query_url='http://ipv4.icanhazip.com/'): '''Return external IP address. @@ -170,7 +173,7 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): :param str query_url: URI to query for IP address, should return only the IP address as a text string ''' - return urllib_read(query_url).strip() + return [{'contents': _external_ip(query_url=query_url), 'divider_highlight_group': 'background:divider'}] @add_divider_highlight_group('background:divider') @@ -297,7 +300,7 @@ def weather(unit='c', location_query=None, icons=None): if not location_query: try: - location = json.loads(urllib_read('http://freegeoip.net/json/' + external_ip())) + location = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip())) location_query = ','.join([location['city'], location['region_name'], location['country_name']]) except (TypeError, ValueError): return None From 9fd63021f0d075e072bca5772708eb4304abbd39 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 00:34:23 +0400 Subject: [PATCH 0413/1472] Add support for python-3.2 --- powerline/renderer.py | 20 +++++++++------ powerline/segment.py | 4 +-- powerline/segments/common.py | 48 ++++++++++++++++++------------------ powerline/segments/vim.py | 42 +++++++++++++++---------------- powerline/theme.py | 2 +- 5 files changed, 61 insertions(+), 55 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 9fdb7015..6dcf2976 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -3,6 +3,12 @@ from powerline.theme import Theme +try: + NBSP = unicode(' ', 'utf-8') +except NameError: + NBSP = ' ' + + class Renderer(object): def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, **options): self.__dict__.update(options) @@ -58,7 +64,7 @@ class Renderer(object): if not width: # No width specified, so we don't need to crop or pad anything - return self._returned_value(u''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) + return self._returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] @@ -81,7 +87,7 @@ class Renderer(object): segment['_space_right'] += space_side segments_spacers[0]['_space_right'] += distribute_len_remainder - rendered_highlighted = u''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() + rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() return self._returned_value(rendered_highlighted, segments, output_raw) @@ -99,8 +105,8 @@ class Renderer(object): segments_len = len(segments) for index, segment in enumerate(segments): - segment['_rendered_raw'] = u'' - segment['_rendered_hl'] = u'' + segment['_rendered_raw'] = '' + segment['_rendered_hl'] = '' prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT @@ -127,8 +133,8 @@ class Renderer(object): contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding # Replace spaces with no-break spaces - contents_raw = contents_raw.replace(' ', u'\u00a0') - divider_raw = divider_raw.replace(' ', u'\u00a0') + contents_raw = contents_raw.replace(' ', NBSP) + divider_raw = divider_raw.replace(' ', NBSP) # Apply highlighting to padded dividers and contents if render_highlighted: @@ -182,4 +188,4 @@ class Renderer(object): raise NotImplementedError def hl(self, contents, fg=None, bg=None, attr=None): - return self.hlstyle(fg, bg, attr) + (contents or u'') + return self.hlstyle(fg, bg, attr) + (contents or '') diff --git a/powerline/segment.py b/powerline/segment.py index 59dc8bbf..9cbca25e 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -79,8 +79,8 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): 'include_modes': segment.get('include_modes', []), 'width': segment.get('width'), 'align': segment.get('align', 'l'), - '_rendered_raw': u'', - '_rendered_hl': u'', + '_rendered_raw': '', + '_rendered_hl': '', '_len': 0, '_space_left': 0, '_space_right': 0, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 52a0ccd1..9ab1a523 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -74,7 +74,7 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): cwd_split_len = len(cwd_split) if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: del(cwd_split[0:-dir_limit_depth]) - cwd_split.insert(0, u'⋯') + cwd_split.insert(0, '⋯') cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] ret = [] if not cwd[0]: @@ -263,18 +263,18 @@ weather_conditions_codes = ( # ('sunny', (32, 36)), # ('night', (31, 33))): weather_conditions_icons = { - 'day': u'〇', - 'blustery': u'⚑', - 'rainy': u'☔', - 'cloudy': u'☁', - 'snowy': u'❅', - 'stormy': u'☈', - 'foggy': u'〰', - 'sunny': u'☼', - 'night': u'☾', - 'windy': u'☴', - 'not_available': u'�', - 'unknown': u'⚠', + 'day': '〇', + 'blustery': '⚑', + 'rainy': '☔', + 'cloudy': '☁', + 'snowy': '❅', + 'stormy': '☈', + 'foggy': '〰', + 'sunny': '☼', + 'night': '☾', + 'windy': '☴', + 'not_available': '�', + 'unknown': '⚠', } @@ -306,8 +306,8 @@ def weather(unit='c', location_query=None, icons=None): return None query_data = { 'q': - u'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' - u'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit).encode('utf-8'), + 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' + 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit).encode('utf-8'), 'format': 'json' } try: @@ -339,7 +339,7 @@ def weather(unit='c', location_query=None, icons=None): 'divider_highlight_group': 'background:divider', }, { - 'contents': u'{0}°{1}'.format(condition['temp'], unit.upper()), + 'contents': '{0}°{1}'.format(condition['temp'], unit.upper()), 'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', @@ -397,7 +397,7 @@ def cpu_load_percent(measure_interval=.5): except ImportError: return None cpu_percent = int(psutil.cpu_percent(interval=measure_interval)) - return u'{0}%'.format(cpu_percent) + return '{0}%'.format(cpu_percent) @add_divider_highlight_group('background:divider') @@ -443,7 +443,7 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F return None time.sleep(measure_interval) b2 = get_bytes() - return u'⬇ {rx_diff} ⬆ {tx_diff}'.format( + return '⬇ {rx_diff} ⬆ {tx_diff}'.format( rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8), tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8), ) @@ -492,13 +492,13 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold class NowPlayingSegment(object): STATE_SYMBOLS = { - 'fallback': u'♫', - 'play': u'▶', - 'pause': u'▮▮', - 'stop': u'■', + 'fallback': '♫', + 'play': '▶', + 'pause': '▮▮', + 'stop': '■', } - def __call__(self, player='mpd', format=u'{state_symbol} {artist} - {title} ({total})', *args, **kwargs): + def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', *args, **kwargs): player_func = getattr(self, 'player_{0}'.format(player)) stats = { 'state': None, @@ -538,7 +538,7 @@ class NowPlayingSegment(object): @staticmethod def _convert_seconds(seconds): - return u'{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) + return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) def player_cmus(self): '''Return cmus player information. diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c18e76aa..47cbddc3 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -22,24 +22,24 @@ vim_funcs = { } vim_modes = { - 'n': u'NORMAL', - 'no': u'N·OPER', - 'v': u'VISUAL', - 'V': u'V·LINE', - '^V': u'V·BLCK', - 's': u'SELECT', - 'S': u'S·LINE', - '^S': 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', + 'n': 'NORMAL', + 'no': 'N·OPER', + 'v': 'VISUAL', + 'V': 'V·LINE', + '^V': 'V·BLCK', + 's': 'SELECT', + 'S': 'S·LINE', + '^S': 'S·BLCK', + 'i': 'INSERT', + 'R': 'REPLACE', + 'Rv': 'V·RPLCE', + 'c': 'COMMND', + 'cv': 'VIM EX', + 'ce': 'EX', + 'r': 'PROMPT', + 'rm': 'MORE', + 'r?': 'CONFIRM', + '!': 'SHELL', } @@ -119,7 +119,7 @@ def mode(segment_info, override=None): @requires_segment_info -def modified_indicator(segment_info, text=u'+'): +def modified_indicator(segment_info, text='+'): '''Return a file modified indicator. :param string text: @@ -139,7 +139,7 @@ def paste_indicator(segment_info, text='PASTE'): @requires_segment_info -def readonly_indicator(segment_info, text=u''): +def readonly_indicator(segment_info, text=''): '''Return a read-only indicator. :param string text: @@ -274,7 +274,7 @@ def virtcol_current(): 'highlight_group': ['virtcol_current', 'col_current']}] -def modified_buffers(text=u'+ ', join_str=u','): +def modified_buffers(text='+ ', join_str=','): '''Return a comma-separated list of modified buffers. :param str text: diff --git a/powerline/theme.py b/powerline/theme.py index 4d8ffd61..014c5eef 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -8,7 +8,7 @@ from .segment import gen_segment_getter try: from __builtin__ import unicode except ImportError: - unicode = str + unicode = str # NOQA def u(s): From 47cfde0ecc2ab59dfa220a2137e4cdcc776df57a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 15:47:54 +0400 Subject: [PATCH 0414/1472] Add support for python-2.6 --- docs/source/installation/linux.rst | 2 +- docs/source/installation/osx.rst | 5 ++--- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 2 +- powerline/__init__.py | 4 ++-- powerline/bindings/vim/plugin/powerline.vim | 4 ++-- powerline/lib/vcs/__init__.py | 4 ++-- powerline/matcher.py | 4 ++-- powerline/segment.py | 4 ++-- powerline/segments/common.py | 6 +++--- powerline/segments/vim.py | 2 ++ 11 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index cffd2a7c..fd25d649 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -21,7 +21,7 @@ follow the installation guide below: Plugin installation =================== -1. Install Python 3.2+ or Python 2.7. +1. Install Python 3.2+ or Python 2.6+. 2. Install Powerline using the following command:: pip install --user git+git://github.com/Lokaltog/powerline diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index 3849e48d..b76bcf19 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -29,9 +29,8 @@ Python package Vim installation ---------------- -Any terminal vim version with Python 3.3 or Python 2.7 support should work, -but if you're using MacVim you need to install it using the following -command:: +Any terminal vim version with Python 3.2+ or Python 2.6+ support should work, +but if you're using MacVim you need to install it using the following command:: brew install macvim --env-std --override-system-vim diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index 107024e6..81dadd26 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 3428 SHA256 56a885903451b133dfd3ff3567613bd04d63242a2a3f6aace3d22eded39f2dcd SHA512 3861c0bd9170ea4d6c28d3a57a63f15ee6c710c4636c41ae4272259a6ddfd0f4a865f851d176115edf95eb335a14cb01fd6c8b8e9bb6b8b393b1025461e53c5c WHIRLPOOL 853fd571b9436ce19a08c4a0bd35c35f6cdb66eeacb4649d1eb235cef2fa47025499cd2d08558913ef2fec09cb21dc62d6c6b2b8f3a252aa8628cd0c181b1523 +EBUILD powerline-9999.ebuild 3436 SHA256 9a09866972c18adb5bc3b9394ae8ec98e305aef27e7558803dd89f499829fa48 SHA512 620363137d24ca57457d97fabb95108e515aec2ce986d7dc6abb4b5c68ef2866e55a4024005efc91f7f84fcac58b79c9445af93e3bfff3d1c12aba462c8e8a61 WHIRLPOOL 227ab590e91c97fbe9b720c882d422ee7c05d0637c174cde48f4cd1f683653f14e67914abdb9ba03406d5090d6a56a96e546c501fb1b10961ddaa4005c04b686 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index c5c42cfa..6a7e05aa 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -3,7 +3,7 @@ # $Header: /var/cvsroot/gentoo-x86/dev-python/setuptools/setuptools-9999.ebuild,v 1.1 2013/01/11 09:59:31 mgorny Exp $ EAPI="5" -PYTHON_COMPAT=( python{2_7,3_3} ) +PYTHON_COMPAT=( python{2_6,2_7,3_2,3_3} ) #if LIVE EGIT_REPO_URI="https://github.com/Lokaltog/${PN}" diff --git a/powerline/__init__.py b/powerline/__init__.py index 434310e5..5736632b 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import importlib +from __future__ import absolute_import import json import os import sys @@ -63,7 +63,7 @@ class Powerline(object): renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) try: - Renderer = getattr(importlib.import_module(renderer_module_import), renderer_class_name) + Renderer = getattr(__import__(renderer_module_import, fromlist=[renderer_class_name]), renderer_class_name) except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 43a2f0f2..34710e8c 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -10,7 +10,7 @@ function! s:CriticalError(message) endfunction if ! has('python') && ! has('python3') - call s:CriticalError('You need vim compiled with Python 2.7 or 3.3+ support + call s:CriticalError('You need vim compiled with Python 2.6+ or 3.2+ support \ for Powerline to work. Please consult the documentation for more details.') finish endif @@ -33,7 +33,7 @@ catch if !exists('s:launched') call s:CriticalError('An error occured while importing the Powerline package. \ This could be caused by an invalid sys.path setting, or by an incompatible - \ Python version (Powerline requires Python 2.7 or 3.3+ to work). Please consult + \ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult \ the troubleshooting section in the documentation for possible solutions.') finish endif diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 7bf396b9..8e583da4 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,4 +1,4 @@ -import importlib +from __future__ import absolute_import import os from powerline.lib.memoize import memoize @@ -24,7 +24,7 @@ def guess(path): if check(os.path.join(directory, vcs_dir)): try: if vcs not in globals(): - globals()[vcs] = importlib.import_module('powerline.lib.vcs.' + vcs) + globals()[vcs] = getattr(__import__('powerline.lib.vcs', fromlist=[vcs]), vcs) return globals()[vcs].Repository(directory) except: pass diff --git a/powerline/matcher.py b/powerline/matcher.py index 31a0969c..aa70d0e1 100644 --- a/powerline/matcher.py +++ b/powerline/matcher.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from importlib import import_module +from __future__ import absolute_import import sys @@ -13,7 +13,7 @@ def gen_matcher_getter(ext, import_paths): oldpath = sys.path sys.path = import_paths + sys.path try: - return getattr(import_module(match_module), match_function) + return getattr(__import__(match_module, fromlist=[match_function]), match_function) finally: sys.path = oldpath return get diff --git a/powerline/segment.py b/powerline/segment.py index 9cbca25e..e93e0383 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from importlib import import_module +from __future__ import absolute_import import sys @@ -25,7 +25,7 @@ def get_function(data, segment): sys.path = data['path'] + sys.path segment_module = str(segment.get('module', data['default_module'])) try: - return None, getattr(import_module(segment_module), segment['name']), segment_module + return None, getattr(__import__(segment_module, fromlist=[segment['name']]), segment['name']), segment_module finally: sys.path = oldpath diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 9ab1a523..3692e5de 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -564,9 +564,9 @@ class NowPlayingSegment(object): if not now_playing_str: return ignore_levels = ('tag', 'set',) - now_playing = {token[0] if token[0] not in ignore_levels else token[1]: - ' '.join(token[1:]) if token[0] not in ignore_levels else - ' '.join(token[2:]) for token in [line.split(' ') for line in now_playing_str.split('\n')[:-1]]} + now_playing = dict(((token[0] if token[0] not in ignore_levels else token[1], + (' '.join(token[1:]) if token[0] not in ignore_levels else + ' '.join(token[2:]))) for token in [line.split(' ') for line in now_playing_str.split('\n')[:-1]])) state = self._convert_state(now_playing.get('status')) return { 'state': state, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 47cbddc3..f3929f1e 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -198,6 +198,8 @@ def file_size(segment_info, suffix='B', si_prefix=False): :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' file_name = segment_info['buffer'].name + if not file_name: + return None try: file_size = os.stat(file_name).st_size except: From 2a39ed83d87f11b8bb4652dd48abbdbf0df94d06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Feb 2013 07:55:06 +0400 Subject: [PATCH 0415/1472] Use mode(1) Otherwise all that fancy double-symbol modes are just not visible. --- powerline/renderers/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 6d31fa92..fdc2a1ee 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -32,7 +32,7 @@ class VimRenderer(Renderer): used in non-current windows. ''' if current: - mode = vim_mode() + mode = vim_mode(1) mode = mode_translations.get(mode, mode) else: mode = 'nc' From 584755765bef0cedd791fbf8daba9837c4c8cb79 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Feb 2013 14:03:14 +0400 Subject: [PATCH 0416/1472] Start adding tests Tests are temporary disabled on all branches except tests (does not really work though, needs travis.yml in all branches). --- .travis.yml | 12 ++ powerline/lib/__init__.py | 2 +- setup.py | 4 +- tests/__init__.py | 0 tests/empty | 0 tests/test_configuration.py | 74 +++++++ tests/test_lib.py | 106 ++++++++++ tests/test_segments.py | 381 ++++++++++++++++++++++++++++++++++++ tests/vim.py | 367 ++++++++++++++++++++++++++++++++++ 9 files changed, 943 insertions(+), 3 deletions(-) create mode 100644 .travis.yml create mode 100644 tests/__init__.py create mode 100644 tests/empty create mode 100644 tests/test_configuration.py create mode 100644 tests/test_lib.py create mode 100644 tests/test_segments.py create mode 100644 tests/vim.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..3e469f10 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: python +python: + - "2.7" + - "3.2" + - "3.3" +install: "pip install ." +script: python setup.py test +branches: + only: + - tests + +# vim: et diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 6aa5a2db..54b9e50d 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -44,7 +44,7 @@ def keyvaluesplit(s): raise ValueError('Option names must not start with `_\'') idx = s.index('=') o = s[:idx] - val = json.loads(s[idx+1:]) + val = json.loads(s[idx + 1:]) return (o, val) diff --git a/setup.py b/setup.py index 5600c5c2..b7a09c99 100755 --- a/setup.py +++ b/setup.py @@ -24,14 +24,14 @@ setup( 'scripts/powerline', ], keywords='', - packages=find_packages(), + packages=find_packages(exclude=('tests',)), include_package_data=True, zip_safe=False, - test_suite='powerline', install_requires=[], extras_require={ 'docs': [ 'Sphinx', ], }, + test_suite='tests', ) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/empty b/tests/empty new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_configuration.py b/tests/test_configuration.py new file mode 100644 index 00000000..c0d80592 --- /dev/null +++ b/tests/test_configuration.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +'''Dynamic configuration files tests.''' + + +from unittest import TestCase +import tests.vim as vim_module +import sys +import os +import json + + +VBLOCK = chr(ord('V') - 0x40) +SBLOCK = chr(ord('S') - 0x40) + + +class TestConfig(TestCase): + def test_vim(self): + from powerline.vim import VimPowerline + cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') + vim_module._g['powerline_config_path'] = cfg_path + buffers = ((lambda: vim_module._set_bufoption('buftype', 'help'), lambda: vim_module._set_bufoption('buftype', '')), + (lambda: vim_module._edit('[Command Line]'), lambda: vim_module._bw())) + with open(os.path.join(cfg_path, 'config.json'), 'r') as f: + self.assertEqual(len(buffers), len(json.load(f)['ext']['vim']['local_themes'])) + outputs = {} + i = 0 + mode = None + powerline = VimPowerline() + + def check_output(*args): + out = powerline.renderer.render(*args + (0 if mode == 'nc' else 1,)) + if out in outputs: + self.fail('Duplicate in set #{0} for mode {1!r} (previously defined in set #{2} for mode {3!r})'.format(i, mode, *outputs[out])) + outputs[out] = (i, mode) + + try: + exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) + try: + for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: + if mode != 'nc': + vim_module._start_mode(mode) + check_output(1, 0) + for setup, teardown in buffers: + i += 1 + if mode in exclude: + continue + setup() + try: + check_output(1, 0) + finally: + teardown() + finally: + vim_module._start_mode('n') + finally: + vim_module._g.pop('powerline_config_path') + + +old_cwd = None + + +def setUpModule(): + global old_cwd + old_cwd = os.getcwd() + sys.modules['vim'] = vim_module._get_module() + from powerline.segments import vim + globals()['vim'] = vim + + +def tearDownModule(): + global old_cwd + sys.modules.pop('vim') + os.chdir(old_cwd) + old_cwd = None diff --git a/tests/test_lib.py b/tests/test_lib.py new file mode 100644 index 00000000..0d4e667a --- /dev/null +++ b/tests/test_lib.py @@ -0,0 +1,106 @@ +from powerline.lib import mergedicts, underscore_to_camelcase, add_divider_highlight_group, humanize_bytes +from powerline.lib.vcs import guess +from unittest import TestCase +from subprocess import call, PIPE +import os +import sys + + +class TestLib(TestCase): + def test_underscore_to_camelcase(self): + self.assertEqual(underscore_to_camelcase('abc_def_ghi'), 'AbcDefGhi') + self.assertEqual(underscore_to_camelcase('abc_def__ghi'), 'AbcDef_Ghi') + + def test_mergedicts(self): + d = {} + mergedicts(d, {'abc': {'def': 'ghi'}}) + self.assertEqual(d, {'abc': {'def': 'ghi'}}) + mergedicts(d, {'abc': {'def': {'ghi': 'jkl'}}}) + self.assertEqual(d, {'abc': {'def': {'ghi': 'jkl'}}}) + mergedicts(d, {}) + self.assertEqual(d, {'abc': {'def': {'ghi': 'jkl'}}}) + mergedicts(d, {'abc': {'mno': 'pqr'}}) + self.assertEqual(d, {'abc': {'def': {'ghi': 'jkl'}, 'mno': 'pqr'}}) + + def test_add_divider_highlight_group(self): + def decorated_function_name(**kwargs): + return str(kwargs) + func = add_divider_highlight_group('hl_group')(decorated_function_name) + self.assertEqual(func.__name__, 'decorated_function_name') + self.assertEqual(func(kw={}), [{'contents': repr({'kw': {}}), 'divider_highlight_group': 'hl_group'}]) + + def test_humanize_bytes(self): + self.assertEqual(humanize_bytes(0), '0 B') + self.assertEqual(humanize_bytes(1), '1 B') + self.assertEqual(humanize_bytes(1, suffix='bit'), '1 bit') + self.assertEqual(humanize_bytes(1000, si_prefix=True), '1 kB') + self.assertEqual(humanize_bytes(1024, si_prefix=True), '1 kB') + self.assertEqual(humanize_bytes(1000000000, si_prefix=True), '1.00 GB') + self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') + + +use_mercurial = sys.version_info < (3, 0) + + +class TestVCS(TestCase): + def test_git(self): + repo = guess(path='git_repo') + self.assertNotEqual(repo, None) + self.assertEqual(repo.branch(), 'master') + self.assertEqual(repo.status(), ' ') + self.assertEqual(repo.status('file'), None) + with open(os.path.join('git_repo', 'file'), 'w') as f: + f.write('abc') + f.flush() + self.assertEqual(repo.status(), ' U') + self.assertEqual(repo.status('file'), '??') + call(['git', 'add', '.'], cwd='git_repo') + self.assertEqual(repo.status(), ' I ') + self.assertEqual(repo.status('file'), 'A ') + f.write('def') + f.flush() + self.assertEqual(repo.status(), 'DI ') + self.assertEqual(repo.status('file'), 'AM') + os.remove(os.path.join('git_repo', 'file')) + + if use_mercurial: + def test_mercurial(self): + repo = guess(path='hg_repo') + self.assertNotEqual(repo, None) + self.assertEqual(repo.branch(), 'default') + with open(os.path.join('hg_repo', 'file'), 'w') as f: + f.write('abc') + f.flush() + self.assertEqual(repo.status(), ' U') + self.assertEqual(repo.status('file'), 'U') + call(['hg', 'add', '.'], cwd='hg_repo', stdout=PIPE) + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), 'A') + os.remove(os.path.join('hg_repo', 'file')) + + +def setUpModule(): + global old_cwd + old_cwd = os.getcwd() + os.chdir(os.path.dirname(__file__)) + call(['git', 'init', '--quiet', 'git_repo']) + call(['git', 'config', '--local', 'user.name', 'Foo'], cwd='git_repo') + call(['git', 'config', '--local', 'user.email', 'bar@example.org'], cwd='git_repo') + call(['git', 'commit', '--allow-empty', '--message', 'Initial commit', '--quiet'], cwd='git_repo') + if use_mercurial: + call(['hg', 'init', 'hg_repo']) + with open(os.path.join('hg_repo', '.hg', 'hgrc'), 'w') as hgrc: + hgrc.write('[ui]\n') + hgrc.write('username = Foo \n') + + +def tearDownModule(): + global old_cwd + for repo_dir in ['git_repo'] + (['hg_repo'] if use_mercurial else []): + for root, dirs, files in list(os.walk(repo_dir, topdown=False)): + for file in files: + os.remove(os.path.join(root, file)) + for dir in dirs: + os.rmdir(os.path.join(root, dir)) + os.rmdir(repo_dir) + os.chdir(old_cwd) # NOQA diff --git a/tests/test_segments.py b/tests/test_segments.py new file mode 100644 index 00000000..30197bf1 --- /dev/null +++ b/tests/test_segments.py @@ -0,0 +1,381 @@ +# -*- coding: utf-8 -*- + +from unittest import TestCase +from powerline.segments import shell, common +import tests.vim as vim_module +import sys +import os +import imp + + +class Args(object): + theme_option = None + config = None + config_path = None + ext = ['shell'] + renderer_module = None + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +vim = None + + +class TestShell(TestCase): + def test_last_status(self): + self.assertEqual(shell.last_status(Args(last_exit_code=10)), + [{'contents': '10', 'highlight_group': 'exit_fail'}]) + self.assertEqual(shell.last_status(Args(last_exit_code=None)), None) + + def test_last_pipe_status(self): + self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[])), None) + self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[0, 0, 0])), None) + self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[0, 2, 0])), + [{'contents': '0', 'highlight_group': 'exit_success'}, + {'contents': '2', 'highlight_group': 'exit_fail'}, + {'contents': '0', 'highlight_group': 'exit_success'}]) + + +class TestCommon(TestCase): + def test_hostname(self): + os.environ['SSH_CLIENT'] = '192.168.0.12 40921 22' + socket = imp.new_module('socket') + socket.gethostname = lambda: 'abc' + sys.modules['socket'] = socket + try: + self.assertEqual(common.hostname(), 'abc') + self.assertEqual(common.hostname(only_if_ssh=True), 'abc') + os.environ.pop('SSH_CLIENT') + self.assertEqual(common.hostname(), 'abc') + self.assertEqual(common.hostname(only_if_ssh=True), None) + finally: + sys.modules.pop('socket') + + def test_user(self): + new_os = imp.new_module('os') + new_os.environ = {'USER': 'def'} + common.os = new_os + try: + self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) + new_os.geteuid = lambda: 1 + self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) + new_os.geteuid = lambda: 0 + self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) + finally: + common.os = os + + def test_branch(self): + vcslib = imp.new_module('powerline.lib.vcs') + vcslib.guess = lambda path: Args(branch=lambda: os.path.basename(path)) + sys.modules['powerline.lib.vcs'] = vcslib + try: + self.assertEqual(common.branch(), 'tests') + vcslib.guess = lambda path: None + self.assertEqual(common.branch(), None) + finally: + sys.modules.pop('powerline.lib.vcs') + + def test_cwd(self): + new_os = imp.new_module('os') + new_os.path = os.path + new_os.environ = {} + new_os.sep = '/' + new_os.getcwd = lambda: '/abc/def/ghi/foo/bar' + common.os = new_os + try: + self.assertEqual(common.cwd(), + [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'def', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + new_os.getcwdu = lambda: '/abc/def/ghi/foo/bar' + self.assertEqual(common.cwd(), + [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'def', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + new_os.environ['HOME'] = '/abc/def/ghi' + self.assertEqual(common.cwd(), + [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + self.assertEqual(common.cwd(dir_limit_depth=3), + [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + self.assertEqual(common.cwd(dir_limit_depth=1), + [{'contents': '⋯', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2), + [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'fo', 'divider_highlight_group': 'cwd:divider'}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + ose = OSError() + ose.errno = 2 + + def raises(exc): + raise exc + + new_os.getcwdu = lambda: raises(ose) + self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2), + [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + new_os.getcwdu = lambda: raises(OSError()) + with self.assertRaises(OSError): + common.cwd(dir_limit_depth=2, dir_shorten_len=2), + new_os.getcwdu = lambda: raises(ValueError()) + with self.assertRaises(ValueError): + common.cwd(dir_limit_depth=2, dir_shorten_len=2), + finally: + common.os = os + + def test_date(self): + datetime = imp.new_module('datetime') + datetime.datetime = Args(now=lambda: Args(strftime=lambda fmt: fmt)) + sys.modules['datetime'] = datetime + try: + self.assertEqual(common.date(), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}]) + self.assertEqual(common.date(format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) + finally: + sys.modules.pop('datetime') + + def test_fuzzy_time(self): + datetime = imp.new_module('datetime') + time = Args(hour=0, minute=45) + datetime.datetime = Args(now=lambda: time) + sys.modules['datetime'] = datetime + try: + self.assertEqual(common.fuzzy_time(), 'quarter to one') + time.hour = 23 + time.minute = 59 + self.assertEqual(common.fuzzy_time(), 'round about midnight') + time.minute = 33 + self.assertEqual(common.fuzzy_time(), 'twenty-five to twelve') + time.minute = 60 + self.assertEqual(common.fuzzy_time(), 'twelve o\'clock') + finally: + sys.modules.pop('datetime') + + def test_external_ip(self): + old_urllib_read = common.urllib_read + common.urllib_read = lambda url: '127.0.0.1' + try: + self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}]) + finally: + common.urllib_read = old_urllib_read + + def test_uptime(self): + # TODO + pass + + def test_weather(self): + # TODO + pass + + def test_system_load(self): + new_os = imp.new_module('os') + new_os.getloadavg = lambda: (7.5, 3.5, 1.5) + multiprocessing = imp.new_module('multiprocessing') + multiprocessing.cpu_count = lambda: 2 + common.os = new_os + sys.modules['multiprocessing'] = multiprocessing + try: + self.assertEqual(common.system_load(), + [{'contents': '7.5 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, + {'contents': '3.5 ', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, + {'contents': '1.5', 'highlight_group': ['system_load_good', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) + self.assertEqual(common.system_load(format='{avg:.0f}', threshold_good=0, threshold_bad=1), + [{'contents': '8 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, + {'contents': '4 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, + {'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) + finally: + common.os = os + sys.modules.pop('multiprocessing') + + def test_cpu_load_percent(self): + psutil = imp.new_module('psutil') + psutil.cpu_percent = lambda **kwargs: 52.3 + sys.modules['psutil'] = psutil + try: + self.assertEqual(common.cpu_load_percent(), '52%') + finally: + sys.modules.pop('psutil') + + def test_network_load(self): + # TODO + pass + + def test_virtualenv(self): + os.environ['VIRTUAL_ENV'] = '/abc/def/ghi' + self.assertEqual(common.virtualenv(), 'ghi') + os.environ.pop('VIRTUAL_ENV') + self.assertEqual(common.virtualenv(), None) + + def test_email_imap_alert(self): + # TODO + pass + + def test_now_playing(self): + # TODO + pass + + +class TestVim(TestCase): + def test_mode(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.mode(segment_info=segment_info), 'NORMAL') + self.assertEqual(vim.mode(segment_info=segment_info, override={'i': 'INS'}), 'NORMAL') + self.assertEqual(vim.mode(segment_info=segment_info, override={'n': 'NORM'}), 'NORM') + try: + vim_module._start_mode('i') + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.mode(segment_info=segment_info), 'INSERT') + vim_module._start_mode(chr(ord('V') - 0x40)) + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.mode(segment_info=segment_info), 'V·BLCK') + self.assertEqual(vim.mode(segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') + finally: + vim_module._start_mode('n') + + def test_modified_indicator(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.modified_indicator(segment_info=segment_info), None) + segment_info['buffer'][0] = 'abc' + try: + self.assertEqual(vim.modified_indicator(segment_info=segment_info), '+') + self.assertEqual(vim.modified_indicator(segment_info=segment_info, text='-'), '-') + finally: + vim_module._undo() + + def test_paste_indicator(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.paste_indicator(segment_info=segment_info), None) + vim_module._options['paste'] = 1 + try: + self.assertEqual(vim.paste_indicator(segment_info=segment_info), 'PASTE') + self.assertEqual(vim.paste_indicator(segment_info=segment_info, text='P'), 'P') + finally: + vim_module._options['paste'] = 0 + + def test_readonly_indicator(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.readonly_indicator(segment_info=segment_info), None) + vim_module._buf_options[vim_module._buffer()]['readonly'] = 1 + try: + self.assertEqual(vim.readonly_indicator(segment_info=segment_info), '') + self.assertEqual(vim.readonly_indicator(segment_info=segment_info, text='L'), 'L') + finally: + vim_module._buf_options[vim_module._buffer()]['readonly'] = 0 + + def test_file_directory(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.file_directory(segment_info=segment_info), None) + os.environ['HOME'] = '/home/foo' + vim_module._edit('/tmp/abc') + segment_info = vim_module._get_segment_info() + try: + self.assertEqual(vim.file_directory(segment_info=segment_info), '/tmp/') + os.environ['HOME'] = '/tmp' + self.assertEqual(vim.file_directory(segment_info=segment_info), '~/') + finally: + vim_module._bw(segment_info['bufnr']) + + def test_file_name(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.file_name(segment_info=segment_info), None) + self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True), + [{'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']}]) + self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True, no_file_text='X'), + [{'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']}]) + vim_module._edit('/tmp/abc') + segment_info = vim_module._get_segment_info() + try: + self.assertEqual(vim.file_name(segment_info=segment_info), 'abc') + finally: + vim_module._bw(segment_info['bufnr']) + vim_module._edit('/tmp/’’') + segment_info = vim_module._get_segment_info() + try: + self.assertEqual(vim.file_name(segment_info=segment_info), '’’') + finally: + vim_module._bw(segment_info['bufnr']) + + def test_file_size(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.file_size(segment_info=segment_info), None) + vim_module._edit(os.path.join(os.path.dirname(__file__), 'empty')) + segment_info = vim_module._get_segment_info() + try: + self.assertEqual(vim.file_size(segment_info=segment_info), '0 B') + finally: + vim_module._bw(segment_info['bufnr']) + + def test_file_opts(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.file_format(segment_info=segment_info), + [{'divider_highlight_group': 'background:divider', 'contents': 'unix'}]) + self.assertEqual(vim.file_encoding(segment_info=segment_info), + [{'divider_highlight_group': 'background:divider', 'contents': 'utf-8'}]) + self.assertEqual(vim.file_type(segment_info=segment_info), None) + vim_module._set_filetype('python') + try: + self.assertEqual(vim.file_type(segment_info=segment_info), + [{'divider_highlight_group': 'background:divider', 'contents': 'python'}]) + finally: + vim_module._set_filetype('') + + def test_line_percent(self): + segment_info = vim_module._get_segment_info() + segment_info['buffer'][0:-1] = [str(i) for i in range(100)] + try: + self.assertEqual(vim.line_percent(segment_info=segment_info), '0') + vim_module._set_cursor(50, 0) + self.assertEqual(vim.line_percent(segment_info=segment_info), '49') + self.assertEqual(vim.line_percent(segment_info=segment_info, gradient=True), + [{'contents': '49', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 49}]) + finally: + vim_module._bw(segment_info['bufnr']) + + def test_cursor_current(self): + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.line_current(segment_info=segment_info), '1') + self.assertEqual(vim.col_current(segment_info=segment_info), '1') + self.assertEqual(vim.virtcol_current(segment_info=segment_info), + [{'highlight_group': ['virtcol_current', 'col_current'], 'contents': '1'}]) + + def test_modified_buffers(self): + self.assertEqual(vim.modified_buffers(), None) + + def test_branch(self): + # TODO + pass + + def test_file_vcs_status(self): + # TODO + pass + + def test_repository_status(self): + # TODO + pass + + +old_cwd = None + + +def setUpModule(): + global old_cwd + old_cwd = os.getcwd() + os.chdir(os.path.dirname(__file__)) + sys.modules['vim'] = vim_module._get_module() + from powerline.segments import vim + globals()['vim'] = vim + + +def tearDownModule(): + global old_cwd + sys.modules.pop('vim') + os.chdir(old_cwd) diff --git a/tests/vim.py b/tests/vim.py new file mode 100644 index 00000000..dd232b70 --- /dev/null +++ b/tests/vim.py @@ -0,0 +1,367 @@ +_log = [] +_g = {} +_window = 0 +_mode = 'n' +_buf_purge_events = set() +_options = { + 'paste': 0, + } +_last_bufnr = 0 +_highlights = {} + +buffers = {} + +windows = [] + + +def _buffer(): + return windows[_window - 1].buffer.number + + +def _logged(func): + from functools import wraps + + @wraps(func) + def f(*args): + _log.append((func.__name__, args)) + return func(*args) + + return f + + +@_logged +def command(cmd): + if cmd.startswith('let g:'): + import re + varname, value = re.compile(r'^let g:(\w+)\s*=\s*(.*)').match(cmd).groups() + _g[varname] = value + elif cmd.startswith('hi '): + sp = cmd.split() + _highlights[sp[1]] = sp[2:] + else: + raise NotImplementedError + + +@_logged +def eval(expr): + if expr.startswith('g:'): + return _g[expr[2:]] + elif expr.startswith('&'): + return _options[expr[1:]] + elif expr.startswith('PowerlineRegisterCachePurgerEvent'): + _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) + return "0" + raise NotImplementedError + + +@_logged +def bindeval(expr): + if expr == 'g:': + return _g + import re + match = re.compile(r'^function\("([^"\\]+)"\)$').match(expr) + if match: + return globals()['_emul_' + match.group(1)] + else: + raise NotImplementedError + + +@_logged +def _emul_mode(*args): + if args and args[0]: + return _mode + else: + return _mode[0] + + +@_logged +def _emul_getbufvar(bufnr, varname): + if varname[0] == '&': + if bufnr not in _buf_options: + return '' + try: + return _buf_options[bufnr][varname[1:]] + except KeyError: + try: + return _options[varname[1:]] + except KeyError: + return '' + raise NotImplementedError + + +@_logged +def _emul_getwinvar(winnr, varname): + return _win_scopes[winnr][varname] + + +@_logged +def _emul_setwinvar(winnr, varname, value): + _win_scopes[winnr][varname] = value + + +@_logged +def _emul_virtcol(expr): + if expr == '.': + return windows[_window - 1].cursor[1] + 1 + raise NotImplementedError + + +@_logged +def _emul_fnamemodify(path, modstring): + import os + _modifiers = { + '~': lambda path: path.replace(os.environ['HOME'], '~') if path.startswith(os.environ['HOME']) else path, + '.': lambda path: (lambda tpath: path if tpath[:3] == '..' + os.sep else tpath)(os.path.relpath(path)), + 't': lambda path: os.path.basename(path), + 'h': lambda path: os.path.dirname(path), + } + + for mods in modstring.split(':')[1:]: + path = _modifiers[mods](path) + return path + + +@_logged +def _emul_expand(expr): + if expr == '': + return _buffer() + raise NotImplementedError + + +@_logged +def _emul_bufnr(expr): + if expr == '$': + return _last_bufnr + raise NotImplementedError + + +@_logged +def _emul_exists(varname): + if varname.startswith('g:'): + return varname[2:] in _g + raise NotImplementedError + + +_window_ids = [None] +_window_id = 0 +_win_scopes = [None] +_win_options = [None] + + +class _Window(object): + def __init__(self, buffer=None, cursor=(1, 0), width=80): + global _window_id + self.cursor = cursor + self.width = width + if buffer: + if type(buffer) is _Buffer: + self.buffer = buffer + else: + self.buffer = _Buffer(**buffer) + else: + self.buffer = _Buffer() + windows.append(self) + _window_id += 1 + _window_ids.append(_window_id) + _win_scopes.append({}) + _win_options.append({}) + + def __repr__(self): + return '' + + +_buf_scopes = {} +_buf_options = {} +_buf_lines = {} +_undostate = {} +_undo_written = {} + + +class _Buffer(object): + def __init__(self, name=None): + global _last_bufnr + import os + _last_bufnr += 1 + bufnr = _last_bufnr + self.number = bufnr + self.name = os.path.abspath(name) if name else None + _buf_scopes[bufnr] = {} + _buf_options[bufnr] = { + 'modified': 0, + 'readonly': 0, + 'fileformat': 'unix', + 'filetype': '', + 'buftype': '', + 'fileencoding': 'utf-8', + } + _buf_lines[bufnr] = [''] + from copy import copy + _undostate[bufnr] = [copy(_buf_lines[bufnr])] + _undo_written[bufnr] = len(_undostate[bufnr]) + buffers[bufnr] = self + + def __getitem__(self, line): + return _buf_lines[self.number][line] + + def __setitem__(self, line, value): + _buf_options[self.number]['modified'] = 1 + _buf_lines[self.number][line] = value + from copy import copy + _undostate[self.number].append(copy(_buf_lines[self.number])) + + def __setslice__(self, *args): + _buf_options[self.number]['modified'] = 1 + _buf_lines[self.number].__setslice__(*args) + from copy import copy + _undostate[self.number].append(copy(_buf_lines[self.number])) + + def __getslice__(self, *args): + return _buf_lines[self.number].__getslice__(*args) + + def __len__(self): + return len(_buf_lines[self.number]) + + def __repr__(self): + return '' + + def __del__(self): + bufnr = self.number + if _buf_options: + _buf_options.pop(bufnr) + _buf_lines.pop(bufnr) + _undostate.pop(bufnr) + _undo_written.pop(bufnr) + _buf_scopes.pop(bufnr) + + +_module = None + + +def _get_module(): + global _module + + if _module: + return _module + + import imp + _module = imp.new_module('vim') + for varname, value in globals().items(): + if varname[0] != '_': + setattr(_module, varname, value) + _new() + return _module + + +def _get_segment_info(): + mode_translations = { + chr(ord('V') - 0x40): '^V', + chr(ord('S') - 0x40): '^S', + } + mode = _mode + mode = mode_translations.get(mode, mode) + return { + 'window': windows[_window - 1], + 'buffer': buffers[_buffer()], + 'bufnr': _buffer(), + 'window_id': _window_ids[_window], + 'mode': mode, + } + + +def _launch_event(event): + pass + + +def _start_mode(mode): + global _mode + if mode == 'i': + _launch_event('InsertEnter') + elif _mode == 'i': + _launch_event('InsertLeave') + _mode = mode + + +def _undo(): + if len(_undostate[_buffer()]) == 1: + return + _undostate[_buffer()].pop(-1) + _buf_lines[_buffer()] = _undostate[_buffer()][-1] + if _undo_written[_buffer()] == len(_undostate[_buffer()]): + _buf_options[_buffer()]['modified'] = 0 + + +def _edit(name=None): + global _last_bufnr + if _buffer() and buffers[_buffer()].name is None: + buf = buffers[_buffer()] + buf.name = name + else: + buf = _Buffer(name) + windows[_window - 1].buffer = buf + + +def _new(name=None): + global _window + _Window(buffer={'name': name}) + _window = len(windows) + + +def _del_window(winnr): + win = windows.pop(winnr - 1) + _win_scopes.pop(winnr) + _win_options.pop(winnr) + _window_ids.pop(winnr) + return win + + +def _close(winnr, wipe=True): + global _window + win = _del_window(winnr) + if _window == winnr: + _window = len(windows) + if wipe: + for w in windows: + if w.buffer.number == win.buffer.number: + break + else: + _bw(win.buffer.number) + if not windows: + _Window() + + +def _bw(bufnr=None): + bufnr = bufnr or _buffer() + winnr = 1 + for win in windows: + if win.buffer.number == bufnr: + _close(winnr, wipe=False) + winnr += 1 + buffers.pop(bufnr) + if not buffers: + _Buffer() + _b(max(buffers.keys())) + + +def _b(bufnr): + windows[_window - 1].buffer = buffers[bufnr] + + +def _set_filetype(ft): + _buf_options[_buffer()]['filetype'] = ft + _launch_event('FileType') + + +def _set_cursor(line, col): + windows[_window - 1].cursor = (line, col) + if _mode == 'n': + _launch_event('CursorMoved') + elif _mode == 'i': + _launch_event('CursorMovedI') + + +def _get_buffer(): + return buffers[_buffer()] + + +def _set_bufoption(option, value, bufnr=None): + _buf_options[bufnr or _buffer()][option] = value From c334be416d1722bdf4fde694cb357adb872760df Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Mar 2013 18:40:13 +0400 Subject: [PATCH 0417/1472] Add tests.lib module with `replace_*()` with statement functions Note: there is new problem: now multiprocessing() globals got assigned None values. It is likely somehow related to extended `sys.modules` utilization --- tests/lib/__init__.py | 111 ++++++++++++++++++++++++++++++++ tests/test_segments.py | 139 ++++++++++++----------------------------- 2 files changed, 152 insertions(+), 98 deletions(-) create mode 100644 tests/lib/__init__.py diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py new file mode 100644 index 00000000..e7df12de --- /dev/null +++ b/tests/lib/__init__.py @@ -0,0 +1,111 @@ +import imp +import sys +import os + + +class Args(object): + theme_option = None + config = None + config_path = None + ext = ['shell'] + renderer_module = None + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +def urllib_read(query_url): + if query_url.startswith('http://ipv'): + if query_url.startswith('http://ipv4.icanhazip.com'): + return '127.0.0.1' + elif query_url.startswith('http://ipv4.icanhazip.com'): + return '2001:4801:7818:6:abc5:ba2c:ff10:275f' + elif query_url.startswith('http://freegeoip.net/json/'): + return '{"city": "Meppen", "region_code": "06", "region_name": "Niedersachsen", "areacode": "", "ip": "82.145.55.16", "zipcode": "49716", "longitude": 7.3167, "country_name": "Germany", "country_code": "DE", "metrocode": "", "latitude": 52.6833}' + elif query_url.startswith('http://query.yahooapis.com/v1/public/'): + return '{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"
\nCurrent Conditions:
\nPartly Cloudy, -11 C
\n
Forecast:
\nSat - Partly Cloudy. High: -9 Low: -19
\nSun - Partly Cloudy. High: -12 Low: -18
\n
\nFull Forecast at Yahoo! Weather

\n(provided by The Weather Channel)
","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}' + else: + raise NotImplementedError + + +class ModuleReplace(object): + def __init__(self, name, new): + self.name = name + self.new = new + + def __enter__(self): + self.old = sys.modules.get(self.name) + if not self.old: + try: + self.old = __import__(self.name) + except ImportError: + pass + sys.modules[self.name] = self.new + + def __exit__(self, *args): + if self.old: + sys.modules[self.name] = self.old + else: + sys.modules.pop(self.name) + + +def replace_module(name, new=None, **kwargs): + if not new: + new = new_module(name, **kwargs) + return ModuleReplace(name, new) + + +def new_module(name, **kwargs): + module = imp.new_module(name) + for k, v in kwargs.items(): + setattr(module, k, v) + return module + + +class ModuleAttrReplace(object): + def __init__(self, module, attr, new): + self.module = module + self.attr = attr + self.new = new + + def __enter__(self): + try: + self.old = getattr(self.module, self.attr) + except AttributeError: + pass + setattr(self.module, self.attr, self.new) + + def __exit__(self, *args): + try: + setattr(self.module, self.attr, self.old) + except AttributeError: + delattr(self.module, self.attr) + + +replace_module_attr = ModuleAttrReplace + + +def replace_module_module(module, name, **kwargs): + return replace_module_attr(module, name, new_module(name, **kwargs)) + + +class EnvReplace(object): + def __init__(self, name, new): + self.name = name + self.new = new + + def __enter__(self): + self.old = os.environ.get(self.name) + os.environ[self.name] = self.new + + def __exit__(self, *args): + if self.old is None: + try: + os.environ.pop(self.name) + except KeyError: + pass + else: + os.environ[self.name] = self.old + + +replace_env = EnvReplace diff --git a/tests/test_segments.py b/tests/test_segments.py index 30197bf1..4d53e353 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -5,18 +5,7 @@ from powerline.segments import shell, common import tests.vim as vim_module import sys import os -import imp - - -class Args(object): - theme_option = None - config = None - config_path = None - ext = ['shell'] - renderer_module = None - - def __init__(self, **kwargs): - self.__dict__.update(kwargs) +from .lib import Args, urllib_read, replace_module, replace_module_attr, new_module, replace_module_module, replace_env vim = None @@ -39,51 +28,34 @@ class TestShell(TestCase): class TestCommon(TestCase): def test_hostname(self): - os.environ['SSH_CLIENT'] = '192.168.0.12 40921 22' - socket = imp.new_module('socket') - socket.gethostname = lambda: 'abc' - sys.modules['socket'] = socket - try: - self.assertEqual(common.hostname(), 'abc') - self.assertEqual(common.hostname(only_if_ssh=True), 'abc') - os.environ.pop('SSH_CLIENT') - self.assertEqual(common.hostname(), 'abc') - self.assertEqual(common.hostname(only_if_ssh=True), None) - finally: - sys.modules.pop('socket') + with replace_env('SSH_CLIENT', '192.168.0.12 40921 22'): + with replace_module('socket', gethostname=lambda: 'abc'): + self.assertEqual(common.hostname(), 'abc') + self.assertEqual(common.hostname(only_if_ssh=True), 'abc') + os.environ.pop('SSH_CLIENT') + self.assertEqual(common.hostname(), 'abc') + self.assertEqual(common.hostname(only_if_ssh=True), None) def test_user(self): - new_os = imp.new_module('os') - new_os.environ = {'USER': 'def'} - common.os = new_os - try: + new_os = new_module('os', environ={'USER': 'def'}) + with replace_module_attr(common, 'os', new_os): self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) new_os.geteuid = lambda: 1 self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) new_os.geteuid = lambda: 0 self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) - finally: - common.os = os def test_branch(self): - vcslib = imp.new_module('powerline.lib.vcs') - vcslib.guess = lambda path: Args(branch=lambda: os.path.basename(path)) - sys.modules['powerline.lib.vcs'] = vcslib - try: + vcslib = new_module('powerline.lib.vcs', guess=lambda path: Args(branch=lambda: os.path.basename(path))) + with replace_module('powerline.lib.vcs', vcslib): self.assertEqual(common.branch(), 'tests') vcslib.guess = lambda path: None self.assertEqual(common.branch(), None) - finally: - sys.modules.pop('powerline.lib.vcs') def test_cwd(self): - new_os = imp.new_module('os') - new_os.path = os.path - new_os.environ = {} - new_os.sep = '/' + new_os = new_module('os', path=os.path, environ={}, sep='/') new_os.getcwd = lambda: '/abc/def/ghi/foo/bar' - common.os = new_os - try: + with replace_module_attr(common, 'os', new_os): self.assertEqual(common.cwd(), [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, @@ -130,25 +102,15 @@ class TestCommon(TestCase): new_os.getcwdu = lambda: raises(ValueError()) with self.assertRaises(ValueError): common.cwd(dir_limit_depth=2, dir_shorten_len=2), - finally: - common.os = os def test_date(self): - datetime = imp.new_module('datetime') - datetime.datetime = Args(now=lambda: Args(strftime=lambda fmt: fmt)) - sys.modules['datetime'] = datetime - try: + with replace_module('datetime', datetime=Args(now=lambda: Args(strftime=lambda fmt: fmt))): self.assertEqual(common.date(), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}]) self.assertEqual(common.date(format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) - finally: - sys.modules.pop('datetime') def test_fuzzy_time(self): - datetime = imp.new_module('datetime') time = Args(hour=0, minute=45) - datetime.datetime = Args(now=lambda: time) - sys.modules['datetime'] = datetime - try: + with replace_module('datetime', datetime=Args(now=lambda: time)): self.assertEqual(common.fuzzy_time(), 'quarter to one') time.hour = 23 time.minute = 59 @@ -157,16 +119,10 @@ class TestCommon(TestCase): self.assertEqual(common.fuzzy_time(), 'twenty-five to twelve') time.minute = 60 self.assertEqual(common.fuzzy_time(), 'twelve o\'clock') - finally: - sys.modules.pop('datetime') def test_external_ip(self): - old_urllib_read = common.urllib_read - common.urllib_read = lambda url: '127.0.0.1' - try: + with replace_module_attr(common, 'urllib_read', urllib_read): self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}]) - finally: - common.urllib_read = old_urllib_read def test_uptime(self): # TODO @@ -177,43 +133,30 @@ class TestCommon(TestCase): pass def test_system_load(self): - new_os = imp.new_module('os') - new_os.getloadavg = lambda: (7.5, 3.5, 1.5) - multiprocessing = imp.new_module('multiprocessing') - multiprocessing.cpu_count = lambda: 2 - common.os = new_os - sys.modules['multiprocessing'] = multiprocessing - try: - self.assertEqual(common.system_load(), - [{'contents': '7.5 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, - {'contents': '3.5 ', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, - {'contents': '1.5', 'highlight_group': ['system_load_good', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) - self.assertEqual(common.system_load(format='{avg:.0f}', threshold_good=0, threshold_bad=1), - [{'contents': '8 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, - {'contents': '4 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, - {'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) - finally: - common.os = os - sys.modules.pop('multiprocessing') + with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): + with replace_module('multiprocessing', cpu_count=lambda: 2): + self.assertEqual(common.system_load(), + [{'contents': '7.5 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, + {'contents': '3.5 ', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, + {'contents': '1.5', 'highlight_group': ['system_load_good', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) + self.assertEqual(common.system_load(format='{avg:.0f}', threshold_good=0, threshold_bad=1), + [{'contents': '8 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, + {'contents': '4 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, + {'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) def test_cpu_load_percent(self): - psutil = imp.new_module('psutil') - psutil.cpu_percent = lambda **kwargs: 52.3 - sys.modules['psutil'] = psutil - try: + with replace_module('psutil', cpu_percent=lambda **kwargs: 52.3): self.assertEqual(common.cpu_load_percent(), '52%') - finally: - sys.modules.pop('psutil') def test_network_load(self): # TODO pass def test_virtualenv(self): - os.environ['VIRTUAL_ENV'] = '/abc/def/ghi' - self.assertEqual(common.virtualenv(), 'ghi') - os.environ.pop('VIRTUAL_ENV') - self.assertEqual(common.virtualenv(), None) + with replace_env('VIRTUAL_ENV', '/abc/def/ghi'): + self.assertEqual(common.virtualenv(), 'ghi') + os.environ.pop('VIRTUAL_ENV') + self.assertEqual(common.virtualenv(), None) def test_email_imap_alert(self): # TODO @@ -274,15 +217,15 @@ class TestVim(TestCase): def test_file_directory(self): segment_info = vim_module._get_segment_info() self.assertEqual(vim.file_directory(segment_info=segment_info), None) - os.environ['HOME'] = '/home/foo' - vim_module._edit('/tmp/abc') - segment_info = vim_module._get_segment_info() - try: - self.assertEqual(vim.file_directory(segment_info=segment_info), '/tmp/') - os.environ['HOME'] = '/tmp' - self.assertEqual(vim.file_directory(segment_info=segment_info), '~/') - finally: - vim_module._bw(segment_info['bufnr']) + with replace_env('HOME', '/home/foo'): + vim_module._edit('/tmp/abc') + segment_info = vim_module._get_segment_info() + try: + self.assertEqual(vim.file_directory(segment_info=segment_info), '/tmp/') + os.environ['HOME'] = '/tmp' + self.assertEqual(vim.file_directory(segment_info=segment_info), '~/') + finally: + vim_module._bw(segment_info['bufnr']) def test_file_name(self): segment_info = vim_module._get_segment_info() From c429d9eeb261787d1f74b452fe94899e1e4aa992 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Mar 2013 19:05:08 +0400 Subject: [PATCH 0418/1472] Omit using sys.modules if possible Does not work though: still TypeError due to some global in multiproccessing that got None --- powerline/segments/common.py | 16 +++++++--------- tests/path/vim.py | 4 ++++ tests/test_configuration.py | 18 ++++++++++++++++-- tests/test_segments.py | 17 ++++++++--------- tests/vim.py | 16 ++++++++-------- 5 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 tests/path/vim.py diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 3692e5de..3da5db47 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -3,7 +3,12 @@ import os import sys +from datetime import datetime +import socket +from multiprocessing import cpu_count + from powerline.lib import memoize, urllib_read, urllib_urlencode, add_divider_highlight_group +from powerline.lib.vcs import guess def hostname(only_if_ssh=False): @@ -12,7 +17,6 @@ def hostname(only_if_ssh=False): :param bool only_if_ssh: only return the hostname if currently in an SSH session ''' - import socket if only_if_ssh and not os.environ.get('SSH_CLIENT'): return None return socket.gethostname() @@ -37,7 +41,6 @@ def user(): def branch(): '''Return the current VCS branch.''' - from powerline.lib.vcs import guess repo = guess(path=os.path.abspath(os.getcwd())) if repo: return repo.branch() @@ -96,7 +99,6 @@ def date(format='%Y-%m-%d', istime=False): :param str format: strftime-style date format string ''' - from datetime import datetime return [{ 'contents': datetime.now().strftime(format), 'highlight_group': (['time'] if istime else []) + ['date'], @@ -106,8 +108,6 @@ def date(format='%Y-%m-%d', istime=False): def fuzzy_time(): '''Display the current time as fuzzy time, e.g. "quarter past six".''' - from datetime import datetime - hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'] minute_str = { 5: 'five past', @@ -188,7 +188,6 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): ''' try: import psutil - from datetime import datetime seconds = int((datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).total_seconds()) except ImportError: try: @@ -361,11 +360,10 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): :param float threshold_bad: threshold for "bad load" highlighting ''' - import multiprocessing - cpu_count = multiprocessing.cpu_count() + cpu_num = cpu_count() ret = [] for avg in os.getloadavg(): - normalized = avg / cpu_count + normalized = avg / cpu_num if normalized < threshold_good: hl = 'system_load_good' elif normalized < threshold_bad: diff --git a/tests/path/vim.py b/tests/path/vim.py new file mode 100644 index 00000000..38df3bd0 --- /dev/null +++ b/tests/path/vim.py @@ -0,0 +1,4 @@ +from tests import vim + + +globals().update(vim._init()) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index c0d80592..cb3e2363 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -8,6 +8,8 @@ import tests.vim as vim_module import sys import os import json +from .lib import Args, urllib_read +import tests.lib as lib VBLOCK = chr(ord('V') - 0x40) @@ -55,20 +57,32 @@ class TestConfig(TestCase): finally: vim_module._g.pop('powerline_config_path') + def test_tmux(self): + import powerline.segments.common + from imp import reload + reload(powerline.segments.common) + old_urllib_read = powerline.segments.common.urllib_read + powerline.segments.common.urllib_read = urllib_read + from powerline.shell import ShellPowerline + try: + ShellPowerline(Args(ext=['tmux'])).renderer.render() + finally: + powerline.segments.common.urllib_read = old_urllib_read + old_cwd = None def setUpModule(): global old_cwd + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) old_cwd = os.getcwd() - sys.modules['vim'] = vim_module._get_module() from powerline.segments import vim globals()['vim'] = vim def tearDownModule(): global old_cwd - sys.modules.pop('vim') os.chdir(old_cwd) old_cwd = None + sys.path.pop(0) diff --git a/tests/test_segments.py b/tests/test_segments.py index 4d53e353..ad2f56a5 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -29,7 +29,7 @@ class TestShell(TestCase): class TestCommon(TestCase): def test_hostname(self): with replace_env('SSH_CLIENT', '192.168.0.12 40921 22'): - with replace_module('socket', gethostname=lambda: 'abc'): + with replace_module_module(common, 'socket', gethostname=lambda: 'abc'): self.assertEqual(common.hostname(), 'abc') self.assertEqual(common.hostname(only_if_ssh=True), 'abc') os.environ.pop('SSH_CLIENT') @@ -46,10 +46,9 @@ class TestCommon(TestCase): self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): - vcslib = new_module('powerline.lib.vcs', guess=lambda path: Args(branch=lambda: os.path.basename(path))) - with replace_module('powerline.lib.vcs', vcslib): + with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path))): self.assertEqual(common.branch(), 'tests') - vcslib.guess = lambda path: None + with replace_module_attr(common, 'guess', lambda path: None): self.assertEqual(common.branch(), None) def test_cwd(self): @@ -104,13 +103,13 @@ class TestCommon(TestCase): common.cwd(dir_limit_depth=2, dir_shorten_len=2), def test_date(self): - with replace_module('datetime', datetime=Args(now=lambda: Args(strftime=lambda fmt: fmt))): + with replace_module_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))): self.assertEqual(common.date(), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}]) self.assertEqual(common.date(format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) def test_fuzzy_time(self): time = Args(hour=0, minute=45) - with replace_module('datetime', datetime=Args(now=lambda: time)): + with replace_module_attr(common, 'datetime', Args(now=lambda: time)): self.assertEqual(common.fuzzy_time(), 'quarter to one') time.hour = 23 time.minute = 59 @@ -134,7 +133,7 @@ class TestCommon(TestCase): def test_system_load(self): with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): - with replace_module('multiprocessing', cpu_count=lambda: 2): + with replace_module_attr(common, 'cpu_count', lambda: 2): self.assertEqual(common.system_load(), [{'contents': '7.5 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, {'contents': '3.5 ', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, @@ -311,14 +310,14 @@ old_cwd = None def setUpModule(): global old_cwd + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) old_cwd = os.getcwd() os.chdir(os.path.dirname(__file__)) - sys.modules['vim'] = vim_module._get_module() from powerline.segments import vim globals()['vim'] = vim def tearDownModule(): global old_cwd - sys.modules.pop('vim') os.chdir(old_cwd) + sys.path.pop(0) diff --git a/tests/vim.py b/tests/vim.py index dd232b70..1c799c88 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -234,22 +234,22 @@ class _Buffer(object): _buf_scopes.pop(bufnr) -_module = None +_dict = None -def _get_module(): - global _module +def _init(): + global _dict - if _module: - return _module + if _dict: + return _dict import imp - _module = imp.new_module('vim') + _dict = {} for varname, value in globals().items(): if varname[0] != '_': - setattr(_module, varname, value) + _dict[varname] = value _new() - return _module + return _dict def _get_segment_info(): From 0a05b2961a2721a3b91278f165698805592773ef Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 00:02:07 +0400 Subject: [PATCH 0419/1472] Add support for with: statement to tests.vim Also made everything use new vim_module._with where possible --- tests/test_configuration.py | 53 +++++++++------------- tests/test_segments.py | 50 +++++---------------- tests/vim.py | 88 ++++++++++++++++++++++++++++++++++--- 3 files changed, 114 insertions(+), 77 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index cb3e2363..6eed7b76 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -8,8 +8,7 @@ import tests.vim as vim_module import sys import os import json -from .lib import Args, urllib_read -import tests.lib as lib +from .lib import Args, urllib_read, replace_module_attr VBLOCK = chr(ord('V') - 0x40) @@ -20,9 +19,7 @@ class TestConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') - vim_module._g['powerline_config_path'] = cfg_path - buffers = ((lambda: vim_module._set_bufoption('buftype', 'help'), lambda: vim_module._set_bufoption('buftype', '')), - (lambda: vim_module._edit('[Command Line]'), lambda: vim_module._bw())) + buffers = ((('bufoptions',), {'buftype': 'help'}), (('buffer', '[Command Line]'), {})) with open(os.path.join(cfg_path, 'config.json'), 'r') as f: self.assertEqual(len(buffers), len(json.load(f)['ext']['vim']['local_themes'])) outputs = {} @@ -36,38 +33,30 @@ class TestConfig(TestCase): self.fail('Duplicate in set #{0} for mode {1!r} (previously defined in set #{2} for mode {3!r})'.format(i, mode, *outputs[out])) outputs[out] = (i, mode) - try: - exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) - try: - for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: - if mode != 'nc': - vim_module._start_mode(mode) - check_output(1, 0) - for setup, teardown in buffers: - i += 1 - if mode in exclude: - continue - setup() - try: - check_output(1, 0) - finally: - teardown() - finally: - vim_module._start_mode('n') - finally: - vim_module._g.pop('powerline_config_path') + with vim_module._with('buffer', 'foo.txt'): + with vim_module._with('globals', powerline_config_path=cfg_path): + exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) + try: + for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: + if mode != 'nc': + vim_module._start_mode(mode) + check_output(1, 0) + for args, kwargs in buffers: + i += 1 + if mode in exclude: + continue + with vim_module._with(*args, **kwargs): + check_output(1, 0) + finally: + vim_module._start_mode('n') def test_tmux(self): - import powerline.segments.common + from powerline.segments import common from imp import reload - reload(powerline.segments.common) - old_urllib_read = powerline.segments.common.urllib_read - powerline.segments.common.urllib_read = urllib_read + reload(common) from powerline.shell import ShellPowerline - try: + with replace_module_attr(common, 'urllib_read', urllib_read): ShellPowerline(Args(ext=['tmux'])).renderer.render() - finally: - powerline.segments.common.urllib_read = old_urllib_read old_cwd = None diff --git a/tests/test_segments.py b/tests/test_segments.py index ad2f56a5..e42724ef 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -172,16 +172,11 @@ class TestVim(TestCase): self.assertEqual(vim.mode(segment_info=segment_info), 'NORMAL') self.assertEqual(vim.mode(segment_info=segment_info, override={'i': 'INS'}), 'NORMAL') self.assertEqual(vim.mode(segment_info=segment_info, override={'n': 'NORM'}), 'NORM') - try: - vim_module._start_mode('i') - segment_info = vim_module._get_segment_info() + with vim_module._with('mode', 'i') as segment_info: self.assertEqual(vim.mode(segment_info=segment_info), 'INSERT') - vim_module._start_mode(chr(ord('V') - 0x40)) - segment_info = vim_module._get_segment_info() + with vim_module._with('mode', chr(ord('V') - 0x40)) as segment_info: self.assertEqual(vim.mode(segment_info=segment_info), 'V·BLCK') self.assertEqual(vim.mode(segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') - finally: - vim_module._start_mode('n') def test_modified_indicator(self): segment_info = vim_module._get_segment_info() @@ -191,40 +186,30 @@ class TestVim(TestCase): self.assertEqual(vim.modified_indicator(segment_info=segment_info), '+') self.assertEqual(vim.modified_indicator(segment_info=segment_info, text='-'), '-') finally: - vim_module._undo() + vim_module._bw(segment_info['bufnr']) def test_paste_indicator(self): segment_info = vim_module._get_segment_info() self.assertEqual(vim.paste_indicator(segment_info=segment_info), None) - vim_module._options['paste'] = 1 - try: + with vim_module._with('options', paste=1): self.assertEqual(vim.paste_indicator(segment_info=segment_info), 'PASTE') self.assertEqual(vim.paste_indicator(segment_info=segment_info, text='P'), 'P') - finally: - vim_module._options['paste'] = 0 def test_readonly_indicator(self): segment_info = vim_module._get_segment_info() self.assertEqual(vim.readonly_indicator(segment_info=segment_info), None) - vim_module._buf_options[vim_module._buffer()]['readonly'] = 1 - try: + with vim_module._with('bufoptions', readonly=1): self.assertEqual(vim.readonly_indicator(segment_info=segment_info), '') self.assertEqual(vim.readonly_indicator(segment_info=segment_info, text='L'), 'L') - finally: - vim_module._buf_options[vim_module._buffer()]['readonly'] = 0 def test_file_directory(self): segment_info = vim_module._get_segment_info() self.assertEqual(vim.file_directory(segment_info=segment_info), None) with replace_env('HOME', '/home/foo'): - vim_module._edit('/tmp/abc') - segment_info = vim_module._get_segment_info() - try: + with vim_module._with('buffer', '/tmp/abc') as segment_info: self.assertEqual(vim.file_directory(segment_info=segment_info), '/tmp/') os.environ['HOME'] = '/tmp' self.assertEqual(vim.file_directory(segment_info=segment_info), '~/') - finally: - vim_module._bw(segment_info['bufnr']) def test_file_name(self): segment_info = vim_module._get_segment_info() @@ -233,28 +218,16 @@ class TestVim(TestCase): [{'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']}]) self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True, no_file_text='X'), [{'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']}]) - vim_module._edit('/tmp/abc') - segment_info = vim_module._get_segment_info() - try: + with vim_module._with('buffer', '/tmp/abc') as segment_info: self.assertEqual(vim.file_name(segment_info=segment_info), 'abc') - finally: - vim_module._bw(segment_info['bufnr']) - vim_module._edit('/tmp/’’') - segment_info = vim_module._get_segment_info() - try: + with vim_module._with('buffer', '/tmp/’’') as segment_info: self.assertEqual(vim.file_name(segment_info=segment_info), '’’') - finally: - vim_module._bw(segment_info['bufnr']) def test_file_size(self): segment_info = vim_module._get_segment_info() self.assertEqual(vim.file_size(segment_info=segment_info), None) - vim_module._edit(os.path.join(os.path.dirname(__file__), 'empty')) - segment_info = vim_module._get_segment_info() - try: + with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info: self.assertEqual(vim.file_size(segment_info=segment_info), '0 B') - finally: - vim_module._bw(segment_info['bufnr']) def test_file_opts(self): segment_info = vim_module._get_segment_info() @@ -263,12 +236,9 @@ class TestVim(TestCase): self.assertEqual(vim.file_encoding(segment_info=segment_info), [{'divider_highlight_group': 'background:divider', 'contents': 'utf-8'}]) self.assertEqual(vim.file_type(segment_info=segment_info), None) - vim_module._set_filetype('python') - try: + with vim_module._with('bufoptions', filetype='python'): self.assertEqual(vim.file_type(segment_info=segment_info), [{'divider_highlight_group': 'background:divider', 'contents': 'python'}]) - finally: - vim_module._set_filetype('') def test_line_percent(self): segment_info = vim_module._get_segment_info() diff --git a/tests/vim.py b/tests/vim.py index 1c799c88..de3c9fe4 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -346,11 +346,6 @@ def _b(bufnr): windows[_window - 1].buffer = buffers[bufnr] -def _set_filetype(ft): - _buf_options[_buffer()]['filetype'] = ft - _launch_event('FileType') - - def _set_cursor(line, col): windows[_window - 1].cursor = (line, col) if _mode == 'n': @@ -365,3 +360,86 @@ def _get_buffer(): def _set_bufoption(option, value, bufnr=None): _buf_options[bufnr or _buffer()][option] = value + if option == 'filetype': + _launch_event('FileType') + + +class _WithNewBuffer(object): + def __init__(self, func, *args, **kwargs): + self.call = lambda: func(*args, **kwargs) + + def __enter__(self): + self.call() + self.bufnr = _buffer() + return _get_segment_info() + + def __exit__(self, *args): + _bw(self.bufnr) + + +def _set_dict(d, new, setfunc=None): + if not setfunc: + def setfunc(k, v): + d[k] = v + + old = {} + na = [] + for k, v in new.items(): + try: + old[k] = d[k] + except KeyError: + na.append(k) + setfunc(k, v) + return old, na + + +class _WithBufOption(object): + def __init__(self, **new): + self.new = new + + def __enter__(self): + self.bufnr = _buffer() + self.old = _set_dict(_buf_options[self.bufnr], self.new, _set_bufoption)[0] + + def __exit__(self, *args): + _buf_options[self.bufnr].update(self.old) + + +class _WithMode(object): + def __init__(self, new): + self.new = new + + def __enter__(self): + self.old = _mode + _start_mode(self.new) + return _get_segment_info() + + def __exit__(self, *args): + _start_mode(self.old) + + +class _WithDict(object): + def __init__(self, d, **new): + self.new = new + self.d = d + + def __enter__(self): + self.old, self.na = _set_dict(self.d, self.new) + + def __exit__(self, *args): + self.d.update(self.old) + for k in self.na: + self.d.pop(k) + + +def _with(key, *args, **kwargs): + if key == 'buffer': + return _WithNewBuffer(_edit, *args, **kwargs) + elif key == 'mode': + return _WithMode(*args, **kwargs) + elif key == 'bufoptions': + return _WithBufOption(**kwargs) + elif key == 'options': + return _WithDict(_options, **kwargs) + elif key == 'globals': + return _WithDict(_g, **kwargs) From aa0a8bf76dc73afb55802f45bedb766b41efa7cc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 20:40:35 +0400 Subject: [PATCH 0420/1472] Make tests support python2.6 Old unittest is not working for unknown reason, using unittest2 instead --- .travis.yml | 5 +++-- setup.py | 5 ++++- tests/__init__.py | 5 +++++ tests/install.sh | 10 ++++++++++ tests/test.sh | 12 ++++++++++++ tests/test_configuration.py | 9 +++++++-- tests/test_lib.py | 22 ++++++++++++++++++++-- tests/test_segments.py | 17 +++++++++++------ tests/vim.py | 27 ++++++++++++++++++++++++--- 9 files changed, 96 insertions(+), 16 deletions(-) create mode 100755 tests/install.sh create mode 100755 tests/test.sh diff --git a/.travis.yml b/.travis.yml index 3e469f10..774a6f60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ language: python python: + - "2.6" - "2.7" - "3.2" - "3.3" -install: "pip install ." -script: python setup.py test +install: tests/install.sh +script: tests/test.sh branches: only: - tests diff --git a/setup.py b/setup.py index b7a09c99..17ea49fc 100755 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import os +import sys from setuptools import setup, find_packages @@ -11,6 +12,8 @@ try: except IOError: README = '' +old_python = sys.version_info < (2, 7) + setup( name='Powerline', version='beta', @@ -33,5 +36,5 @@ setup( 'Sphinx', ], }, - test_suite='tests', + test_suite='tests' if not old_python else None, ) diff --git a/tests/__init__.py b/tests/__init__.py index e69de29b..b6e78fe6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,5 @@ +import sys +if sys.version_info < (2, 7): + from unittest2 import TestCase, main # NOQA +else: + from unittest import TestCase, main # NOQA diff --git a/tests/install.sh b/tests/install.sh new file mode 100755 index 00000000..ddce7e74 --- /dev/null +++ b/tests/install.sh @@ -0,0 +1,10 @@ +#!/bin/sh +pip install . +if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then + # Python 2 + pip install mercurial + if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then + # Python 2.6 + pip install unittest2 + fi +fi diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 00000000..7a018357 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,12 @@ +#!/bin/sh +if python -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then + # Python 2.6 + export PYTHONPATH="${PYTHONPATH}:`realpath .`" + for file in tests/test_*.py ; do + if ! python $file ; then + exit 1 + fi + done +else + python setup.py test +fi diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 6eed7b76..529b48b4 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -3,12 +3,12 @@ '''Dynamic configuration files tests.''' -from unittest import TestCase import tests.vim as vim_module import sys import os import json -from .lib import Args, urllib_read, replace_module_attr +from tests.lib import Args, urllib_read, replace_module_attr +from tests import TestCase VBLOCK = chr(ord('V') - 0x40) @@ -75,3 +75,8 @@ def tearDownModule(): os.chdir(old_cwd) old_cwd = None sys.path.pop(0) + + +if __name__ == '__main__': + from tests import main + main() diff --git a/tests/test_lib.py b/tests/test_lib.py index 0d4e667a..46dd03b3 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,9 +1,9 @@ from powerline.lib import mergedicts, underscore_to_camelcase, add_divider_highlight_group, humanize_bytes from powerline.lib.vcs import guess -from unittest import TestCase from subprocess import call, PIPE import os import sys +from tests import TestCase class TestLib(TestCase): @@ -79,8 +79,13 @@ class TestVCS(TestCase): os.remove(os.path.join('hg_repo', 'file')) +old_HGRCPATH = None +old_cwd = None + + def setUpModule(): global old_cwd + global old_HGRCPATH old_cwd = os.getcwd() os.chdir(os.path.dirname(__file__)) call(['git', 'init', '--quiet', 'git_repo']) @@ -88,6 +93,8 @@ def setUpModule(): call(['git', 'config', '--local', 'user.email', 'bar@example.org'], cwd='git_repo') call(['git', 'commit', '--allow-empty', '--message', 'Initial commit', '--quiet'], cwd='git_repo') if use_mercurial: + old_HGRCPATH = os.environ.get('HGRCPATH') + os.environ['HGRCPATH'] = '' call(['hg', 'init', 'hg_repo']) with open(os.path.join('hg_repo', '.hg', 'hgrc'), 'w') as hgrc: hgrc.write('[ui]\n') @@ -96,6 +103,7 @@ def setUpModule(): def tearDownModule(): global old_cwd + global old_HGRCPATH for repo_dir in ['git_repo'] + (['hg_repo'] if use_mercurial else []): for root, dirs, files in list(os.walk(repo_dir, topdown=False)): for file in files: @@ -103,4 +111,14 @@ def tearDownModule(): for dir in dirs: os.rmdir(os.path.join(root, dir)) os.rmdir(repo_dir) - os.chdir(old_cwd) # NOQA + if use_mercurial: + if old_HGRCPATH is None: + os.environ.pop('HGRCPATH') + else: + os.environ['HGRCPATH'] = old_HGRCPATH + os.chdir(old_cwd) + + +if __name__ == '__main__': + from tests import main + main() diff --git a/tests/test_segments.py b/tests/test_segments.py index e42724ef..d3edf072 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -from unittest import TestCase from powerline.segments import shell, common import tests.vim as vim_module import sys import os -from .lib import Args, urllib_read, replace_module, replace_module_attr, new_module, replace_module_module, replace_env +from tests.lib import Args, urllib_read, replace_module, replace_module_attr, new_module, replace_module_module, replace_env +from tests import TestCase vim = None @@ -96,11 +96,9 @@ class TestCommon(TestCase): self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2), [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) new_os.getcwdu = lambda: raises(OSError()) - with self.assertRaises(OSError): - common.cwd(dir_limit_depth=2, dir_shorten_len=2), + self.assertRaises(OSError, common.cwd, tuple(), {'dir_limit_depth': 2, 'dir_shorten_len': 2}) new_os.getcwdu = lambda: raises(ValueError()) - with self.assertRaises(ValueError): - common.cwd(dir_limit_depth=2, dir_shorten_len=2), + self.assertRaises(ValueError, common.cwd, tuple(), {'dir_limit_depth': 2, 'dir_shorten_len': 2}) def test_date(self): with replace_module_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))): @@ -280,8 +278,10 @@ old_cwd = None def setUpModule(): global old_cwd + global __file__ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) old_cwd = os.getcwd() + __file__ = os.path.abspath(__file__) os.chdir(os.path.dirname(__file__)) from powerline.segments import vim globals()['vim'] = vim @@ -291,3 +291,8 @@ def tearDownModule(): global old_cwd os.chdir(old_cwd) sys.path.pop(0) + + +if __name__ == '__main__': + from tests import main + main() diff --git a/tests/vim.py b/tests/vim.py index de3c9fe4..c49a0da5 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -22,13 +22,19 @@ def _logged(func): from functools import wraps @wraps(func) - def f(*args): + def f(*args, **kwargs): _log.append((func.__name__, args)) - return func(*args) + return func(*args, **kwargs) return f +def _log_print(): + import sys + for entry in _log: + sys.stdout.write(repr(entry) + '\n') + + @_logged def command(cmd): if cmd.startswith('let g:'): @@ -237,13 +243,13 @@ class _Buffer(object): _dict = None +@_logged def _init(): global _dict if _dict: return _dict - import imp _dict = {} for varname, value in globals().items(): if varname[0] != '_': @@ -252,6 +258,7 @@ def _init(): return _dict +@_logged def _get_segment_info(): mode_translations = { chr(ord('V') - 0x40): '^V', @@ -268,10 +275,12 @@ def _get_segment_info(): } +@_logged def _launch_event(event): pass +@_logged def _start_mode(mode): global _mode if mode == 'i': @@ -281,6 +290,7 @@ def _start_mode(mode): _mode = mode +@_logged def _undo(): if len(_undostate[_buffer()]) == 1: return @@ -290,6 +300,7 @@ def _undo(): _buf_options[_buffer()]['modified'] = 0 +@_logged def _edit(name=None): global _last_bufnr if _buffer() and buffers[_buffer()].name is None: @@ -300,12 +311,14 @@ def _edit(name=None): windows[_window - 1].buffer = buf +@_logged def _new(name=None): global _window _Window(buffer={'name': name}) _window = len(windows) +@_logged def _del_window(winnr): win = windows.pop(winnr - 1) _win_scopes.pop(winnr) @@ -314,6 +327,7 @@ def _del_window(winnr): return win +@_logged def _close(winnr, wipe=True): global _window win = _del_window(winnr) @@ -329,6 +343,7 @@ def _close(winnr, wipe=True): _Window() +@_logged def _bw(bufnr=None): bufnr = bufnr or _buffer() winnr = 1 @@ -342,10 +357,12 @@ def _bw(bufnr=None): _b(max(buffers.keys())) +@_logged def _b(bufnr): windows[_window - 1].buffer = buffers[bufnr] +@_logged def _set_cursor(line, col): windows[_window - 1].cursor = (line, col) if _mode == 'n': @@ -354,10 +371,12 @@ def _set_cursor(line, col): _launch_event('CursorMovedI') +@_logged def _get_buffer(): return buffers[_buffer()] +@_logged def _set_bufoption(option, value, bufnr=None): _buf_options[bufnr or _buffer()][option] = value if option == 'filetype': @@ -377,6 +396,7 @@ class _WithNewBuffer(object): _bw(self.bufnr) +@_logged def _set_dict(d, new, setfunc=None): if not setfunc: def setfunc(k, v): @@ -432,6 +452,7 @@ class _WithDict(object): self.d.pop(k) +@_logged def _with(key, *args, **kwargs): if key == 'buffer': return _WithNewBuffer(_edit, *args, **kwargs) From 4c233122d62dd3fc7d15c088bdc76b7c5152ac9d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 20:58:33 +0400 Subject: [PATCH 0421/1472] Do not restrict branches to `tests` branch In any case it was not working --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 774a6f60..dadf667d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,5 @@ python: - "3.3" install: tests/install.sh script: tests/test.sh -branches: - only: - - tests # vim: et From f6d23bdf7e25abb00542d359e1ffb78dbcfd1453 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Mar 2013 15:00:10 +0400 Subject: [PATCH 0422/1472] Always return a string in email_imap_alert segment Fixes #279 --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 3da5db47..29c65be1 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -484,7 +484,7 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold return None return [{ 'highlight_group': 'email_alert', - 'contents': unread_count, + 'contents': str(unread_count), }] From cb87cfbe0fe7408b77ad05636571bceab12ebad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 3 Mar 2013 22:11:39 +0100 Subject: [PATCH 0423/1472] Update credits and PR guidelines --- docs/source/license-credits.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index 89393f05..7bbe7654 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -16,9 +16,10 @@ Unported License`_. Credits ======= -:Author: `Kim Silkebækken `_ -:Main contributors: +:Authors: + * `Kim Silkebækken `_ * `ZyX-I `_ +:Main contributors: * `Liam Curry `_ The glyphs in the font patcher are created by Fabrizio Schiavi, creator of @@ -33,7 +34,7 @@ If you experience any bugs or have any feature requests, please `open an issue on GitHub `_. Pull request guidelines ------------------------ +======================= This project uses `Git Flow`_ for maintaining a clean history and a consistent way of branching and merging new features. All commit messages @@ -42,9 +43,8 @@ commit messages`_. All code must use tabs for indentation and spaces for alignment. -Python code must pass flake8 tests with ``flake8 --ignore=W191,E501`` (ignore -tab warnings and line length errors). +Python code should pass flake8 tests with ``flake8 +--ignore=W191,E501,E121,E122,E123,E128``. .. _`Git Flow`: http://nvie.com/posts/a-successful-git-branching-model/ .. _`Tim Pope's blog post about git commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html - From be62815c0bd5547d1370ed7ec47db69f15f0c494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 4 Mar 2013 15:13:58 +0100 Subject: [PATCH 0424/1472] Handle socket errors in IMAP mail segment --- powerline/segments/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 29c65be1..65e7c7a0 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -478,6 +478,8 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold rc, message = mail.status(folder, '(UNSEEN)') unread_str = message[0].decode('utf-8') unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) + except socket.gaierror: + return None except imaplib.IMAP4.error as e: unread_count = str(e) if not unread_count: From 162f5ac55bd4eff13ddf975ee3472130c940e01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Mar 2013 17:57:41 +0100 Subject: [PATCH 0425/1472] Change license from CC BY-SA to the MIT license Refs #299. --- LICENSE | 21 +++++++++++++++++++ docs/source/license-credits.rst | 9 ++------ .../archlinux/python-powerline-git/PKGBUILD | 8 +++++-- .../archlinux/python2-powerline-git/PKGBUILD | 8 +++++-- 4 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ad3cf61a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright 2013 Kim Silkebækken and other contributors +https://github.com/Lokaltog/powerline + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index 7bbe7654..167c613e 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -5,13 +5,8 @@ License and credits License ======= -.. image:: http://i.creativecommons.org/l/by-sa/3.0/88x31.png - :target: `Creative Commons Attribution-ShareAlike 3.0 Unported License`_ - -Powerline is licensed under a `Creative Commons Attribution-ShareAlike 3.0 -Unported License`_. - -.. _`Creative Commons Attribution-ShareAlike 3.0 Unported License`: http://creativecommons.org/licenses/by-sa/3.0/ +Powerline is licensed under the `MIT license +`_. Credits ======= diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 42ca251f..6d0d00e8 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -2,11 +2,11 @@ pkgbase=python-powerline pkgname=python-powerline-git -pkgver=20130208 +pkgver=20130308 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' -license=('CC BY-SA 3.0') +license=('MIT') arch=('any') depends=('python>=3.3') makedepends=('git' 'python-distribute') @@ -62,4 +62,8 @@ package() { msg2 "Installing tmux configuration..." install -dm755 "${pkgdir}/usr/share/tmux" install -m644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" + + msg2 "Installing license..." + install -dm755 "${pkgdir}/usr/share/licenses/${pkgname}" + install -m644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 8d8388f3..48d1e916 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -2,11 +2,11 @@ pkgbase=python-powerline pkgname=python2-powerline-git -pkgver=20130208 +pkgver=20130308 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' -license=('CC BY-SA 3.0') +license=('MIT') arch=('any') depends=('python2>=2.7') makedepends=('git' 'python2-distribute') @@ -63,4 +63,8 @@ package() { msg2 "Installing tmux configuration..." install -dm755 "${pkgdir}/usr/share/tmux" install -m644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" + + msg2 "Installing license..." + install -dm755 "${pkgdir}/usr/share/licenses/${pkgname}" + install -m644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } From 154d1aeac14d848397797faef9f9d450e8113480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Mar 2013 18:01:50 +0100 Subject: [PATCH 0426/1472] Fix issues in Arch Linux PKGBUILDS Closes #291. --- packages/archlinux/python-powerline-git/PKGBUILD | 3 +-- packages/archlinux/python-powerline-git/powerline.install | 5 +++++ packages/archlinux/python2-powerline-git/PKGBUILD | 5 ++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 6d0d00e8..a0c2d717 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -1,6 +1,5 @@ # Maintainer: Kim Silkebækken -pkgbase=python-powerline pkgname=python-powerline-git pkgver=20130308 pkgrel=1 @@ -8,7 +7,7 @@ pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') arch=('any') -depends=('python>=3.3') +depends=('python>=3.2') makedepends=('git' 'python-distribute') optdepends=('python-psutil: improved system information' 'python-pygit2: improved git support' diff --git a/packages/archlinux/python-powerline-git/powerline.install b/packages/archlinux/python-powerline-git/powerline.install index d7938d2b..d6644b17 100644 --- a/packages/archlinux/python-powerline-git/powerline.install +++ b/packages/archlinux/python-powerline-git/powerline.install @@ -16,6 +16,11 @@ on GitHub: https://github.com/Lokaltog/powerline/issues Consult the documentation for detailed installation instructions and troubleshooting information: https://powerline.readthedocs.org/en/latest/ +You may need to update your PYTHONPATH environment variable by adding the +following line to your /etc/profile, .zshrc, etc.: + + export PYTHONPATH=/usr/lib/python3.3/site-packages + Vim installation ---------------- diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 48d1e916..8239b93d 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,6 +1,5 @@ # Maintainer: Kim Silkebækken -pkgbase=python-powerline pkgname=python2-powerline-git pkgver=20130308 pkgrel=1 @@ -8,13 +7,13 @@ pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') arch=('any') -depends=('python2>=2.7') +depends=('python2>=2.6') makedepends=('git' 'python2-distribute') optdepends=('python2-psutil: improved system information' 'python2-pygit2: improved git support' 'mercurial: improved mercurial support' 'zsh: better shell prompt') -conflicts=('powerline-git') +replaces=('powerline-git') install='powerline.install' source=() From 6777fd41d4be7fda0827c07196f3cecc6c840774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Mar 2013 18:03:57 +0100 Subject: [PATCH 0427/1472] Fix minor typo in powerline.vim --- powerline/bindings/vim/plugin/powerline.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 34710e8c..1d79c726 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -22,7 +22,7 @@ let s:import_cmd = 'from powerline.vim import VimPowerline' try exec s:powerline_pycmd s:import_cmd catch - " An error occured while importing the module, it could be installed + " An error occurred while importing the module, it could be installed " outside of Python's module search paths. Update sys.path and try again. exec s:powerline_pycmd 'import sys, vim' exec s:powerline_pycmd 'sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' @@ -31,7 +31,7 @@ catch let s:launched = 1 finally if !exists('s:launched') - call s:CriticalError('An error occured while importing the Powerline package. + call s:CriticalError('An error occurred while importing the Powerline package. \ This could be caused by an invalid sys.path setting, or by an incompatible \ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult \ the troubleshooting section in the documentation for possible solutions.') From 20639494c04bba2cc7611f4b893f414bd3bee370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 8 Mar 2013 18:35:44 +0100 Subject: [PATCH 0428/1472] Add Travis build status to README --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 0acc43b4..a5a4fee6 100644 --- a/README.rst +++ b/README.rst @@ -4,6 +4,10 @@ Powerline :Author: Kim Silkebækken (kim.silkebaekken+vim@gmail.com) :Source: https://github.com/Lokaltog/powerline :Version: beta +:Build status: + .. image:: https://api.travis-ci.org/Lokaltog/powerline.png?branch=develop + :target: `travis-build-status`_ + :alt: Build status This is the upcoming version of Powerline, implemented in Python. The project is currently in a stable beta and almost ready for release. @@ -14,6 +18,8 @@ project is currently in a stable beta and almost ready for release. * Check out `powerline-fonts `_ for pre-patched versions of popular coding fonts. +.. _travis-build-status: https://travis-ci.org/Lokaltog/powerline + Screenshots ----------- From bee14674849edcaff4814a7f6f85dce2c584ed15 Mon Sep 17 00:00:00 2001 From: Peter Fern Date: Sat, 9 Mar 2013 18:15:24 +1100 Subject: [PATCH 0429/1472] Output in either otf or ttf (so we don't loose hinting/whatever) --- font/fontpatcher.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/font/fontpatcher.py b/font/fontpatcher.py index b0869843..2f2e4138 100755 --- a/font/fontpatcher.py +++ b/font/fontpatcher.py @@ -3,6 +3,7 @@ import argparse import sys +import os.path try: import fontforge @@ -100,7 +101,11 @@ class FontPatcher(object): target_font.em = target_font_em_original # Generate patched font - target_font.generate('{0}.otf'.format(target_font.fullname)) + extension = os.path.splitext(target_font.path)[1] + if extension.lower() not in ['.ttf', '.otf']: + # Default to OpenType if input is not TrueType/OpenType + extension = '.otf' + target_font.generate('{0}{1}'.format(target_font.fullname, extension)) fp = FontPatcher(args.source_font, args.target_fonts, args.rename_font) fp.patch() From 38e2368682c4abdf43cfe9e8ebb1110891399907 Mon Sep 17 00:00:00 2001 From: Peter Fern Date: Sat, 9 Mar 2013 18:55:22 +1100 Subject: [PATCH 0430/1472] Preserve style suffixes by inserting ForPowerline before any '-'s --- font/fontpatcher.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/font/fontpatcher.py b/font/fontpatcher.py index 2f2e4138..61663be1 100755 --- a/font/fontpatcher.py +++ b/font/fontpatcher.py @@ -3,6 +3,7 @@ import argparse import sys +import re import os.path try: @@ -41,7 +42,10 @@ class FontPatcher(object): if self.rename_font: target_font.familyname += ' for Powerline' target_font.fullname += ' for Powerline' - target_font.fontname += 'ForPowerline' + fontname, style = re.match("^([^-]*)(?:(-.*))?$", target_font.fontname).groups() + target_font.fontname = fontname + 'ForPowerline' + if style is not None: + target_font.fontname += style target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) From c70d3b38b65153fceeec6b745673cef02ac9fca1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 25 Feb 2013 22:56:22 +0530 Subject: [PATCH 0431/1472] Add bzr support --- powerline/lib/vcs/__init__.py | 7 ++-- powerline/lib/vcs/bzr.py | 62 +++++++++++++++++++++++++++++++++++ tests/install.sh | 2 +- tests/test_lib.py | 30 +++++++++++++++-- 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 powerline/lib/vcs/bzr.py diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 8e583da4..14c5cc72 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -3,8 +3,11 @@ import os from powerline.lib.memoize import memoize -vcs_props = (('git', '.git', os.path.exists), - ('mercurial', '.hg', os.path.isdir)) +vcs_props = ( + ('git', '.git', os.path.exists), + ('mercurial', '.hg', os.path.isdir), + ('bzr', '.bzr', os.path.isdir), +) def generate_directories(path): diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py new file mode 100644 index 00000000..1843001f --- /dev/null +++ b/powerline/lib/vcs/bzr.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import, unicode_literals, division, print_function + +import sys +from io import StringIO + +from bzrlib import (branch, workingtree, status, library_state, trace, ui) + + +class CoerceIO(StringIO): + def write(self, arg): + if isinstance(arg, bytes): + arg = arg.decode('utf-8', 'replace') + return super(CoerceIO, self).write(arg) + + +class Repository(object): + def __init__(self, directory): + if isinstance(directory, bytes): + directory = directory.decode(sys.getfilesystemencoding() or sys.getdefaultencoding() or 'utf-8') + self.directory = directory + self.state = library_state.BzrLibraryState(ui=ui.SilentUIFactory, trace=trace.DefaultConfig()) + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :"D?": dirty (tracked modified files: added, removed, deleted, modified), + :"?U": untracked-dirty (added, but not tracked files) + :None: clean (status is empty) + + With file argument: returns status of this file: The status codes are + those returned by bzr status -S + ''' + try: + return self._status(path) + except: + pass + + def _status(self, path): + buf = CoerceIO() + w = workingtree.WorkingTree.open(self.directory) + status.show_tree_status(w, specific_files=[path] if path else None, to_file=buf, short=True) + raw = buf.getvalue() + if not raw.strip(): + return + if path: + return raw[:2] + dirtied = untracked = ' ' + for line in raw.splitlines(): + if len(line) > 1 and line[1] in 'ACDMRIN': + dirtied = 'D' + elif line and line[0] == '?': + untracked = 'U' + return dirtied + untracked + + def branch(self): + try: + b = branch.Branch.open(self.directory) + return b.nick or None + except: + pass diff --git a/tests/install.sh b/tests/install.sh index ddce7e74..9a5b1cd2 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -2,7 +2,7 @@ pip install . if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 - pip install mercurial + pip install mercurial bzr if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 pip install unittest2 diff --git a/tests/test_lib.py b/tests/test_lib.py index 46dd03b3..c475808b 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -39,7 +39,7 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') -use_mercurial = sys.version_info < (3, 0) +use_mercurial = use_bzr = sys.version_info < (3, 0) class TestVCS(TestCase): @@ -78,6 +78,27 @@ class TestVCS(TestCase): self.assertEqual(repo.status('file'), 'A') os.remove(os.path.join('hg_repo', 'file')) + if use_bzr: + def test_bzr(self): + repo = guess(path='bzr_repo') + self.assertNotEqual(repo, None, 'No bzr repo found. Do you have bzr installed?') + self.assertEqual(repo.branch(), 'test_powerline') + self.assertEqual(repo.status(), None) + with open(os.path.join('bzr_repo', 'file'), 'w') as f: + f.write('abc') + self.assertEqual(repo.status(), ' U') + self.assertEqual(repo.status('file'), '? ') + call(['bzr', 'add', '.'], cwd='bzr_repo', stdout=PIPE) + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), '+N') + call(['bzr', 'commit', '-m', 'initial commit'], cwd='bzr_repo', stdout=PIPE, stderr=PIPE) + self.assertEqual(repo.status(), None) + with open(os.path.join('bzr_repo', 'file'), 'w') as f: + f.write('def') + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), ' M') + self.assertEqual(repo.status('notexist'), None) + os.remove(os.path.join('bzr_repo', 'file')) old_HGRCPATH = None old_cwd = None @@ -99,12 +120,17 @@ def setUpModule(): with open(os.path.join('hg_repo', '.hg', 'hgrc'), 'w') as hgrc: hgrc.write('[ui]\n') hgrc.write('username = Foo \n') + if use_bzr: + call(['bzr', 'init', '--quiet', 'bzr_repo']) + call(['bzr', 'config', 'email=Foo '], cwd='bzr_repo') + call(['bzr', 'config', 'nickname=test_powerline'], cwd='bzr_repo') + call(['bzr', 'config', 'create_signatures=0'], cwd='bzr_repo') def tearDownModule(): global old_cwd global old_HGRCPATH - for repo_dir in ['git_repo'] + (['hg_repo'] if use_mercurial else []): + for repo_dir in (['git_repo'] + (['hg_repo'] if use_mercurial else []) + (['bzr_repo'] if use_bzr else [])): for root, dirs, files in list(os.walk(repo_dir, topdown=False)): for file in files: os.remove(os.path.join(root, file)) From 707cf2780c3dae052403a4425e3da634932b7a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Mar 2013 07:13:57 +0100 Subject: [PATCH 0432/1472] Cleanup .gitignore file --- .gitignore | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 5152e47d..c60af16c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,9 @@ +tags + *.py[co] __pycache__ -env -# Packages *.egg *.egg-info dist build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.tox -junit-*.xml -.coverage -coverage.xml - -# Translations -*.mo -*.pot - -# SQLite databases -*.db From 59ef974b293b4d3b86fd97802ae17ac90c92eb41 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Mar 2013 21:02:52 +0400 Subject: [PATCH 0433/1472] Fix set statusline location, readd fix from #243 Ref #303 --- powerline/bindings/vim/plugin/powerline.vim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 1d79c726..c692e294 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -76,10 +76,6 @@ function! PowerlineNew() call map(range(1, winnr('$')), 's:GetWinID(v:val)') endfunction -" Is immediately changed when PowerlineNew() function is run. Good for global -" value. -set statusline=%!PowerlineNew() - function! PowerlineRegisterCachePurgerEvent(event) exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent' augroup Powerline @@ -87,7 +83,13 @@ function! PowerlineRegisterCachePurgerEvent(event) augroup END endfunction +" Is immediately changed when PowerlineNew() function is run. Good for global +" value. +set statusline=%!PowerlineNew() +call PowerlineNew() + augroup Powerline autocmd! - autocmd ColorScheme * exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' + autocmd ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' + autocmd VimEnter * :redrawstatus! augroup END From 40fe38f64104799722d5caa18e29dedd2985c5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Mar 2013 07:52:24 +0100 Subject: [PATCH 0434/1472] Move argparser to powerline.shell so it can be re-used --- powerline/shell.py | 19 ++++++++++++++++++- scripts/powerline | 20 +++----------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/powerline/shell.py b/powerline/shell.py index d394b7ec..713a6575 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from powerline import Powerline -from powerline.lib import mergedicts +from powerline.lib import mergedicts, parsedotval def mergeargs(argvalue): @@ -39,3 +39,20 @@ class ShellPowerline(Powerline): return [self.args.config_path] else: return super(ShellPowerline, self).get_config_paths() + + +def get_argparser(parser=None, *args, **kwargs): + if not parser: + import argparse + parser = argparse.ArgumentParser + p = parser(*args, **kwargs) + p.add_argument('ext', nargs=1) + p.add_argument('side', nargs='?', choices=('left', 'right')) + p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) + p.add_argument('-w', '--width', type=int) + p.add_argument('--last_exit_code', metavar='INT', type=int) + p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) + p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', type=parsedotval, action='append') + p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', type=parsedotval, action='append') + p.add_argument('-p', '--config_path', metavar='PATH') + return p diff --git a/scripts/powerline b/scripts/powerline index f43b7bab..01a74892 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -1,31 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- '''Powerline prompt and statusline script.''' -import argparse import sys -import json -from powerline.lib import parsedotval try: - from powerline.shell import ShellPowerline + from powerline.shell import ShellPowerline, get_argparser except ImportError: import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from powerline.shell import ShellPowerline # NOQA - -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('ext', nargs=1) -parser.add_argument('side', nargs='?', choices=('left', 'right')) -parser.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) -parser.add_argument('-w', '--width', type=int) -parser.add_argument('--last_exit_code', metavar='INT', type=int) -parser.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) -parser.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', type=parsedotval, action='append') -parser.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', type=parsedotval, action='append') -parser.add_argument('-p', '--config_path', metavar='PATH') + from powerline.shell import ShellPowerline, get_argparser # NOQA if __name__ == '__main__': - args = parser.parse_args() + args = get_argparser(description=__doc__).parse_args() powerline = ShellPowerline(args) rendered = powerline.renderer.render(width=args.width, side=args.side) try: From 6748701fcbbf37ba1afeb143bbcbe870bd45f120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Mar 2013 08:11:25 +0100 Subject: [PATCH 0435/1472] Add vim modelines to all Python files Added with `sed -i '2i# vim:fenc=utf-8:noet' `find -name '*.py'`` and fixed in a couple of files without the UTF-8 encoding on top. Ref #314 --- docs/source/conf.py | 1 + font/fontpatcher.py | 1 + powerline/__init__.py | 1 + powerline/bindings/ipython/post_0_11.py | 1 + powerline/bindings/ipython/pre_0_11.py | 1 + powerline/bindings/qtile/widget.py | 1 + powerline/bindings/vim/__init__.py | 1 + powerline/bindings/zsh/__init__.py | 1 + powerline/colorscheme.py | 1 + powerline/ipython.py | 1 + powerline/lib/__init__.py | 1 + powerline/lib/humanize_bytes.py | 1 + powerline/lib/memoize.py | 1 + powerline/lib/url.py | 1 + powerline/lib/vcs/__init__.py | 1 + powerline/lib/vcs/bzr.py | 1 + powerline/lib/vcs/git.py | 1 + powerline/lib/vcs/mercurial.py | 1 + powerline/matcher.py | 1 + powerline/matchers/vim.py | 1 + powerline/renderer.py | 1 + powerline/renderers/bash_prompt.py | 1 + powerline/renderers/ipython.py | 1 + powerline/renderers/pango_markup.py | 1 + powerline/renderers/shell.py | 1 + powerline/renderers/tmux.py | 1 + powerline/renderers/vim.py | 1 + powerline/renderers/zsh_prompt.py | 1 + powerline/segment.py | 1 + powerline/segments/common.py | 1 + powerline/segments/shell.py | 1 + powerline/segments/vim.py | 1 + powerline/shell.py | 1 + powerline/theme.py | 1 + powerline/vim.py | 1 + setup.py | 1 + tests/__init__.py | 1 + tests/lib/__init__.py | 1 + tests/path/vim.py | 1 + tests/test_configuration.py | 1 + tests/test_lib.py | 1 + tests/test_segments.py | 1 + tests/vim.py | 1 + 43 files changed, 43 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index bb86ae6f..3d9755d0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet import os import sys diff --git a/font/fontpatcher.py b/font/fontpatcher.py index b0869843..327fd406 100755 --- a/font/fontpatcher.py +++ b/font/fontpatcher.py @@ -1,5 +1,6 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet import argparse import sys diff --git a/powerline/__init__.py b/powerline/__init__.py index 5736632b..e92af7c5 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import import json diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index f1b8729d..4ff79fb7 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from powerline.ipython import IpythonPowerline from IPython.core.prompts import PromptManager diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 0943025a..b47205a9 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from powerline.ipython import IpythonPowerline from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index dc4132aa..5a8c98a2 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from libqtile import bar from libqtile.widget import base diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 13af748c..5258ea05 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet try: import vim diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 75f7f309..87e32970 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet import zsh from powerline.shell import ShellPowerline from powerline.lib import parsedotval diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 42fbb60d..484cf7e9 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from copy import copy diff --git a/powerline/ipython.py b/powerline/ipython.py index 498ca42b..62b296d5 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline import Powerline from powerline.lib import mergedicts diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 54b9e50d..b0a492c6 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from functools import wraps import json diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index d69726fa..5c4c4ff5 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from math import log unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2])) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 016e36e5..df705bd9 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from functools import wraps import time diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 41a40938..8164a981 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet def urllib_read(url): diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 14c5cc72..d74ef5de 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from __future__ import absolute_import import os from powerline.lib.memoize import memoize diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index 1843001f..a35924ad 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from __future__ import absolute_import, unicode_literals, division, print_function import sys diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 2c92c45a..be18c266 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet try: import pygit2 as git diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index bc2f7add..71ce9243 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from __future__ import absolute_import from mercurial import hg, ui, match diff --git a/powerline/matcher.py b/powerline/matcher.py index aa70d0e1..571ee6ec 100644 --- a/powerline/matcher.py +++ b/powerline/matcher.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import import sys diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index 195f5e40..a908f590 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import diff --git a/powerline/renderer.py b/powerline/renderer.py index 6dcf2976..38a33663 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.theme import Theme diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index 875527b8..b64194a7 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index f00573af..17fcef8c 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index f8e3ed88..edb4fb50 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 5c969756..fb995210 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 3b122dcf..ab5c1f34 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index fdc2a1ee..7c190e23 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 52c85dc5..0d777e02 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.renderers.shell import ShellRenderer diff --git a/powerline/segment.py b/powerline/segment.py index e93e0383..138e58b6 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import import sys diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 65e7c7a0..b42f11c3 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet import os import sys diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 2473bb8f..8cbf881d 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.theme import requires_segment_info diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f3929f1e..f056cedc 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import diff --git a/powerline/shell.py b/powerline/shell.py index 713a6575..c0ea10cc 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline import Powerline from powerline.lib import mergedicts, parsedotval diff --git a/powerline/theme.py b/powerline/theme.py index 014c5eef..60e6db17 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from copy import copy diff --git a/powerline/vim.py b/powerline/vim.py index 2e234d97..8e33576a 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from __future__ import absolute_import diff --git a/setup.py b/setup.py index 17ea49fc..5c526325 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet import os import sys diff --git a/tests/__init__.py b/tests/__init__.py index b6e78fe6..bf960b9f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet import sys if sys.version_info < (2, 7): from unittest2 import TestCase, main # NOQA diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index e7df12de..de22d31d 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet import imp import sys import os diff --git a/tests/path/vim.py b/tests/path/vim.py index 38df3bd0..8b3b218a 100644 --- a/tests/path/vim.py +++ b/tests/path/vim.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from tests import vim diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 529b48b4..d60779c6 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet '''Dynamic configuration files tests.''' diff --git a/tests/test_lib.py b/tests/test_lib.py index c475808b..416edefe 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet from powerline.lib import mergedicts, underscore_to_camelcase, add_divider_highlight_group, humanize_bytes from powerline.lib.vcs import guess from subprocess import call, PIPE diff --git a/tests/test_segments.py b/tests/test_segments.py index d3edf072..9d9d84ac 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# vim:fenc=utf-8:noet from powerline.segments import shell, common import tests.vim as vim_module diff --git a/tests/vim.py b/tests/vim.py index c49a0da5..89b17aec 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -1,3 +1,4 @@ +# vim:fenc=utf-8:noet _log = [] _g = {} _window = 0 From b31c987cc85d1ae7dc132851ef0957f5379cd272 Mon Sep 17 00:00:00 2001 From: Peter Fern Date: Sat, 9 Mar 2013 20:05:31 +1100 Subject: [PATCH 0436/1472] Add XDG_CONFIG_DIRS to config_paths for system-wide install Commonly this will be `/etc/xdg/powerline` --- powerline/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index e92af7c5..7a2eb5c8 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -78,8 +78,13 @@ class Powerline(object): ''' config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) config_path = os.path.join(config_home, 'powerline') + config_paths = [config_path] + config_dirs = os.environ.get('XDG_CONFIG_DIRS', None) + if config_dirs is not None: + config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')]) plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') - return [config_path, plugin_path] + config_paths.append(plugin_path) + return config_paths def load_theme_config(self, name): '''Get theme configuration. From 111eaa27e81ecc46a77599124dcffbd76ca7a2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 11 Mar 2013 10:40:09 +0100 Subject: [PATCH 0437/1472] Combine vim modelines and Python encoding declarations Ref #314 --- docs/source/conf.py | 3 +-- font/fontpatcher.py | 3 +-- powerline/__init__.py | 3 +-- powerline/bindings/ipython/post_0_11.py | 2 +- powerline/bindings/ipython/pre_0_11.py | 2 +- powerline/bindings/qtile/widget.py | 3 +-- powerline/bindings/vim/__init__.py | 3 +-- powerline/bindings/zsh/__init__.py | 2 +- powerline/colorscheme.py | 3 +-- powerline/ipython.py | 3 +-- powerline/lib/__init__.py | 2 +- powerline/lib/humanize_bytes.py | 3 +-- powerline/lib/memoize.py | 3 +-- powerline/lib/url.py | 3 +-- powerline/lib/vcs/__init__.py | 2 +- powerline/lib/vcs/bzr.py | 2 +- powerline/lib/vcs/git.py | 2 +- powerline/lib/vcs/mercurial.py | 2 +- powerline/matcher.py | 3 +-- powerline/matchers/vim.py | 3 +-- powerline/renderer.py | 3 +-- powerline/renderers/bash_prompt.py | 3 +-- powerline/renderers/ipython.py | 3 +-- powerline/renderers/pango_markup.py | 3 +-- powerline/renderers/shell.py | 3 +-- powerline/renderers/tmux.py | 3 +-- powerline/renderers/vim.py | 3 +-- powerline/renderers/zsh_prompt.py | 3 +-- powerline/segment.py | 3 +-- powerline/segments/common.py | 3 +-- powerline/segments/shell.py | 3 +-- powerline/segments/vim.py | 3 +-- powerline/shell.py | 3 +-- powerline/theme.py | 3 +-- powerline/vim.py | 3 +-- setup.py | 3 +-- tests/__init__.py | 2 +- tests/lib/__init__.py | 2 +- tests/path/vim.py | 2 +- tests/test_configuration.py | 3 +-- tests/test_lib.py | 2 +- tests/test_segments.py | 3 +-- tests/vim.py | 2 +- 43 files changed, 43 insertions(+), 73 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3d9755d0..c2af72cb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import os import sys diff --git a/font/fontpatcher.py b/font/fontpatcher.py index fff9a2a6..e2bbf2a0 100755 --- a/font/fontpatcher.py +++ b/font/fontpatcher.py @@ -1,6 +1,5 @@ #!/usr/bin/env python2 -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import argparse import sys diff --git a/powerline/__init__.py b/powerline/__init__.py index 7a2eb5c8..65d132ff 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import import json diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 4ff79fb7..c0c40ce1 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.ipython import IpythonPowerline from IPython.core.prompts import PromptManager diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index b47205a9..098ddb6b 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.ipython import IpythonPowerline from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index 5a8c98a2..71d8d3fa 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from libqtile import bar from libqtile.widget import base diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 5258ea05..f9741163 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet try: import vim diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 87e32970..8be9f6f8 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import zsh from powerline.shell import ShellPowerline from powerline.lib import parsedotval diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 484cf7e9..c5d09f6f 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from copy import copy diff --git a/powerline/ipython.py b/powerline/ipython.py index 62b296d5..e1fc4149 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline import Powerline from powerline.lib import mergedicts diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index b0a492c6..549d7455 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from functools import wraps import json diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index 5c4c4ff5..769c7d13 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from math import log unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2])) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index df705bd9..194cf69e 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from functools import wraps import time diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 8164a981..ef46df70 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet def urllib_read(url): diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index d74ef5de..2bbc9bdf 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import import os from powerline.lib.memoize import memoize diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index a35924ad..feb7844c 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import, unicode_literals, division, print_function import sys diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index be18c266..ec99f0c6 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet try: import pygit2 as git diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index 71ce9243..246bdec2 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import from mercurial import hg, ui, match diff --git a/powerline/matcher.py b/powerline/matcher.py index 571ee6ec..5578d282 100644 --- a/powerline/matcher.py +++ b/powerline/matcher.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import import sys diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index a908f590..fd1db4b6 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import diff --git a/powerline/renderer.py b/powerline/renderer.py index 38a33663..dbaa04fc 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.theme import Theme diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index b64194a7..b9f911ad 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 17fcef8c..d1e4c7bd 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index edb4fb50..54e78868 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index fb995210..b2ad0197 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index ab5c1f34..57dd3cd8 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 7c190e23..90047751 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 0d777e02..f66f7359 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.renderers.shell import ShellRenderer diff --git a/powerline/segment.py b/powerline/segment.py index 138e58b6..998d9939 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import import sys diff --git a/powerline/segments/common.py b/powerline/segments/common.py index b42f11c3..5511a8cd 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import os import sys diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 8cbf881d..cd418361 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.theme import requires_segment_info diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f056cedc..99e3c1a0 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import diff --git a/powerline/shell.py b/powerline/shell.py index c0ea10cc..f84157ae 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline import Powerline from powerline.lib import mergedicts, parsedotval diff --git a/powerline/theme.py b/powerline/theme.py index 60e6db17..5c2b0c4a 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from copy import copy diff --git a/powerline/vim.py b/powerline/vim.py index 8e33576a..6fda5575 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from __future__ import absolute_import diff --git a/setup.py b/setup.py index 5c526325..46c843a8 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import os import sys diff --git a/tests/__init__.py b/tests/__init__.py index bf960b9f..13b41550 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import sys if sys.version_info < (2, 7): from unittest2 import TestCase, main # NOQA diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index de22d31d..872be033 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet import imp import sys import os diff --git a/tests/path/vim.py b/tests/path/vim.py index 8b3b218a..899678e5 100644 --- a/tests/path/vim.py +++ b/tests/path/vim.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from tests import vim diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d60779c6..5851ec3f 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet '''Dynamic configuration files tests.''' diff --git a/tests/test_lib.py b/tests/test_lib.py index 416edefe..8dac5c5d 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.lib import mergedicts, underscore_to_camelcase, add_divider_highlight_group, humanize_bytes from powerline.lib.vcs import guess from subprocess import call, PIPE diff --git a/tests/test_segments.py b/tests/test_segments.py index 9d9d84ac..d41c145e 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet from powerline.segments import shell, common import tests.vim as vim_module diff --git a/tests/vim.py b/tests/vim.py index 89b17aec..6253956f 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -1,4 +1,4 @@ -# vim:fenc=utf-8:noet +# vim:fileencoding=utf-8:noet _log = [] _g = {} _window = 0 From a6d48232b088384f1773fd93ec8137ff7268e263 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 15:13:23 +0400 Subject: [PATCH 0438/1472] Add python_test, more exclusions Fixes #289 --- packages/gentoo/app-misc/powerline/Manifest | 2 +- .../app-misc/powerline/powerline-9999.ebuild | 26 ++++++++++++++++--- setup.py | 2 +- tests/test.sh | 7 ++--- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index 81dadd26..e8479c3f 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 3436 SHA256 9a09866972c18adb5bc3b9394ae8ec98e305aef27e7558803dd89f499829fa48 SHA512 620363137d24ca57457d97fabb95108e515aec2ce986d7dc6abb4b5c68ef2866e55a4024005efc91f7f84fcac58b79c9445af93e3bfff3d1c12aba462c8e8a61 WHIRLPOOL 227ab590e91c97fbe9b720c882d422ee7c05d0637c174cde48f4cd1f683653f14e67914abdb9ba03406d5090d6a56a96e546c501fb1b10961ddaa4005c04b686 +EBUILD powerline-9999.ebuild 3863 SHA256 cc90b1ce3cb2bab7875809a928e02950e3490810e58e6f73c1f21087e36877b7 SHA512 276f4edc5eae5cd3ce82eb54994559c269ea29f07125422787eb00bc2af2ffef67d59aac87c695827c048ad97784d66dedab6581c4f5e51fd3d3111b1dada08e WHIRLPOOL ea94e63f150a9035d05c0fd8403681b0b4d52c962ba22fdd361f7ca20cd986cb4b7179bcff26f98869e51b1b55a4c228c3b2a5146badb6c1ebd67bd9bf2368f9 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 6a7e05aa..b3554855 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -19,7 +19,7 @@ SRC_URI="" LICENSE="CC-Attribution-ShareAlike-3.0" SLOT="0" KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" -IUSE="vim zsh doc awesome tmux bash ipython" +IUSE="vim zsh doc awesome tmux bash ipython test git" #if LIVE SRC_URI= @@ -30,8 +30,24 @@ S="${WORKDIR}/${PN}" RDEPEND=" vim? ( || ( app-editors/vim[python] app-editors/gvim[python] ) ) - awesome? ( >=x11-wm/awesome-3.5 )" -DEPEND="doc? ( dev-python/sphinx dev-python/docutils )" + awesome? ( >=x11-wm/awesome-3.5 ) + git? ( || ( >=dev-vcs/git-1.7.2 >=dev-python/pygit2-0.17 ) ) +" +DEPEND=" + doc? ( dev-python/sphinx dev-python/docutils ) + test? ( + python_targets_python2_6? ( virtual/python-unittest2 ) + || ( >=dev-vcs/git-1.7.2 >=dev-python/pygit2-0.17 ) + python_targets_python2_6? ( + dev-vcs/mercurial + dev-vcs/bzr + ) + python_targets_python2_7? ( + dev-vcs/mercurial + dev-vcs/bzr + ) + ) +" FONT_SUFFIX="otf" FONT_S="${S}/font" @@ -40,6 +56,10 @@ FONT_CONF=( "${FONT_S}/10-powerline-symbols.conf" ) +python_test() { + PYTHON="${PYTHON}" tests/test.sh || die "Tests fail with ${EPYTHON}" +} + src_compile() { distutils-r1_src_compile if use doc ; then diff --git a/setup.py b/setup.py index 46c843a8..6c5b1b9e 100755 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( 'scripts/powerline', ], keywords='', - packages=find_packages(exclude=('tests',)), + packages=find_packages(exclude=('tests', 'tests.*')), include_package_data=True, zip_safe=False, install_requires=[], diff --git a/tests/test.sh b/tests/test.sh index 7a018357..5e7cdaf5 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,12 +1,13 @@ #!/bin/sh -if python -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then +: ${PYTHON:=python} +if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then # Python 2.6 export PYTHONPATH="${PYTHONPATH}:`realpath .`" for file in tests/test_*.py ; do - if ! python $file ; then + if ! ${PYTHON} $file ; then exit 1 fi done else - python setup.py test + ${PYTHON} setup.py test fi From 4374e1f71c21962e692915af4db6eb5d4506b1ef Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 15:34:27 +0400 Subject: [PATCH 0439/1472] Fix git/hg repo tests --- tests/test_lib.py | 56 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 8dac5c5d..e48730d1 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -45,93 +45,99 @@ use_mercurial = use_bzr = sys.version_info < (3, 0) class TestVCS(TestCase): def test_git(self): - repo = guess(path='git_repo') + repo = guess(path=GIT_REPO) self.assertNotEqual(repo, None) self.assertEqual(repo.branch(), 'master') self.assertEqual(repo.status(), ' ') self.assertEqual(repo.status('file'), None) - with open(os.path.join('git_repo', 'file'), 'w') as f: + with open(os.path.join(GIT_REPO, 'file'), 'w') as f: f.write('abc') f.flush() self.assertEqual(repo.status(), ' U') self.assertEqual(repo.status('file'), '??') - call(['git', 'add', '.'], cwd='git_repo') + call(['git', 'add', '.'], cwd=GIT_REPO) self.assertEqual(repo.status(), ' I ') self.assertEqual(repo.status('file'), 'A ') f.write('def') f.flush() self.assertEqual(repo.status(), 'DI ') self.assertEqual(repo.status('file'), 'AM') - os.remove(os.path.join('git_repo', 'file')) + os.remove(os.path.join(GIT_REPO, 'file')) if use_mercurial: def test_mercurial(self): - repo = guess(path='hg_repo') + repo = guess(path=HG_REPO) self.assertNotEqual(repo, None) self.assertEqual(repo.branch(), 'default') - with open(os.path.join('hg_repo', 'file'), 'w') as f: + with open(os.path.join(HG_REPO, 'file'), 'w') as f: f.write('abc') f.flush() self.assertEqual(repo.status(), ' U') self.assertEqual(repo.status('file'), 'U') - call(['hg', 'add', '.'], cwd='hg_repo', stdout=PIPE) + call(['hg', 'add', '.'], cwd=HG_REPO, stdout=PIPE) self.assertEqual(repo.status(), 'D ') self.assertEqual(repo.status('file'), 'A') - os.remove(os.path.join('hg_repo', 'file')) + os.remove(os.path.join(HG_REPO, 'file')) if use_bzr: def test_bzr(self): - repo = guess(path='bzr_repo') + repo = guess(path=BZR_REPO) self.assertNotEqual(repo, None, 'No bzr repo found. Do you have bzr installed?') self.assertEqual(repo.branch(), 'test_powerline') self.assertEqual(repo.status(), None) - with open(os.path.join('bzr_repo', 'file'), 'w') as f: + with open(os.path.join(BZR_REPO, 'file'), 'w') as f: f.write('abc') self.assertEqual(repo.status(), ' U') self.assertEqual(repo.status('file'), '? ') - call(['bzr', 'add', '.'], cwd='bzr_repo', stdout=PIPE) + call(['bzr', 'add', '.'], cwd=BZR_REPO, stdout=PIPE) self.assertEqual(repo.status(), 'D ') self.assertEqual(repo.status('file'), '+N') - call(['bzr', 'commit', '-m', 'initial commit'], cwd='bzr_repo', stdout=PIPE, stderr=PIPE) + call(['bzr', 'commit', '-m', 'initial commit'], cwd=BZR_REPO, stdout=PIPE, stderr=PIPE) self.assertEqual(repo.status(), None) - with open(os.path.join('bzr_repo', 'file'), 'w') as f: + with open(os.path.join(BZR_REPO, 'file'), 'w') as f: f.write('def') self.assertEqual(repo.status(), 'D ') self.assertEqual(repo.status('file'), ' M') self.assertEqual(repo.status('notexist'), None) - os.remove(os.path.join('bzr_repo', 'file')) + os.remove(os.path.join(BZR_REPO, 'file')) old_HGRCPATH = None old_cwd = None +GIT_REPO = 'git_repo' + os.environ.get('PYTHON', '') +HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') +BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') + + def setUpModule(): global old_cwd global old_HGRCPATH old_cwd = os.getcwd() os.chdir(os.path.dirname(__file__)) - call(['git', 'init', '--quiet', 'git_repo']) - call(['git', 'config', '--local', 'user.name', 'Foo'], cwd='git_repo') - call(['git', 'config', '--local', 'user.email', 'bar@example.org'], cwd='git_repo') - call(['git', 'commit', '--allow-empty', '--message', 'Initial commit', '--quiet'], cwd='git_repo') + call(['git', 'init', '--quiet', GIT_REPO]) + assert os.path.isdir(GIT_REPO) + call(['git', 'config', '--local', 'user.name', 'Foo'], cwd=GIT_REPO) + call(['git', 'config', '--local', 'user.email', 'bar@example.org'], cwd=GIT_REPO) + call(['git', 'commit', '--allow-empty', '--message', 'Initial commit', '--quiet'], cwd=GIT_REPO) if use_mercurial: old_HGRCPATH = os.environ.get('HGRCPATH') os.environ['HGRCPATH'] = '' - call(['hg', 'init', 'hg_repo']) - with open(os.path.join('hg_repo', '.hg', 'hgrc'), 'w') as hgrc: + call(['hg', 'init', HG_REPO]) + with open(os.path.join(HG_REPO, '.hg', 'hgrc'), 'w') as hgrc: hgrc.write('[ui]\n') hgrc.write('username = Foo \n') if use_bzr: - call(['bzr', 'init', '--quiet', 'bzr_repo']) - call(['bzr', 'config', 'email=Foo '], cwd='bzr_repo') - call(['bzr', 'config', 'nickname=test_powerline'], cwd='bzr_repo') - call(['bzr', 'config', 'create_signatures=0'], cwd='bzr_repo') + call(['bzr', 'init', '--quiet', BZR_REPO]) + call(['bzr', 'config', 'email=Foo '], cwd=BZR_REPO) + call(['bzr', 'config', 'nickname=test_powerline'], cwd=BZR_REPO) + call(['bzr', 'config', 'create_signatures=0'], cwd=BZR_REPO) def tearDownModule(): global old_cwd global old_HGRCPATH - for repo_dir in (['git_repo'] + (['hg_repo'] if use_mercurial else []) + (['bzr_repo'] if use_bzr else [])): + for repo_dir in [GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): for root, dirs, files in list(os.walk(repo_dir, topdown=False)): for file in files: os.remove(os.path.join(root, file)) From 6c7e454fd88c665447991a4f6fe1af2e8002ee55 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 15:37:25 +0400 Subject: [PATCH 0440/1472] Fix license Fixes #299 --- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index e8479c3f..44496eab 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 3863 SHA256 cc90b1ce3cb2bab7875809a928e02950e3490810e58e6f73c1f21087e36877b7 SHA512 276f4edc5eae5cd3ce82eb54994559c269ea29f07125422787eb00bc2af2ffef67d59aac87c695827c048ad97784d66dedab6581c4f5e51fd3d3111b1dada08e WHIRLPOOL ea94e63f150a9035d05c0fd8403681b0b4d52c962ba22fdd361f7ca20cd986cb4b7179bcff26f98869e51b1b55a4c228c3b2a5146badb6c1ebd67bd9bf2368f9 +EBUILD powerline-9999.ebuild 3837 SHA256 0d0cee43831ed7bc82f02bca8855d9ad31bbabdd352b9590e5c77bd4bc6cd920 SHA512 32b8e6b1707e46db90a35e709b325ce3561229fdd2037365afa76b50d9aa715e106a28f3a53bb9846c62f9d22a384f602a1904b88799445631a0efea2f376d9a WHIRLPOOL 5a8b0aa667a78907eb2991aed82c8b7c563823f75d10d7c0566b6130413e7249272fbc3445aa457870200423227a54263a82fde9ab2d3b6b59ed54f6250367fc diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index b3554855..565aec7f 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -16,7 +16,7 @@ DESCRIPTION="The ultimate statusline/prompt utility." HOMEPAGE="http://github.com/Lokaltog/powerline" SRC_URI="" -LICENSE="CC-Attribution-ShareAlike-3.0" +LICENSE="MIT" SLOT="0" KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" IUSE="vim zsh doc awesome tmux bash ipython test git" From fdab80cd000c0add36cdd3d7d44987a4cecc479f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 21:38:33 +0400 Subject: [PATCH 0441/1472] Add python-argparse dependency --- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index 44496eab..cdeed146 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 3837 SHA256 0d0cee43831ed7bc82f02bca8855d9ad31bbabdd352b9590e5c77bd4bc6cd920 SHA512 32b8e6b1707e46db90a35e709b325ce3561229fdd2037365afa76b50d9aa715e106a28f3a53bb9846c62f9d22a384f602a1904b88799445631a0efea2f376d9a WHIRLPOOL 5a8b0aa667a78907eb2991aed82c8b7c563823f75d10d7c0566b6130413e7249272fbc3445aa457870200423227a54263a82fde9ab2d3b6b59ed54f6250367fc +EBUILD powerline-9999.ebuild 3916 SHA256 754f8750aa5c6a455871d44c9478b81278b71318e591a13ef30f763ddb1fbda5 SHA512 48dd9d3ac737417b6a072397a4a50fefc04c0cb4a3c6249b5bf11ee201986f7b25a94b83e6901e8099ae3ea733dc0e650f5ce2234b5143bdbafd906af56916e7 WHIRLPOOL 803298b97adaeb2e3a6cd31c4bd12897373d7ce0722df22a991c684ecd5146fcd87addfc6e428374d924d6cb950dee6bbcf2ea0a262904cc4d04e3eac0b2dcb8 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 565aec7f..9153ca33 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -28,12 +28,17 @@ KEYWORDS= S="${WORKDIR}/${PN}" +COMMON_DEPEND=" + virtual/python-argparse +" RDEPEND=" + ${COMMON_DEPEND} vim? ( || ( app-editors/vim[python] app-editors/gvim[python] ) ) awesome? ( >=x11-wm/awesome-3.5 ) git? ( || ( >=dev-vcs/git-1.7.2 >=dev-python/pygit2-0.17 ) ) " DEPEND=" + ${COMMON_DEPEND} doc? ( dev-python/sphinx dev-python/docutils ) test? ( python_targets_python2_6? ( virtual/python-unittest2 ) From ae62aee529a6b5115c339cdb0fdf3b067c91512a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 19:11:10 +0400 Subject: [PATCH 0442/1472] Renamed term_24bit_colors to term_truecolor --- docs/source/configuration.rst | 4 ++-- docs/source/overview.rst | 4 ++-- powerline/__init__.py | 2 +- powerline/config_files/config.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 55cb28ad..05e10a53 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -113,9 +113,9 @@ Common configuration Common configuration is a subdictionary that is a value of ``common`` key in :file:`powerline/config.json` file. -.. _config-common-term_24bit_colors: +.. _config-common-term_truecolor: -``term_24bit_colors`` +``term_truecolor`` Defines whether to output cterm indices (8-bit) or RGB colors (24-bit) to the terminal emulator. See the :ref:`term-feature-support-matrix` for information on whether your terminal emulator supports 24-bit colors. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index a550f32e..494a14a0 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -31,8 +31,8 @@ custom symbols for developers. This requires that you either have a symbol font or a patched font on your system. Your terminal emulator must also support either patched fonts or fontconfig for Powerline to work properly. -You can also enable :ref:`24-bit color support -` if your terminal emulator supports it. +You can also enable :ref:`24-bit color support ` +if your terminal emulator supports it. .. table:: Application/terminal emulator feature support matrix :name: term-feature-support-matrix diff --git a/powerline/__init__.py b/powerline/__init__.py index 65d132ff..93e0194b 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -67,7 +67,7 @@ class Powerline(object): except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) - options = {'term_truecolor': common_config.get('term_24bit_colors', False)} + options = {'term_truecolor': common_config.get('term_truecolor', False)} self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) def get_config_paths(self): diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 4c8ad85f..3cb73855 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -1,6 +1,6 @@ { "common": { - "term_24bit_colors": false, + "term_truecolor": false, "dividers": { "left": { "hard": " ", From 657fcd1a99bf0edbabfc8ddac00eb0000efe02d7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 20:28:47 +0400 Subject: [PATCH 0443/1472] Make @window_cached use @wraps Otherwise documentation is not saved (hidden in decorated function only visible in the closure). --- powerline/segments/vim.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 99e3c1a0..1cccd888 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -12,6 +12,7 @@ from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info from powerline.lib import memoize, humanize_bytes, add_divider_highlight_group from powerline.lib.vcs import guess +from functools import wraps from collections import defaultdict vim_funcs = { @@ -87,6 +88,8 @@ def bufname(segment_info, **kwargs): def window_cached(func): cache = {} + @requires_segment_info + @wraps(func) def ret(segment_info, *args, **kwargs): window_id = segment_info['window_id'] if segment_info['mode'] == 'nc': @@ -95,8 +98,7 @@ def window_cached(func): r = func(*args, **kwargs) cache[window_id] = r return r - ret = requires_segment_info(ret) - ret.__name__ = func.__name__ + return ret From 551a2685f443984d3e059e377a4a67855f0f0b66 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Mar 2013 13:30:24 +0400 Subject: [PATCH 0444/1472] Move add_local_theme and get_theme to vim renderer In any case for all other extensions they are useless. (Except for ipython, but it is TODO and I am not going to use matchers this way, simple dictionary lookup is better in this case.) --- powerline/__init__.py | 7 ++++--- powerline/renderer.py | 14 +------------- powerline/renderers/vim.py | 16 ++++++++++++++++ powerline/vim.py | 10 +++++----- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 93e0194b..c5dbc19f 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -56,7 +56,7 @@ class Powerline(object): 'common_config': common_config, 'segment_info': self.get_segment_info(), } - local_themes = self.get_local_themes(ext_config.get('local_themes', {})) + local_themes = self.get_local_themes(ext_config.get('local_themes')) # Load and initialize extension renderer renderer_module_name = renderer_module or ext @@ -125,14 +125,15 @@ class Powerline(object): required. :param dict local_themes: - Usually accepts ``{matcher_name : theme_name}``. + Usually accepts ``{matcher_name : theme_name}``. May also receive + None in case there is no local_themes configuration. :return: anything accepted by ``self.renderer.get_theme`` and processable by ``self.renderer.add_local_theme``. Renderer module is determined by ``__init__`` arguments, refer to its documentation. ''' - return {} + return None @staticmethod def get_segment_info(): diff --git a/powerline/renderer.py b/powerline/renderer.py index dbaa04fc..e91be682 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -18,20 +18,8 @@ class Renderer(object): self.theme_kwargs = theme_kwargs self.colorscheme = colorscheme - def add_local_theme(self, matcher, theme): - if matcher in self.local_themes: - raise KeyError('There is already a local theme with given matcher') - self.local_themes[matcher] = theme - def get_theme(self, matcher_info): - for matcher in self.local_themes.keys(): - if matcher(matcher_info): - match = self.local_themes[matcher] - if 'config' in match: - match['theme'] = Theme(theme_config=match.pop('config'), top_theme_config=self.theme_config, **self.theme_kwargs) - return match['theme'] - else: - return self.theme + return self.theme def get_highlighting(self, segment, mode): segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 90047751..5099e4dc 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -5,6 +5,7 @@ from __future__ import absolute_import from powerline.bindings.vim import vim_get_func from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +from powerline.theme import Theme import vim @@ -24,6 +25,21 @@ class VimRenderer(Renderer): super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} + def add_local_theme(self, matcher, theme): + if matcher in self.local_themes: + raise KeyError('There is already a local theme with given matcher') + self.local_themes[matcher] = theme + + def get_theme(self, matcher_info): + for matcher in self.local_themes.keys(): + if matcher(matcher_info): + match = self.local_themes[matcher] + if 'config' in match: + match['theme'] = Theme(theme_config=match.pop('config'), top_theme_config=self.theme_config, **self.theme_kwargs) + return match['theme'] + else: + return self.theme + def render(self, window_id, winidx, current): '''Render all segments. diff --git a/powerline/vim.py b/powerline/vim.py index 6fda5575..b0610baf 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -65,12 +65,12 @@ class VimPowerline(Powerline): 'g:powerline_theme_overrides__' + name) def get_local_themes(self, local_themes): + if not local_themes: + return {} + self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) - r = {} - for key, local_theme_name in local_themes.items(): - key = self.get_matcher(key) - r[key] = {'config': self.load_theme_config(local_theme_name)} - return r + return dict(((self.get_matcher(key), {'config': self.load_theme_config(val)}) + for key, val in local_themes.items())) def get_config_paths(self): if vim_exists('g:powerline_config_path'): From 7fca338315c705884407cde75261b6c24611684e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Mar 2013 15:26:37 +0400 Subject: [PATCH 0445/1472] =?UTF-8?q?Remove=20unused=20=E2=80=9Ccolors?= =?UTF-8?q?=E2=80=9D=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerline/config_files/config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 3cb73855..f9cf67be 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -11,8 +11,7 @@ "soft": " " } }, - "spaces": 1, - "colors": "default" + "spaces": 1 }, "ext": { "ipython": { From 9e8fab3312f6e1e929de22748446e4783713054a Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 8 Mar 2013 21:09:44 +0400 Subject: [PATCH 0446/1472] Add highlight group data for powerline-lint --- powerline/segments/common.py | 26 ++++++++++++++++++++++++++ powerline/segments/shell.py | 10 ++++++++-- powerline/segments/vim.py | 23 ++++++++++++++++++++--- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5511a8cd..df931ca8 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -26,6 +26,8 @@ def user(): '''Return the current user. Highlights the user with the ``superuser`` if the effective user ID is 0. + + Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups. ''' user = os.environ.get('USER') try: @@ -56,6 +58,11 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): shorten parent directory names to this length (e.g. :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) :param int dir_limit_depth: limit directory depth to this number (e.g. :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) + + + Divider highlight group used: ``cwd:divider``. + + Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. ''' import re try: @@ -98,6 +105,10 @@ def date(format='%Y-%m-%d', istime=False): :param str format: strftime-style date format string + + Divider highlight group used: ``time:divider``. + + Highlight groups used: ``time`` or ``date``. ''' return [{ 'contents': datetime.now().strftime(format), @@ -172,6 +183,8 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): :param str query_url: URI to query for IP address, should return only the IP address as a text string + + Divider highlight group used: ``background:divider``. ''' return [{'contents': _external_ip(query_url=query_url), 'divider_highlight_group': 'background:divider'}] @@ -185,6 +198,8 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): :param str format: format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments + + Divider highlight group used: ``background:divider``. ''' try: import psutil @@ -294,6 +309,11 @@ def weather(unit='c', location_query=None, icons=None): location query for your current location, e.g. ``oslo, norway`` :param dict icons: dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` + + Divider highlight group used: ``background:divider``. + + Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``. + Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. ''' import json @@ -359,6 +379,10 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): threshold for "good load" highlighting :param float threshold_bad: threshold for "bad load" highlighting + + Divider highlight group used: ``background:divider``. + + Highlight groups used: ``system_load_good`` or ``system_load``, ``system_load_bad`` or ``system_load``, ``system_load_ugly`` or ``system_load``. It is recommended to define all highlight groups. ''' cpu_num = cpu_count() ret = [] @@ -466,6 +490,8 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold e-mail server port :param str folder: folder to check for e-mails + + Highlight groups used: ``email_alert``. ''' import imaplib import re diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index cd418361..5bcbe5ac 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -5,7 +5,10 @@ from powerline.theme import requires_segment_info @requires_segment_info def last_status(segment_info): - '''Return last exit code.''' + '''Return last exit code. + + Highlight groups used: ``exit_fail`` + ''' if not segment_info.last_exit_code: return None return [{'contents': str(segment_info.last_exit_code), 'highlight_group': 'exit_fail'}] @@ -13,7 +16,10 @@ def last_status(segment_info): @requires_segment_info def last_pipe_status(segment_info): - '''Return last pipe status.''' + '''Return last pipe status. + + Highlight groups used: ``exit_fail``, ``exit_success`` + ''' if any(segment_info.last_pipe_status): return [{"contents": str(status), "highlight_group": "exit_fail" if status else "exit_success"} for status in segment_info.last_pipe_status] diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 1cccd888..6f3a9ecd 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -215,6 +215,8 @@ def file_format(segment_info): '''Return file format (i.e. line ending type). :return: file format or None if unknown or missing file format + + Divider highlight group used: ``background:divider``. ''' return getbufvar(segment_info['bufnr'], '&fileformat') or None @@ -225,6 +227,8 @@ def file_encoding(segment_info): '''Return file encoding/character set. :return: file encoding/character set or None if unknown or missing file encoding + + Divider highlight group used: ``background:divider``. ''' return getbufvar(segment_info['bufnr'], '&fileencoding') or None @@ -235,6 +239,8 @@ def file_type(segment_info): '''Return file type. :return: file type or None if unknown file type + + Divider highlight group used: ``background:divider``. ''' return getbufvar(segment_info['bufnr'], '&filetype') or None @@ -245,6 +251,8 @@ def line_percent(segment_info, gradient=False): :param bool gradient: highlight the percentage with a color gradient (by default a green to red gradient) + + Highlight groups used: ``line_percent_gradient`` (gradient), ``line_percent``. ''' line_current = segment_info['window'].cursor[0] line_last = len(segment_info['buffer']) @@ -273,7 +281,10 @@ def col_current(segment_info): @window_cached def virtcol_current(): - '''Return current visual column with concealed characters ingored''' + '''Return current visual column with concealed characters ingored + + Highlight groups used: ``virtcol_current`` or ``col_current``. + ''' return [{'contents': str(vim_funcs['virtcol']('.')), 'highlight_group': ['virtcol_current', 'col_current']}] @@ -296,7 +307,10 @@ def modified_buffers(text='+ ', join_str=','): @requires_segment_info @memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell) def branch(segment_info): - '''Return the current working branch.''' + '''Return the current working branch. + + Divider highlight group used: ``branch:divider``. + ''' repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return [{ @@ -309,7 +323,10 @@ def branch(segment_info): @requires_segment_info @memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write) def file_vcs_status(segment_info): - '''Return the VCS status for this buffer.''' + '''Return the VCS status for this buffer. + + Highlight groups used: ``file_vcs_status``. + ''' name = segment_info['buffer'].name if name and not getbufvar(segment_info['bufnr'], '&buftype'): repo = guess(path=os.path.abspath(name)) From d33b8ee94660ff4afc3662780e74b698ebeef745 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 20:57:52 +0400 Subject: [PATCH 0447/1472] Fix highlight group documentation for line_percent --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 6f3a9ecd..c70eb3a1 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -252,7 +252,7 @@ def line_percent(segment_info, gradient=False): :param bool gradient: highlight the percentage with a color gradient (by default a green to red gradient) - Highlight groups used: ``line_percent_gradient`` (gradient), ``line_percent``. + Highlight groups used: ``line_percent_gradient`` (gradient) or ``line_percent``. ''' line_current = segment_info['window'].cursor[0] line_last = len(segment_info['buffer']) From 5b11feac73c0380ad955fc9893c2f9f6e8c80c11 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Mar 2013 12:40:03 +0400 Subject: [PATCH 0448/1472] Make some functions be more convenient for use in lint checker --- powerline/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index c5dbc19f..752d845a 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -9,13 +9,17 @@ from powerline.colorscheme import Colorscheme from powerline.lib import underscore_to_camelcase -def load_json_config(search_paths, config_file): +def open_file(path): + return open(path, 'r') + + +def load_json_config(search_paths, config_file, load=json.load, open=open_file): config_file += '.json' for path in search_paths: config_file_path = os.path.join(path, config_file) if os.path.isfile(config_file_path): - with open(config_file_path, 'r') as config_file_fp: - return json.load(config_file_fp) + with open(config_file_path) as config_file_fp: + return load(config_file_fp) raise IOError('Config file not found in search path: {0}'.format(config_file)) @@ -70,7 +74,8 @@ class Powerline(object): options = {'term_truecolor': common_config.get('term_truecolor', False)} self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) - def get_config_paths(self): + @staticmethod + def get_config_paths(): '''Get configuration paths. :return: list of paths From 287a88f473cdd11317fed5cc15f6f6bbf3506465 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 8 Mar 2013 20:01:45 +0400 Subject: [PATCH 0449/1472] Add JSON parser producing marked values Ref #278 --- powerline/lint/__init__.py | 0 powerline/lint/markedjson/__init__.py | 67 ++++ powerline/lint/markedjson/composer.py | 119 ++++++ powerline/lint/markedjson/constructor.py | 308 +++++++++++++++ powerline/lint/markedjson/error.py | 86 ++++ powerline/lint/markedjson/events.py | 83 ++++ powerline/lint/markedjson/loader.py | 20 + powerline/lint/markedjson/markedvalue.py | 28 ++ powerline/lint/markedjson/nodes.py | 49 +++ powerline/lint/markedjson/parser.py | 254 ++++++++++++ powerline/lint/markedjson/reader.py | 160 ++++++++ powerline/lint/markedjson/resolver.py | 135 +++++++ powerline/lint/markedjson/scanner.py | 477 +++++++++++++++++++++++ powerline/lint/markedjson/tokens.py | 57 +++ 14 files changed, 1843 insertions(+) create mode 100644 powerline/lint/__init__.py create mode 100644 powerline/lint/markedjson/__init__.py create mode 100644 powerline/lint/markedjson/composer.py create mode 100644 powerline/lint/markedjson/constructor.py create mode 100644 powerline/lint/markedjson/error.py create mode 100644 powerline/lint/markedjson/events.py create mode 100644 powerline/lint/markedjson/loader.py create mode 100644 powerline/lint/markedjson/markedvalue.py create mode 100644 powerline/lint/markedjson/nodes.py create mode 100644 powerline/lint/markedjson/parser.py create mode 100644 powerline/lint/markedjson/reader.py create mode 100644 powerline/lint/markedjson/resolver.py create mode 100644 powerline/lint/markedjson/scanner.py create mode 100644 powerline/lint/markedjson/tokens.py diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/lint/markedjson/__init__.py b/powerline/lint/markedjson/__init__.py new file mode 100644 index 00000000..e63b4e1d --- /dev/null +++ b/powerline/lint/markedjson/__init__.py @@ -0,0 +1,67 @@ + +from .error import * + +from .tokens import * +from .events import * +from .nodes import * + +from .loader import * + +__version__ = '3.10' + +def scan(stream, Loader=Loader): + """ + Scan a YAML stream and produce scanning tokens. + """ + loader = Loader(stream) + try: + while loader.check_token(): + yield loader.get_token() + finally: + loader.dispose() + +def parse(stream, Loader=Loader): + """ + Parse a YAML stream and produce parsing events. + """ + loader = Loader(stream) + try: + while loader.check_event(): + yield loader.get_event() + finally: + loader.dispose() + +def compose(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding representation tree. + """ + loader = Loader(stream) + try: + return loader.get_single_node() + finally: + loader.dispose() + +def compose_all(stream, Loader=Loader): + """ + Parse all YAML documents in a stream + and produce corresponding representation trees. + """ + loader = Loader(stream) + try: + while loader.check_node(): + yield loader.get_node() + finally: + loader.dispose() + +def load(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + """ + loader = Loader(stream) + try: + return loader.get_single_data() + finally: + loader.dispose() + diff --git a/powerline/lint/markedjson/composer.py b/powerline/lint/markedjson/composer.py new file mode 100644 index 00000000..115a48dd --- /dev/null +++ b/powerline/lint/markedjson/composer.py @@ -0,0 +1,119 @@ + +__all__ = ['Composer', 'ComposerError'] + +from .error import MarkedYAMLError +from .events import * +from .nodes import * + +class ComposerError(MarkedYAMLError): + pass + +class Composer: + + def __init__(self): + pass + + def check_node(self): + # Drop the STREAM-START event. + if self.check_event(StreamStartEvent): + self.get_event() + + # If there are more documents available? + return not self.check_event(StreamEndEvent) + + def get_node(self): + # Get the root node of the next document. + if not self.check_event(StreamEndEvent): + return self.compose_document() + + def get_single_node(self): + # Drop the STREAM-START event. + self.get_event() + + # Compose a document if the stream is not empty. + document = None + if not self.check_event(StreamEndEvent): + document = self.compose_document() + + # Ensure that the stream contains no more documents. + if not self.check_event(StreamEndEvent): + event = self.get_event() + raise ComposerError("expected a single document in the stream", + document.start_mark, "but found another document", + event.start_mark) + + # Drop the STREAM-END event. + self.get_event() + + return document + + def compose_document(self): + # Drop the DOCUMENT-START event. + self.get_event() + + # Compose the root node. + node = self.compose_node(None, None) + + # Drop the DOCUMENT-END event. + self.get_event() + + return node + + def compose_node(self, parent, index): + event = self.peek_event() + self.descend_resolver(parent, index) + if self.check_event(ScalarEvent): + node = self.compose_scalar_node() + elif self.check_event(SequenceStartEvent): + node = self.compose_sequence_node() + elif self.check_event(MappingStartEvent): + node = self.compose_mapping_node() + self.ascend_resolver() + return node + + def compose_scalar_node(self): + event = self.get_event() + tag = event.tag + if tag is None or tag == '!': + tag = self.resolve(ScalarNode, event.value, event.implicit, event.start_mark) + node = ScalarNode(tag, event.value, + event.start_mark, event.end_mark, style=event.style) + return node + + def compose_sequence_node(self): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(SequenceNode, None, start_event.implicit) + node = SequenceNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + index = 0 + while not self.check_event(SequenceEndEvent): + node.value.append(self.compose_node(node, index)) + index += 1 + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + + def compose_mapping_node(self): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(MappingNode, None, start_event.implicit) + node = MappingNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + while not self.check_event(MappingEndEvent): + #key_event = self.peek_event() + item_key = self.compose_node(node, None) + #if item_key in node.value: + # raise ComposerError("while composing a mapping", start_event.start_mark, + # "found duplicate key", key_event.start_mark) + item_value = self.compose_node(node, item_key) + #node.value[item_key] = item_value + node.value.append((item_key, item_value)) + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py new file mode 100644 index 00000000..c010ffaa --- /dev/null +++ b/powerline/lint/markedjson/constructor.py @@ -0,0 +1,308 @@ + +__all__ = ['BaseConstructor', 'Constructor', 'ConstructorError'] + +from .error import * +from .nodes import * +from .markedvalue import * + +import collections, datetime, base64, binascii, re, sys, types + +from functools import wraps + +def marked(func): + @wraps(func) + def f(self, node, *args, **kwargs): + return gen_marked_value(func(self, node, *args, **kwargs), node.start_mark) + return f + +class ConstructorError(MarkedYAMLError): + pass + +class BaseConstructor: + + yaml_constructors = {} + yaml_multi_constructors = {} + + def __init__(self): + self.constructed_objects = {} + self.state_generators = [] + self.deep_construct = False + + def check_data(self): + # If there are more documents available? + return self.check_node() + + def get_data(self): + # Construct and return the next document. + if self.check_node(): + return self.construct_document(self.get_node()) + + def get_single_data(self): + # Ensure that the stream contains a single document and construct it. + node = self.get_single_node() + if node is not None: + return self.construct_document(node) + return None + + def construct_document(self, node): + data = self.construct_object(node) + while self.state_generators: + state_generators = self.state_generators + self.state_generators = [] + for generator in state_generators: + for dummy in generator: + pass + self.constructed_objects = {} + self.deep_construct = False + return data + + def construct_object(self, node, deep=False): + if node in self.constructed_objects: + return self.constructed_objects[node] + if deep: + old_deep = self.deep_construct + self.deep_construct = True + constructor = None + tag_suffix = None + if node.tag in self.yaml_constructors: + constructor = self.yaml_constructors[node.tag] + else: + for tag_prefix in self.yaml_multi_constructors: + if node.tag.startswith(tag_prefix): + tag_suffix = node.tag[len(tag_prefix):] + constructor = self.yaml_multi_constructors[tag_prefix] + break + else: + if None in self.yaml_multi_constructors: + tag_suffix = node.tag + constructor = self.yaml_multi_constructors[None] + elif None in self.yaml_constructors: + constructor = self.yaml_constructors[None] + elif isinstance(node, ScalarNode): + constructor = self.__class__.construct_scalar + elif isinstance(node, SequenceNode): + constructor = self.__class__.construct_sequence + elif isinstance(node, MappingNode): + constructor = self.__class__.construct_mapping + if tag_suffix is None: + data = constructor(self, node) + else: + data = constructor(self, tag_suffix, node) + if isinstance(data, types.GeneratorType): + generator = data + data = next(generator) + if self.deep_construct: + for dummy in generator: + pass + else: + self.state_generators.append(generator) + self.constructed_objects[node] = data + if deep: + self.deep_construct = old_deep + return data + + @marked + def construct_scalar(self, node): + if not isinstance(node, ScalarNode): + raise ConstructorError(None, None, + "expected a scalar node, but found %s" % node.id, + node.start_mark) + return node.value + + def construct_sequence(self, node, deep=False): + if not isinstance(node, SequenceNode): + raise ConstructorError(None, None, + "expected a sequence node, but found %s" % node.id, + node.start_mark) + return [self.construct_object(child, deep=deep) + for child in node.value] + + def construct_mapping(self, node, deep=False): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark) + mapping = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + if not isinstance(key, collections.Hashable): + raise ConstructorError("while constructing a mapping", node.start_mark, + "found unhashable key", key_node.start_mark) + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping + + @classmethod + def add_constructor(cls, tag, constructor): + if not 'yaml_constructors' in cls.__dict__: + cls.yaml_constructors = cls.yaml_constructors.copy() + cls.yaml_constructors[tag] = constructor + +class Constructor(BaseConstructor): + + def construct_scalar(self, node): + if isinstance(node, MappingNode): + for key_node, value_node in node.value: + if key_node.tag == 'tag:yaml.org,2002:value': + return self.construct_scalar(value_node) + return BaseConstructor.construct_scalar(self, node) + + def flatten_mapping(self, node): + merge = [] + index = 0 + while index < len(node.value): + key_node, value_node = node.value[index] + if key_node.tag == 'tag:yaml.org,2002:merge': + del node.value[index] + if isinstance(value_node, MappingNode): + self.flatten_mapping(value_node) + merge.extend(value_node.value) + elif isinstance(value_node, SequenceNode): + submerge = [] + for subnode in value_node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found %s" + % subnode.id, subnode.start_mark) + self.flatten_mapping(subnode) + submerge.append(subnode.value) + submerge.reverse() + for value in submerge: + merge.extend(value) + else: + raise ConstructorError("while constructing a mapping", node.start_mark, + "expected a mapping or list of mappings for merging, but found %s" + % value_node.id, value_node.start_mark) + elif key_node.tag == 'tag:yaml.org,2002:value': + key_node.tag = 'tag:yaml.org,2002:str' + index += 1 + else: + index += 1 + if merge: + node.value = merge + node.value + + def construct_mapping(self, node, deep=False): + if isinstance(node, MappingNode): + self.flatten_mapping(node) + return BaseConstructor.construct_mapping(self, node, deep=deep) + + @marked + def construct_yaml_null(self, node): + self.construct_scalar(node) + return None + + bool_values = { + 'yes': True, + 'no': False, + 'true': True, + 'false': False, + 'on': True, + 'off': False, + } + + @marked + def construct_yaml_bool(self, node): + value = self.construct_scalar(node) + return self.bool_values[value.lower()] + + @marked + def construct_yaml_int(self, node): + value = self.construct_scalar(node) + value = value.replace('_', '') + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '0': + return 0 + elif value.startswith('0b'): + return sign*int(value[2:], 2) + elif value.startswith('0x'): + return sign*int(value[2:], 16) + elif value[0] == '0': + return sign*int(value, 8) + elif ':' in value: + digits = [int(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*int(value) + + @marked + def construct_yaml_float(self, node): + value = self.construct_scalar(node) + value = value.replace('_', '').lower() + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + elif ':' in value: + digits = [float(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0.0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*float(value) + + def construct_yaml_str(self, node): + return self.construct_scalar(node) + + def construct_yaml_seq(self, node): + data = gen_marked_value([], node.start_mark) + yield data + data.extend(self.construct_sequence(node)) + + def construct_yaml_map(self, node): + data = gen_marked_value({}, node.start_mark) + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_undefined(self, node): + raise ConstructorError(None, None, + "could not determine a constructor for the tag %r" % node.tag, + node.start_mark) + +Constructor.add_constructor( + 'tag:yaml.org,2002:null', + Constructor.construct_yaml_null) + +Constructor.add_constructor( + 'tag:yaml.org,2002:bool', + Constructor.construct_yaml_bool) + +Constructor.add_constructor( + 'tag:yaml.org,2002:int', + Constructor.construct_yaml_int) + +Constructor.add_constructor( + 'tag:yaml.org,2002:float', + Constructor.construct_yaml_float) + +Constructor.add_constructor( + 'tag:yaml.org,2002:str', + Constructor.construct_yaml_str) + +Constructor.add_constructor( + 'tag:yaml.org,2002:seq', + Constructor.construct_yaml_seq) + +Constructor.add_constructor( + 'tag:yaml.org,2002:map', + Constructor.construct_yaml_map) + +Constructor.add_constructor(None, + Constructor.construct_undefined) + diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py new file mode 100644 index 00000000..f3cd90cd --- /dev/null +++ b/powerline/lint/markedjson/error.py @@ -0,0 +1,86 @@ + +__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError', 'echoerr'] + + +import sys + + +def strtrans(s): + return s.replace('\t', '>---') + +class Mark: + + def __init__(self, name, index, line, column, buffer, pointer): + self.name = name + self.index = index + self.line = line + self.column = column + self.buffer = buffer + self.pointer = pointer + + def get_snippet(self, indent=4, max_length=75): + if self.buffer is None: + return None + head = '' + start = self.pointer + while start > 0 and self.buffer[start-1] not in '\0\n': + start -= 1 + if self.pointer-start > max_length/2-1: + head = ' ... ' + start += 5 + break + tail = '' + end = self.pointer + while end < len(self.buffer) and self.buffer[end] not in '\0\n': + end += 1 + if end-self.pointer > max_length/2-1: + tail = ' ... ' + end -= 5 + break + snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer+1:end]] + snippet = [strtrans(s) for s in snippet] + return ' ' * indent + head + ''.join(snippet) + tail + '\n' \ + + ' ' * (indent + len(head) + len(snippet[0])) + '^' + + def __str__(self): + snippet = self.get_snippet() + where = " in \"%s\", line %d, column %d" \ + % (self.name, self.line+1, self.column+1) + if snippet is not None: + where += ":\n" + snippet + try: + return where.encode('utf-8') + except AttributeError: + return where + +class YAMLError(Exception): + pass + + +def echoerr(*args, **kwargs): + sys.stderr.write(format_error(*args, **kwargs) + '\n') + +def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None): + lines = [] + if context is not None: + lines.append(context) + if context_mark is not None \ + and (problem is None or problem_mark is None + or context_mark.name != problem_mark.name + or context_mark.line != problem_mark.line + or context_mark.column != problem_mark.column): + lines.append(str(context_mark)) + if problem is not None: + lines.append(problem) + if problem_mark is not None: + lines.append(str(problem_mark)) + if note is not None: + lines.append(note) + return '\n'.join(lines) + +class MarkedYAMLError(YAMLError): + + def __init__(self, context=None, context_mark=None, + problem=None, problem_mark=None, note=None): + YAMLError.__init__(format_error(context, context_mark, problem, + problem_mark, note)) diff --git a/powerline/lint/markedjson/events.py b/powerline/lint/markedjson/events.py new file mode 100644 index 00000000..d1fe2d76 --- /dev/null +++ b/powerline/lint/markedjson/events.py @@ -0,0 +1,83 @@ + +# Abstract classes. + +class Event(object): + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + attributes = [key for key in ['implicit', 'value'] + if hasattr(self, key)] + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + +class NodeEvent(Event): + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + +class CollectionStartEvent(NodeEvent): + def __init__(self, implicit, start_mark=None, end_mark=None, + flow_style=None): + self.tag = None + self.implicit = implicit + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + +class CollectionEndEvent(Event): + pass + +# Implementations. + +class StreamStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + +class StreamEndEvent(Event): + pass + +class DocumentStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, + explicit=None, version=None, tags=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + self.version = version + self.tags = tags + +class DocumentEndEvent(Event): + def __init__(self, start_mark=None, end_mark=None, + explicit=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + +class AliasEvent(NodeEvent): + pass + +class ScalarEvent(NodeEvent): + def __init__(self, implicit, value, + start_mark=None, end_mark=None, style=None): + self.tag = None + self.implicit = implicit + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + +class SequenceStartEvent(CollectionStartEvent): + pass + +class SequenceEndEvent(CollectionEndEvent): + pass + +class MappingStartEvent(CollectionStartEvent): + pass + +class MappingEndEvent(CollectionEndEvent): + pass + diff --git a/powerline/lint/markedjson/loader.py b/powerline/lint/markedjson/loader.py new file mode 100644 index 00000000..0a0ac54a --- /dev/null +++ b/powerline/lint/markedjson/loader.py @@ -0,0 +1,20 @@ + +__all__ = ['Loader'] + +from .reader import * +from .scanner import * +from .parser import * +from .composer import * +from .constructor import * +from .resolver import * + +class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self) + diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py new file mode 100644 index 00000000..a6a9e7eb --- /dev/null +++ b/powerline/lint/markedjson/markedvalue.py @@ -0,0 +1,28 @@ +__all__ = ['gen_marked_value', 'MarkedValue'] + +class MarkedValue: + def __init__(self, value, mark): + self.mark = mark + self.value = value + +classcache = {} + +def gen_marked_value(value, mark): + if value.__class__ in classcache: + Marked = classcache[value.__class__] + else: + class Marked(MarkedValue): + for func in value.__class__.__dict__: + if func not in set(('__init__', '__new__', '__getattribute__')): + if func in set(('__eq__',)): + # HACK to make marked dictionaries always work + exec '''def {0}(self, *args): + return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args]) + '''.format(func) + else: + exec '''def {0}(self, *args, **kwargs): + return self.value.{0}(*args, **kwargs) + '''.format(func) + classcache[value.__class__] = Marked + + return Marked(value, mark) diff --git a/powerline/lint/markedjson/nodes.py b/powerline/lint/markedjson/nodes.py new file mode 100644 index 00000000..c4f070c4 --- /dev/null +++ b/powerline/lint/markedjson/nodes.py @@ -0,0 +1,49 @@ + +class Node(object): + def __init__(self, tag, value, start_mark, end_mark): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + value = self.value + #if isinstance(value, list): + # if len(value) == 0: + # value = '' + # elif len(value) == 1: + # value = '<1 item>' + # else: + # value = '<%d items>' % len(value) + #else: + # if len(value) > 75: + # value = repr(value[:70]+u' ... ') + # else: + # value = repr(value) + value = repr(value) + return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) + +class ScalarNode(Node): + id = 'scalar' + def __init__(self, tag, value, + start_mark=None, end_mark=None, style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + +class CollectionNode(Node): + def __init__(self, tag, value, + start_mark=None, end_mark=None, flow_style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + +class SequenceNode(CollectionNode): + id = 'sequence' + +class MappingNode(CollectionNode): + id = 'mapping' + diff --git a/powerline/lint/markedjson/parser.py b/powerline/lint/markedjson/parser.py new file mode 100644 index 00000000..4f373cee --- /dev/null +++ b/powerline/lint/markedjson/parser.py @@ -0,0 +1,254 @@ +__all__ = ['Parser', 'ParserError'] + +from .error import MarkedYAMLError, echoerr +from .tokens import * +from .events import * +from .scanner import * + +class ParserError(MarkedYAMLError): + pass + +class Parser: + # Since writing a recursive-descendant parser is a straightforward task, we + # do not give many comments here. + + DEFAULT_TAGS = { + '!': '!', + '!!': 'tag:yaml.org,2002:', + } + + def __init__(self): + self.current_event = None + self.yaml_version = None + self.tag_handles = {} + self.states = [] + self.marks = [] + self.state = self.parse_stream_start + + def dispose(self): + # Reset the state attributes (to clear self-references) + self.states = [] + self.state = None + + def check_event(self, *choices): + # Check the type of the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + if self.current_event is not None: + if not choices: + return True + for choice in choices: + if isinstance(self.current_event, choice): + return True + return False + + def peek_event(self): + # Get the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + return self.current_event + + def get_event(self): + # Get the next event and proceed further. + if self.current_event is None: + if self.state: + self.current_event = self.state() + value = self.current_event + self.current_event = None + return value + + # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END + # implicit_document ::= block_node DOCUMENT-END* + # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + + def parse_stream_start(self): + + # Parse the stream start. + token = self.get_token() + event = StreamStartEvent(token.start_mark, token.end_mark, + encoding=token.encoding) + + # Prepare the next state. + self.state = self.parse_implicit_document_start + + return event + + def parse_implicit_document_start(self): + + # Parse an implicit document. + if not self.check_token(StreamEndToken): + self.tag_handles = self.DEFAULT_TAGS + token = self.peek_token() + start_mark = end_mark = token.start_mark + event = DocumentStartEvent(start_mark, end_mark, explicit=False) + + # Prepare the next state. + self.states.append(self.parse_document_end) + self.state = self.parse_node + + return event + + else: + return self.parse_document_start() + + def parse_document_start(self): + + # Parse an explicit document. + if not self.check_token(StreamEndToken): + token = self.peek_token() + start_mark = token.start_mark + raise ParserError(None, None, + "expected '', but found %r" + % self.peek_token().id, + self.peek_token().start_mark) + else: + # Parse the end of the stream. + token = self.get_token() + event = StreamEndEvent(token.start_mark, token.end_mark) + assert not self.states + assert not self.marks + self.state = None + return event + + def parse_document_end(self): + + # Parse the document end. + token = self.peek_token() + start_mark = end_mark = token.start_mark + explicit = False + event = DocumentEndEvent(start_mark, end_mark, explicit=explicit) + + # Prepare the next state. + self.state = self.parse_document_start + + return event + + def parse_document_content(self): + return self.parse_node() + + def parse_node(self, indentless_sequence=False): + start_mark = end_mark = tag_mark = None + if start_mark is None: + start_mark = end_mark = self.peek_token().start_mark + event = None + implicit = True + if self.check_token(ScalarToken): + token = self.get_token() + end_mark = token.end_mark + if token.plain: + implicit = (True, False) + else: + implicit = (False, True) + event = ScalarEvent(implicit, token.value, + start_mark, end_mark, style=token.style) + self.state = self.states.pop() + elif self.check_token(FlowSequenceStartToken): + end_mark = self.peek_token().end_mark + event = SequenceStartEvent(implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_sequence_first_entry + elif self.check_token(FlowMappingStartToken): + end_mark = self.peek_token().end_mark + event = MappingStartEvent(implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_mapping_first_key + else: + token = self.peek_token() + raise ParserError("while parsing a flow node", start_mark, + "expected the node content, but found %r" % token.id, + token.start_mark) + return event + + def parse_flow_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_sequence_entry(first=True) + + def parse_flow_sequence_entry(self, first=False): + if not self.check_token(FlowSequenceEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + if self.check_token(FlowSequenceEndToken): + token = self.peek_token() + echoerr("While parsing a flow sequence", self.marks[-1], + "expected sequence value, but got %r" % token.id, token.start_mark) + else: + token = self.peek_token() + raise ParserError("while parsing a flow sequence", self.marks[-1], + "expected ',' or ']', but got %r" % token.id, token.start_mark) + + if not self.check_token(FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry) + return self.parse_node() + token = self.get_token() + event = SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_sequence_entry_mapping_end(self): + self.state = self.parse_flow_sequence_entry + token = self.peek_token() + return MappingEndEvent(token.start_mark, token.start_mark) + + def parse_flow_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_mapping_key(first=True) + + def parse_flow_mapping_key(self, first=False): + if not self.check_token(FlowMappingEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + if self.check_token(FlowMappingEndToken): + token = self.peek_token() + echoerr("While parsing a flow mapping", self.marks[-1], + "expected mapping key, but got %r" % token.id, token.start_mark) + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected ',' or '}', but got %r" % token.id, token.start_mark) + if self.check_token(KeyToken): + token = self.get_token() + if not self.check_token(ValueToken, + FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_value) + return self.parse_node() + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected value, but got %r" % token.id, token.start_mark) + elif not self.check_token(FlowMappingEndToken): + token = self.peek_token() + expect_key = self.check_token(ValueToken, FlowEntryToken) + if not expect_key: + self.get_token() + expect_key = self.check_token(ValueToken) + + if expect_key: + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected string key, but got %r" % token.id, token.start_mark) + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected ':', but got %r" % token.id, token.start_mark) + token = self.get_token() + event = MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_key) + return self.parse_node() + + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected mapping value, but got %r" % token.id, token.start_mark) diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py new file mode 100644 index 00000000..a1cf3b97 --- /dev/null +++ b/powerline/lint/markedjson/reader.py @@ -0,0 +1,160 @@ +# This module contains abstractions for the input stream. You don't have to +# looks further, there are no pretty code. +# +# We define two classes here. +# +# Mark(source, line, column) +# It's just a record and its only use is producing nice error messages. +# Parser does not use it for any other purposes. +# +# Reader(source, data) +# Reader determines the encoding of `data` and converts it to unicode. +# Reader provides the following methods and attributes: +# reader.peek(length=1) - return the next `length` characters +# reader.forward(length=1) - move the current position to `length` characters. +# reader.index - the number of the current character. +# reader.line, stream.column - the line and the column of the current character. + +__all__ = ['Reader', 'ReaderError'] + +from .error import YAMLError, Mark + +import codecs, re + + +try: + from __builtin__ import unicode, unichr +except ImportError: + unicode = str + unichr = chr + +class ReaderError(YAMLError): + + def __init__(self, name, position, character, encoding, reason): + self.name = name + self.character = character + self.position = position + self.encoding = encoding + self.reason = reason + + def __str__(self): + if isinstance(self.character, bytes): + return "'%s' codec can't decode byte #x%02x: %s\n" \ + " in \"%s\", position %d" \ + % (self.encoding, ord(self.character), self.reason, + self.name, self.position) + else: + return "unacceptable character #x%04x: %s\n" \ + " in \"%s\", position %d" \ + % (self.character, self.reason, + self.name, self.position) + +class Reader(object): + # Reader: + # - determines the data encoding and converts it to a unicode string, + # - checks if characters are in allowed range, + # - adds '\0' to the end. + + # Reader accepts + # - a file-like object with its `read` method returning `str`, + + # Yeah, it's ugly and slow. + + def __init__(self, stream): + self.name = None + self.stream = None + self.stream_pointer = 0 + self.eof = True + self.buffer = '' + self.pointer = 0 + self.full_buffer = unicode('') + self.full_pointer = 0 + self.raw_buffer = None + self.raw_decode = codecs.utf_8_decode + self.encoding = 'utf-8' + self.index = 0 + self.line = 0 + self.column = 0 + + self.stream = stream + self.name = getattr(stream, 'name', "") + self.eof = False + self.raw_buffer = None + + while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): + self.update_raw() + self.update(1) + + def peek(self, index=0): + try: + return self.buffer[self.pointer+index] + except IndexError: + self.update(index+1) + return self.buffer[self.pointer+index] + + def prefix(self, length=1): + if self.pointer+length >= len(self.buffer): + self.update(length) + return self.buffer[self.pointer:self.pointer+length] + + def forward(self, length=1): + if self.pointer+length+1 >= len(self.buffer): + self.update(length+1) + while length: + ch = self.buffer[self.pointer] + self.pointer += 1 + self.full_pointer += 1 + self.index += 1 + if ch == '\n': + self.line += 1 + self.column = 0 + length -= 1 + + def get_mark(self): + return Mark(self.name, self.index, self.line, self.column, + self.full_buffer, self.full_pointer) + + NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0)+'-'+unichr(0xD7FF)) + (unichr(0xE000)+'-'+unichr(0xFFFD)) + ']') + def check_printable(self, data): + match = self.NON_PRINTABLE.search(data) + if match: + character = match.group() + position = self.index+(len(self.buffer)-self.pointer)+match.start() + raise ReaderError(self.name, position, ord(character), + 'unicode', "special characters are not allowed") + + def update(self, length): + if self.raw_buffer is None: + return + self.buffer = self.buffer[self.pointer:] + self.pointer = 0 + while len(self.buffer) < length: + if not self.eof: + self.update_raw() + try: + data, converted = self.raw_decode(self.raw_buffer, + 'strict', self.eof) + except UnicodeDecodeError as exc: + character = self.raw_buffer[exc.start] + position = self.stream_pointer-len(self.raw_buffer)+exc.start + raise ReaderError(self.name, position, character, + exc.encoding, exc.reason) + self.check_printable(data) + self.buffer += data + self.full_buffer += data + self.raw_buffer = self.raw_buffer[converted:] + if self.eof: + self.buffer += '\0' + self.raw_buffer = None + break + + def update_raw(self, size=4096): + data = self.stream.read(size) + if self.raw_buffer is None: + self.raw_buffer = data + else: + self.raw_buffer += data + self.stream_pointer += len(data) + if not data: + self.eof = True + diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py new file mode 100644 index 00000000..3a139b42 --- /dev/null +++ b/powerline/lint/markedjson/resolver.py @@ -0,0 +1,135 @@ + +__all__ = ['BaseResolver', 'Resolver'] + +from .error import * +from .nodes import * + +import re + +class ResolverError(MarkedYAMLError): + pass + +class BaseResolver: + + DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' + DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' + DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' + + yaml_implicit_resolvers = {} + yaml_path_resolvers = {} + + def __init__(self): + self.resolver_exact_paths = [] + self.resolver_prefix_paths = [] + + @classmethod + def add_implicit_resolver(cls, tag, regexp, first): + if not 'yaml_implicit_resolvers' in cls.__dict__: + cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() + if first is None: + first = [None] + for ch in first: + cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) + + def descend_resolver(self, current_node, current_index): + if not self.yaml_path_resolvers: + return + exact_paths = {} + prefix_paths = [] + if current_node: + depth = len(self.resolver_prefix_paths) + for path, kind in self.resolver_prefix_paths[-1]: + if self.check_resolver_prefix(depth, path, kind, + current_node, current_index): + if len(path) > depth: + prefix_paths.append((path, kind)) + else: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + for path, kind in self.yaml_path_resolvers: + if not path: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + prefix_paths.append((path, kind)) + self.resolver_exact_paths.append(exact_paths) + self.resolver_prefix_paths.append(prefix_paths) + + def ascend_resolver(self): + if not self.yaml_path_resolvers: + return + self.resolver_exact_paths.pop() + self.resolver_prefix_paths.pop() + + def check_resolver_prefix(self, depth, path, kind, + current_node, current_index): + node_check, index_check = path[depth-1] + if isinstance(node_check, str): + if current_node.tag != node_check: + return + elif node_check is not None: + if not isinstance(current_node, node_check): + return + if index_check is True and current_index is not None: + return + if (index_check is False or index_check is None) \ + and current_index is None: + return + if isinstance(index_check, str): + if not (isinstance(current_index, ScalarNode) + and index_check == current_index.value): + return + elif isinstance(index_check, int) and not isinstance(index_check, bool): + if index_check != current_index: + return + return True + + def resolve(self, kind, value, implicit, mark=None): + if kind is ScalarNode and implicit[0]: + if value == '': + resolvers = self.yaml_implicit_resolvers.get('', []) + else: + resolvers = self.yaml_implicit_resolvers.get(value[0], []) + resolvers += self.yaml_implicit_resolvers.get(None, []) + for tag, regexp in resolvers: + if regexp.match(value): + return tag + else: + raise ResolverError('while resolving plain scalar', None, + "expected floating-point value, integer, null or boolean, but got %r" % value, + mark) + implicit = implicit[1] + if self.yaml_path_resolvers: + exact_paths = self.resolver_exact_paths[-1] + if kind in exact_paths: + return exact_paths[kind] + if None in exact_paths: + return exact_paths[None] + if kind is ScalarNode: + return self.DEFAULT_SCALAR_TAG + elif kind is SequenceNode: + return self.DEFAULT_SEQUENCE_TAG + elif kind is MappingNode: + return self.DEFAULT_MAPPING_TAG + +class Resolver(BaseResolver): + pass + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:bool', + re.compile(r'''^(?:true|false)$''', re.X), + list('yYnNtTfFoO')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:float', + re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X), + list('-0123456789')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:int', + re.compile(r'^(?:0|-?[1-9]\d*)$', re.X), + list('-0123456789')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:null', + re.compile(r'^null$', re.X), + ['n']) diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py new file mode 100644 index 00000000..bb5dd93a --- /dev/null +++ b/powerline/lint/markedjson/scanner.py @@ -0,0 +1,477 @@ + +# Scanner produces tokens of the following types: +# STREAM-START +# STREAM-END +# DOCUMENT-START +# DOCUMENT-END +# FLOW-SEQUENCE-START +# FLOW-MAPPING-START +# FLOW-SEQUENCE-END +# FLOW-MAPPING-END +# FLOW-ENTRY +# KEY +# VALUE +# ALIAS(value) +# ANCHOR(value) +# TAG(value) +# SCALAR(value, plain, style) +# +# Read comments in the Scanner code for more details. +# + +__all__ = ['Scanner', 'ScannerError'] + +from .error import MarkedYAMLError +from .tokens import * + +class ScannerError(MarkedYAMLError): + pass + +class SimpleKey: + # See below simple keys treatment. + + def __init__(self, token_number, index, line, column, mark): + self.token_number = token_number + self.index = index + self.line = line + self.column = column + self.mark = mark + +class Scanner: + + def __init__(self): + """Initialize the scanner.""" + # It is assumed that Scanner and Reader will have a common descendant. + # Reader do the dirty work of checking for BOM and converting the + # input data to Unicode. It also adds NUL to the end. + # + # Reader supports the following methods + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. + + # Had we reached the end of the stream? + self.done = False + + # The number of unclosed '{' and '['. `flow_level == 0` means block + # context. + self.flow_level = 0 + + # List of processed tokens that are not yet emitted. + self.tokens = [] + + # Add the STREAM-START token. + self.fetch_stream_start() + + # Number of tokens that were emitted through the `get_token` method. + self.tokens_taken = 0 + + # Variables related to simple keys treatment. + + # A simple key is a key that is not denoted by the '?' indicator. + # We emit the KEY token before all keys, so when we find a potential + # simple key, we try to locate the corresponding ':' indicator. + # Simple keys should be limited to a single line. + + # Can a simple key start at the current position? A simple key may + # start: + # - after '{', '[', ',' (in the flow context), + self.allow_simple_key = False + + # Keep track of possible simple keys. This is a dictionary. The key + # is `flow_level`; there can be no more that one possible simple key + # for each level. The value is a SimpleKey record: + # (token_number, index, line, column, mark) + # A simple key may start with SCALAR(flow), '[', or '{' tokens. + self.possible_simple_keys = {} + + # Public methods. + + def check_token(self, *choices): + # Check if the next token is one of the given types. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + if not choices: + return True + for choice in choices: + if isinstance(self.tokens[0], choice): + return True + return False + + def peek_token(self): + # Return the next token, but do not delete if from the queue. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + return self.tokens[0] + + def get_token(self): + # Return the next token. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + self.tokens_taken += 1 + return self.tokens.pop(0) + + # Private methods. + + def need_more_tokens(self): + if self.done: + return False + if not self.tokens: + return True + # The current token may be a potential simple key, so we + # need to look further. + self.stale_possible_simple_keys() + if self.next_possible_simple_key() == self.tokens_taken: + return True + + def fetch_more_tokens(self): + + # Eat whitespaces and comments until we reach the next token. + self.scan_to_next_token() + + # Remove obsolete possible simple keys. + self.stale_possible_simple_keys() + + # Peek the next character. + ch = self.peek() + + # Is it the end of stream? + if ch == '\0': + return self.fetch_stream_end() + + # Note: the order of the following checks is NOT significant. + + # Is it the flow sequence start indicator? + if ch == '[': + return self.fetch_flow_sequence_start() + + # Is it the flow mapping start indicator? + if ch == '{': + return self.fetch_flow_mapping_start() + + # Is it the flow sequence end indicator? + if ch == ']': + return self.fetch_flow_sequence_end() + + # Is it the flow mapping end indicator? + if ch == '}': + return self.fetch_flow_mapping_end() + + # Is it the flow entry indicator? + if ch == ',': + return self.fetch_flow_entry() + + # Is it the value indicator? + if ch == ':' and self.flow_level: + return self.fetch_value() + + # Is it a double quoted scalar? + if ch == '\"': + return self.fetch_double() + + # It must be a plain scalar then. + if self.check_plain(): + return self.fetch_plain() + + # No? It's an error. Let's produce a nice error message. + raise ScannerError("while scanning for the next token", None, + "found character %r that cannot start any token" % ch, + self.get_mark()) + + # Simple keys treatment. + + def next_possible_simple_key(self): + # Return the number of the nearest possible simple key. Actually we + # don't need to loop through the whole dictionary. We may replace it + # with the following code: + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number + min_token_number = None + for level in self.possible_simple_keys: + key = self.possible_simple_keys[level] + if min_token_number is None or key.token_number < min_token_number: + min_token_number = key.token_number + return min_token_number + + def stale_possible_simple_keys(self): + # Remove entries that are no longer possible simple keys. According to + # the YAML specification, simple keys + # - should be limited to a single line, + # Disabling this procedure will allow simple keys of any length and + # height (may cause problems if indentation is broken though). + for level in list(self.possible_simple_keys): + key = self.possible_simple_keys[level] + if key.line != self.line: + del self.possible_simple_keys[level] + + def save_possible_simple_key(self): + # The next token may start a simple key. We check if it's possible + # and save its position. This function is called for + # SCALAR(flow), '[', and '{'. + + # The next token might be a simple key. Let's save it's number and + # position. + if self.allow_simple_key: + self.remove_possible_simple_key() + token_number = self.tokens_taken+len(self.tokens) + key = SimpleKey(token_number, + self.index, self.line, self.column, self.get_mark()) + self.possible_simple_keys[self.flow_level] = key + + def remove_possible_simple_key(self): + # Remove the saved possible key position at the current flow level. + if self.flow_level in self.possible_simple_keys: + key = self.possible_simple_keys[self.flow_level] + + del self.possible_simple_keys[self.flow_level] + + # Fetchers. + + def fetch_stream_start(self): + # We always add STREAM-START as the first token and STREAM-END as the + # last token. + + # Read the token. + mark = self.get_mark() + + # Add STREAM-START. + self.tokens.append(StreamStartToken(mark, mark, + encoding=self.encoding)) + + + def fetch_stream_end(self): + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + self.possible_simple_keys = {} + + # Read the token. + mark = self.get_mark() + + # Add STREAM-END. + self.tokens.append(StreamEndToken(mark, mark)) + + # The steam is finished. + self.done = True + + def fetch_flow_sequence_start(self): + self.fetch_flow_collection_start(FlowSequenceStartToken) + + def fetch_flow_mapping_start(self): + self.fetch_flow_collection_start(FlowMappingStartToken) + + def fetch_flow_collection_start(self, TokenClass): + + # '[' and '{' may start a simple key. + self.save_possible_simple_key() + + # Increase the flow level. + self.flow_level += 1 + + # Simple keys are allowed after '[' and '{'. + self.allow_simple_key = True + + # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_end(self): + self.fetch_flow_collection_end(FlowSequenceEndToken) + + def fetch_flow_mapping_end(self): + self.fetch_flow_collection_end(FlowMappingEndToken) + + def fetch_flow_collection_end(self, TokenClass): + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Decrease the flow level. + self.flow_level -= 1 + + # No simple keys after ']' or '}'. + self.allow_simple_key = False + + # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_value(self): + # Do we determine a simple key? + if self.flow_level in self.possible_simple_keys: + + # Add KEY. + key = self.possible_simple_keys[self.flow_level] + del self.possible_simple_keys[self.flow_level] + self.tokens.insert(key.token_number-self.tokens_taken, + KeyToken(key.mark, key.mark)) + + # There cannot be two simple keys one after another. + self.allow_simple_key = False + + # Add VALUE. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(ValueToken(start_mark, end_mark)) + + def fetch_flow_entry(self): + + # Simple keys are allowed after ','. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add FLOW-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(FlowEntryToken(start_mark, end_mark)) + + def fetch_double(self): + # A flow scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after flow scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. + self.tokens.append(self.scan_flow_scalar()) + + def fetch_plain(self): + + # No simple keys after plain scalars. But note that `scan_plain` will + # change this flag if the scan is finished at the beginning of the + # line. + self.allow_simple_key = False + + # Scan and add SCALAR. May change `allow_simple_key`. + self.tokens.append(self.scan_plain()) + + # Checkers. + + def check_plain(self): + return self.peek() in '0123456789-ntf' + + # Scanners. + + def scan_to_next_token(self): + while self.peek() in ' \t\n': + self.forward() + + def scan_flow_scalar(self): + # See the specification for details. + # Note that we loose indentation rules for quoted scalars. Quoted + # scalars don't need to adhere indentation because " and ' clearly + # mark the beginning and the end of them. Therefore we are less + # restrictive then the specification requires. We only need to check + # that document separators are not included in scalars. + chunks = [] + start_mark = self.get_mark() + quote = self.peek() + self.forward() + chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) + while self.peek() != quote: + chunks.extend(self.scan_flow_scalar_spaces(start_mark)) + chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) + self.forward() + end_mark = self.get_mark() + return ScalarToken(''.join(chunks), False, start_mark, end_mark, '"') + + ESCAPE_REPLACEMENTS = { + 'b': '\x08', + 't': '\x09', + 'n': '\x0A', + 'f': '\x0C', + 'r': '\x0D', + '\"': '\"', + '\\': '\\', + } + + ESCAPE_CODES = { + 'u': 4, + } + + def scan_flow_scalar_non_spaces(self, start_mark): + # See the specification for details. + chunks = [] + while True: + length = 0 + while self.peek(length) not in '\"\\\0 \t\n': + length += 1 + if length: + chunks.append(self.prefix(length)) + self.forward(length) + ch = self.peek() + if ch == '\\': + self.forward() + ch = self.peek() + if ch in self.ESCAPE_REPLACEMENTS: + chunks.append(self.ESCAPE_REPLACEMENTS[ch]) + self.forward() + elif ch in self.ESCAPE_CODES: + length = self.ESCAPE_CODES[ch] + self.forward() + for k in range(length): + if self.peek(k) not in '0123456789ABCDEFabcdef': + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexdecimal numbers, but found %r" % + (length, self.peek(k)), self.get_mark()) + code = int(self.prefix(length), 16) + chunks.append(chr(code)) + self.forward(length) + else: + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "found unknown escape character %r" % ch, self.get_mark()) + else: + return chunks + + def scan_flow_scalar_spaces(self, start_mark): + # See the specification for details. + chunks = [] + length = 0 + while self.peek(length) in ' \t': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch == '\0': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark()) + elif ch == '\n': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected line end", self.get_mark()) + else: + chunks.append(whitespaces) + return chunks + + def scan_plain(self): + chunks = [] + start_mark = self.get_mark() + spaces = [] + while True: + length = 0 + while True: + ch = self.peek(length) + if self.peek(length) not in 'eE.0123456789nul-tr+fas': + break + length += 1 + if length == 0: + break + self.allow_simple_key = False + chunks.extend(spaces) + chunks.append(self.prefix(length)) + self.forward(length) + end_mark = self.get_mark() + return ScalarToken(''.join(chunks), True, start_mark, end_mark) diff --git a/powerline/lint/markedjson/tokens.py b/powerline/lint/markedjson/tokens.py new file mode 100644 index 00000000..98ba90e8 --- /dev/null +++ b/powerline/lint/markedjson/tokens.py @@ -0,0 +1,57 @@ + +class Token(object): + def __init__(self, start_mark, end_mark): + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + attributes = [key for key in self.__dict__ + if not key.endswith('_mark')] + attributes.sort() + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + +#class BOMToken(Token): +# id = '' + +class StreamStartToken(Token): + id = '' + def __init__(self, start_mark=None, end_mark=None, + encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + +class StreamEndToken(Token): + id = '' + +class FlowSequenceStartToken(Token): + id = '[' + +class FlowMappingStartToken(Token): + id = '{' + +class FlowSequenceEndToken(Token): + id = ']' + +class FlowMappingEndToken(Token): + id = '}' + +class KeyToken(Token): + id = '?' + +class ValueToken(Token): + id = ':' + +class FlowEntryToken(Token): + id = ',' + +class ScalarToken(Token): + id = '' + def __init__(self, value, plain, start_mark, end_mark, style=None): + self.value = value + self.plain = plain + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + From d27f7a0411955e15720494cfd9399104e16396a3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Mar 2013 16:16:00 +0400 Subject: [PATCH 0450/1472] Add most configuration checks Changes: - Add main configuration, colors.json, almost full themes and colorschemes checks - Make powerline.lint.check return whether it had problems - Make powerline-lint fail if .check reported problems - Make tests run powerline-lint - Add the script to the list of the installed scripts Fixes #278 --- powerline/lint/__init__.py | 899 +++++++++++++++++++++++++++++ powerline/lint/markedjson/error.py | 1 + scripts/powerline-lint | 14 + setup.py | 1 + tests/install.sh | 2 +- tests/test.sh | 9 +- 6 files changed, 924 insertions(+), 2 deletions(-) create mode 100755 scripts/powerline-lint diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index e69de29b..67ef8b71 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -0,0 +1,899 @@ +from powerline.lint.markedjson import load +from powerline import load_json_config, Powerline +from powerline.lint.markedjson.error import echoerr +from powerline.segments.vim import vim_modes +import itertools +import sys +import os +import re +from collections import defaultdict +from copy import copy + + +try: + from __builtin__ import unicode +except ImportError: + unicode = str + + +def open_file(path): + return open(path, 'rb') + + +def find_config(search_paths, config_file): + config_file += '.json' + for path in search_paths: + if os.path.isfile(os.path.join(path, config_file)): + return path + return None + + +EMPTYTUPLE = tuple() + + +def context_key(context): + return '/'.join((unicode(c[0]) for c in context)) + + +class Spec(object): + def __init__(self, **keys): + new_keys = {} + self.specs = list(keys.values()) + for k, v in keys.items(): + new_keys[k] = len(self.specs) + self.specs.append(v) + self.keys = new_keys + self.checks = [] + self.cmsg = '' + self.isoptional = False + self.uspecs = [] + self.ufailmsg = lambda key: 'found unknown key: {0}'.format(key) + if keys: + self.type(dict) + + def copy(self): + return self.__class__().update(self.__dict__) + + def update(self, d): + self.__dict__.update(d) + self.checks = copy(self.checks) + self.uspecs = copy(self.uspecs) + self.specs = [spec.copy() for spec in self.specs] + return self + + def unknown_spec(self, keyfunc, spec): + if isinstance(keyfunc, Spec): + self.specs.append(keyfunc) + keyfunc = len(self.specs) - 1 + self.specs.append(spec) + self.uspecs.append((keyfunc, len(self.specs) - 1)) + return self + + def unknown_msg(self, msgfunc): + self.ufailmsg = msgfunc + return self + + def context_message(self, msg): + self.cmsg = msg + for spec in self.specs: + if not spec.cmsg: + spec.context_message(msg) + return self + + def check_type(self, value, context_mark, data, context, echoerr, t): + if type(value.value) is not t: + echoerr(context=self.cmsg.format(key=context_key(context)), + context_mark=context_mark, + problem='must be a {0} instance'.format(t.__name__), + problem_mark=value.mark) + return False, True + return True, False + + def check_func(self, value, context_mark, data, context, echoerr, func, msg_func): + proceed, echo, hadproblem = func(value, data, context, echoerr) + if echo and hadproblem: + echoerr(context=self.cmsg.format(key=context_key(context)), + context_mark=context_mark, + problem=msg_func(value), + problem_mark=value.mark) + return proceed, hadproblem + + def check_list(self, value, context_mark, data, context, echoerr, item_func, msg_func): + i = 0 + hadproblem = False + for item in value: + if isinstance(item_func, int): + spec = self.specs[item_func] + proceed, fhadproblem = spec.match(item, value.mark, data, context + (('list item ' + unicode(i), item),), echoerr) + else: + proceed, echo, fhadproblem = item_func(item, data, context, echoerr) + if echo and fhadproblem: + echoerr(context=self.cmsg.format(key=context_key(context)+'/list item '+unicode(i)), + context_mark=value.mark, + problem=msg_func(item), + problem_mark=item.mark) + if fhadproblem: + hadproblem = True + if not proceed: + return proceed, hadproblem + i += 1 + return True, hadproblem + + def check_either(self, value, context_mark, data, context, echoerr, start, end): + errs = [] + + def new_echoerr(*args, **kwargs): + errs.append((args, kwargs)) + + hadproblem = False + for spec in self.specs[start:end]: + proceed, hadproblem = spec.match(value, value.mark, data, context, new_echoerr) + if not proceed: + break + if not hadproblem: + return True, False + + for args, kwargs in errs: + echoerr(*args, **kwargs) + + return False, hadproblem + + def check_tuple(self, value, context_mark, data, context, echoerr, start, end): + hadproblem = False + for (i, item, spec) in zip(itertools.count(), value, self.specs[start:end]): + proceed, ihadproblem = spec.match(item, value.mark, data, context + (('tuple item '+unicode(i), item),), echoerr) + if ihadproblem: + hadproblem = True + if not proceed: + return False, hadproblem + return True, hadproblem + + def type(self, t): + self.checks.append(('check_type', t)) + return self + + cmp_funcs = { + 'le': lambda x, y: x <= y, + 'lt': lambda x, y: x < y, + 'ge': lambda x, y: x >= y, + 'gt': lambda x, y: x > y, + 'eq': lambda x, y: x == y, + } + + cmp_msgs = { + 'le': 'lesser or equal to', + 'lt': 'lesser then', + 'ge': 'greater or equal to', + 'gt': 'greater then', + 'eq': 'equal to', + } + + def len(self, comparison, cint, msg_func=None): + cmp_func = self.cmp_funcs[comparison] + msg_func = msg_func or (lambda value: 'length of {0!r} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint)) + self.checks.append(('check_func', + (lambda value, *args: (True, True, not cmp_func(len(value), cint))), + msg_func)) + return self + + def cmp(self, comparison, cint, msg_func=None): + if type(cint) is str: + self.type(unicode) + else: + self.type(type(cint)) + cmp_func = self.cmp_funcs[comparison] + msg_func = msg_func or (lambda value: '{0} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint)) + self.checks.append(('check_func', + (lambda value, *args: (True, True, not cmp_func(value, cint))), + msg_func)) + return self + + def unsigned(self, msg_func=None): + self.type(int) + self.checks.append(('check_func', + (lambda value, *args: (True, True, value < 0)), + lambda value: '{0} must be greater then zero'.format(value))) + return self + + def list(self, item_func, msg_func=None): + self.type(list) + if isinstance(item_func, Spec): + self.specs.append(item_func) + item_func = len(self.specs) - 1 + self.checks.append(('check_list', item_func, msg_func or (lambda item: 'failed check'))) + return self + + def tuple(self, *specs): + self.type(list) + + max_len = len(specs) + min_len = max_len + for spec in reversed(specs): + if spec.isoptional: + min_len -= 1 + else: + break + if max_len == min_len: + self.len('eq', len(specs)) + else: + self.len('ge', min_len) + self.len('le', max_len) + + start = len(self.specs) + for i, spec in zip(itertools.count(), specs): + self.specs.append(spec) + self.checks.append(('check_tuple', start, len(self.specs))) + return self + + def func(self, func, msg_func=None): + self.checks.append(('check_func', func, msg_func or (lambda value: 'failed check'))) + return self + + def re(self, regex, msg_func=None): + self.type(unicode) + compiled = re.compile(regex) + msg_func = msg_func or (lambda value: 'String "{0}" does not match "{1}"'.format(value, regex)) + self.checks.append(('check_func', + (lambda value, *args: (True, True, not compiled.match(value.value))), + msg_func)) + return self + + def ident(self, msg_func=None): + msg_func = msg_func or (lambda value: 'String "{0}" is not an alphanumeric/underscore identifier'.format(value)) + return self.re('^\w+$', msg_func) + + def oneof(self, collection, msg_func=None): + msg_func = msg_func or (lambda value: '"{0}" must be one of {1!r}'.format(value, list(collection))) + self.checks.append(('check_func', + lambda value, *args: (True, True, value not in collection), + msg_func)) + return self + + def either(self, *specs): + start = len(self.specs) + self.specs.extend(specs) + self.checks.append(('check_either', start, len(self.specs))) + return self + + def optional(self): + self.isoptional = True + return self + + def match_checks(self, *args): + hadproblem = False + for check in self.checks: + proceed, chadproblem = getattr(self, check[0])(*(args + check[1:])) + if chadproblem: + hadproblem = True + if not proceed: + return False, hadproblem + return True, hadproblem + + def match(self, value, context_mark=None, data=None, context=EMPTYTUPLE, echoerr=echoerr): + proceed, hadproblem = self.match_checks(value, context_mark, data, context, echoerr) + if proceed: + if self.keys or self.uspecs: + for key, vali in self.keys.items(): + valspec = self.specs[vali] + if key in value: + proceed, mhadproblem = valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr) + if mhadproblem: + hadproblem = True + if not proceed: + return False, hadproblem + else: + if not valspec.isoptional: + hadproblem = True + echoerr(context=self.cmsg.format(key=context_key(context)), + context_mark=None, + problem='required key is missing: {0}'.format(key), + problem_mark=value.mark) + for key in value.keys(): + if key not in self.keys: + for keyfunc, vali in self.uspecs: + valspec = self.specs[vali] + if isinstance(keyfunc, int): + spec = self.specs[keyfunc] + proceed, khadproblem = spec.match(key, context_mark, data, context, echoerr) + else: + proceed, khadproblem = keyfunc(key, data, context, echoerr) + if khadproblem: + hadproblem = True + if proceed: + valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr) + break + else: + hadproblem = True + if self.ufailmsg: + echoerr(context=self.cmsg.format(key=context_key(context)), + context_mark=None, + problem=self.ufailmsg(key), + problem_mark=key.mark) + return True, hadproblem + + +class WithPath(object): + def __init__(self, import_paths): + self.import_paths = import_paths + + def __enter__(self): + self.oldpath = sys.path + sys.path = self.import_paths + sys.path + + def __exit__(self, *args): + sys.path = self.oldpath + + +def check_matcher_func(ext, match_name, data, context, echoerr): + import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])] + + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.matchers.{0}'.format(ext) + match_function = match_name + with WithPath(import_paths): + try: + func = getattr(__import__(match_module, fromlist=[match_function]), unicode(match_function)) + except ImportError: + echoerr(context='Error while loading matcher functions', + problem='failed to load module {0}'.format(match_module), + problem_mark=match_name.mark) + return True, True + except AttributeError: + echoerr(context='Error while loading matcher functions', + problem='failed to load matcher function {0}'.format(match_function), + problem_mark=match_name.mark) + return True, True + + if not callable(func): + echoerr(context='Error while loading matcher functions', + problem='loaded "function" {0} is not callable'.format(match_function), + problem_mark=match_name.mark) + return True, True + + if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'): + if func.func_code.co_argcount != 1: + echoerr(context='Error while loading matcher functions', + problem='function {0} accepts {1} arguments instead of 1. Are you sure it is the proper function?'.format(match_function, func.func_code.co_argcount), + problem_mark=match_name.mark) + + return True, False + + +def check_ext(ext, data, context, echoerr): + hadsomedirs = False + hadproblem = False + for subdir in ('themes', 'colorschemes'): + if ext not in data['configs'][subdir]: + hadproblem = True + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='{0} configuration does not exist'.format(subdir)) + else: + hadsomedirs = True + return hadsomedirs, hadproblem + + +def check_config(d, theme, data, context, echoerr): + if len(context) == 4: + ext = context[-2][0] + else: + # local_themes + ext = context[-3][0] + if ext not in data['configs']['themes'] or theme not in data['configs']['themes'][ext]: + echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext), + problem='failed to find configuration file themes/{0}/{1}.json'.format(ext, theme), + problem_mark=theme.mark) + return True, False, True + return True, False, False + +divider_spec = Spec().type(unicode).len('le', 3, + lambda value: 'Divider {0!r} is too large!'.format(value)).copy +divside_spec = Spec( + hard=divider_spec(), + soft=divider_spec(), +).copy +colorscheme_spec = Spec().type(unicode).func(lambda *args: check_config('colorschemes', *args)).copy +theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy +main_spec = (Spec( + common=Spec( + dividers=Spec( + left=divside_spec(), + right=divside_spec(), + ), + spaces=Spec().unsigned().cmp('le', 2, + lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value)), + term_truecolor=Spec().type(bool).optional(), + # Python is capable of loading from zip archives. Thus checking path + # only for existence of the path, not for it being a directory + paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(value.value))), + lambda value: 'path does not exist: {0}'.format(value)).optional(), + ).context_message('Error while loading common configuration (key {key})'), + ext=Spec( + vim=Spec( + colorscheme=colorscheme_spec(), + theme=theme_spec(), + local_themes=Spec() + .unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec()) + ), + ).unknown_spec(check_ext, + Spec( + colorscheme=colorscheme_spec(), + theme=theme_spec(), + )) + .context_message('Error while loading extensions configuration (key {key})'), +).context_message('Error while loading main configuration')) + +term_color_spec=Spec().unsigned().cmp('le', 255).copy +true_color_spec=Spec().re('^[0-9a-fA-F]{6}$', + lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value)).copy +colors_spec = (Spec( + colors=Spec().unknown_spec( + Spec().ident(), + Spec().either( + Spec().tuple(term_color_spec(), true_color_spec()), + term_color_spec())) + .context_message('Error while checking colors (key {key})'), + gradients=Spec().unknown_spec( + Spec().ident(), + Spec().tuple( + Spec().len('gt', 1).list(term_color_spec()), + Spec().len('gt', 1).list(true_color_spec()).optional(), + ) + ).context_message('Error while checking gradients (key {key})'), +).context_message('Error while loading colors configuration')) + + +def check_color(color, data, context, echoerr): + if color not in data['colors_config'].get('colors', {}) and color not in data['colors_config'].get('gradients', {}): + echoerr(context='Error while checking highlight group in colorscheme (key {key})'.format(key=context_key(context)), + problem='found unexistent color or gradient {0}'.format(color), + problem_mark=color.mark) + return True, False, True + return True, False, False + + +def check_translated_group_name(group, data, context, echoerr): + if group not in context[0][1].get('groups', {}): + echoerr(context='Error while checking translated group in colorscheme (key {key})'.format(key=context_key(context)), + problem='translated group {0} is not in main groups dictionary'.format(group), + problem_mark=group.mark) + return True, False, True + return True, False, False + + +color_spec = Spec().type(unicode).func(check_color).copy +name_spec = Spec().type(unicode).len('gt', 0).optional().copy +group_spec = Spec( + fg=color_spec(), + bg=color_spec(), + attr=Spec().list(Spec().type(unicode).oneof(set(('bold', 'italic', 'underline')))).optional(), +).copy +group_name_spec = Spec().re('^\w+(?::\w+)?$').copy +groups_spec = Spec().unknown_spec( + group_name_spec(), + group_spec(), +).copy +colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec().context_message('Error while loading groups (key {key})'), +).context_message('Error while loading coloscheme')) +vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc'])).copy +vim_colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec().context_message('Error while loading groups (key {key})'), + mode_translations=Spec().unknown_spec( + vim_mode_spec(), + Spec( + colors=Spec().unknown_spec( + color_spec(), + color_spec(), + ).optional(), + groups=Spec().unknown_spec( + group_name_spec().func(check_translated_group_name), + group_spec(), + ).optional(), + ), + ).context_message('Error while loading mode translations (key {key})'), +).context_message('Error while loading vim colorscheme')) + + +generic_keys = set(('exclude_modes', 'include_modes', 'width', 'align', 'name', 'draw_divider', 'priority', 'after', 'before')) +type_keys = { + 'function': set(('args', 'module')), + 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), + 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), + } +required_keys = { + 'function': set(), + 'string': set(('contents', 'highlight_group')), + 'filler': set(('highlight_group',)), + } +function_keys = set(('args', 'module')) + + +def check_key_compatibility(segment, data, context, echoerr): + segment_type = segment.get('type', 'function') + + if segment_type not in type_keys: + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='found segment with unknown type {0}'.format(segment_type), + problem_mark=segment_type.mark) + return False, False, True + + keys = set(segment) + if not ((keys - generic_keys) < type_keys[segment_type]): + unknown_keys = keys - generic_keys - type_keys[segment_type] + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=context[-1][1].mark, + problem='found keys not used with the current segment type: {0}'.format( + ', '.join((unicode(key) for key in unknown_keys))), + problem_mark=list(unknown_keys)[0].mark) + return True, False, True + + if not (keys > required_keys[segment_type]): + missing_keys = required_keys[segment_type] - keys + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=context[-1][1].mark, + problem='found missing required keys: {0}'.format( + ', '.join((unicode(key) for key in missing_keys)))) + return True, False, True + + return True, False, False + + +def check_segment_module(module, data, context, echoerr): + with WithPath(data['import_paths']): + try: + __import__(unicode(module)) + except ImportError: + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='failed to import module {0}'.format(module), + problem_mark=module.mark) + return True, False, True + return True, False, False + + +def check_full_segment_data(segment, data, context, echoerr): + if 'name' not in segment: + return True, False, False + + ext = data['ext'] + theme_segment_data = context[0][1].get('segment_data', {}) + top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', {}) + if data['theme'] == top_theme_name: + top_segment_data = {} + else: + top_segment_data = data['ext_theme_configs'].get(top_theme_name, {}).get('segment_data', {}) + + names = [segment['name']] + if segment.get('type', 'function') == 'function': + module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.'+ext)) + names.insert(0, unicode(module) + '.' + unicode(names[0])) + + segment_copy = segment.copy() + + for key in ('before', 'after', 'args', 'contents'): + if key not in segment_copy: + for segment_data in [theme_segment_data, top_segment_data]: + for name in names: + try: + val = segment_data[name][key] + # HACK to keep marks + l = list(segment_data[name]) + k = l[l.index(key)] + segment_copy[k] = val + except KeyError: + pass + + return check_key_compatibility(segment_copy, data, context, echoerr) + + +def check_segment_name(name, data, context, echoerr): + ext = data['ext'] + if context[-2][1].get('type', 'function') == 'function': + module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.'+ext)) + with WithPath(data['import_paths']): + try: + func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name)) + except ImportError: + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='failed to import function {0} from module {1}'.format(name, module), + problem_mark=module.mark) + return True, False, True + + if not callable(func): + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='imported "function" {0} from module {1} is not callable'.format(name, module), + problem_mark=module.mark) + return True, False, True + + hl_groups = [] + divider_hl_group = None + + if func.__doc__: + H_G_USED_STR = 'Highlight groups used: ' + D_H_G_USED_STR = 'Divider highlight group used: ' + for line in func.__doc__.split('\n'): + if H_G_USED_STR in line: + hl_groups.append(line[line.index(H_G_USED_STR)+len(H_G_USED_STR):]) + elif D_H_G_USED_STR in line: + divider_hl_group = line[line.index(D_H_G_USED_STR)+len(D_H_G_USED_STR)+2:-3] + + hadproblem = False + + if divider_hl_group: + r = hl_exists(divider_hl_group, data, context, echoerr, allow_gradients=True) + if r: + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}\n(Group name was obtained from function documentation.)'.format( + divider_hl_group, ', '.join(r)), + problem_mark=name.mark) + hadproblem = True + + if hl_groups: + greg = re.compile(r'``([^`]+)``( \(gradient\))?') + hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (', '.join(hl_groups)).split(', ')] + for required_pack in hl_groups: + rs = [hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) + for hl_group, gradient in required_pack] + if all(rs): + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight groups list ({0}) with all groups not defined in some colorschemes\n(Group names were taken from function documentation.)'.format( + ', '.join((unicode(h[0]) for h in required_pack))), + problem_mark=name.mark) + for r, h in zip(rs, required_pack): + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + h[0], ', '.join(r))) + hadproblem = True + else: + r = hl_exists(name, data, context, echoerr, allow_gradients=True) + if r: + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}\n(If not specified otherwise in documentation, highlight group for function segments\nis the same as the function name.)'.format( + name, ', '.join(r)), + problem_mark=name.mark) + hadproblem = True + + return True, False, hadproblem + else: + if name not in context[0][1].get('segment_data', {}): + top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', {}) + if data['theme'] == top_theme_name: + top_theme = {} + else: + top_theme = data['ext_theme_configs'].get(top_theme_name, {}) + if name not in top_theme.get('segment_data', {}): + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='found useless use of name key (such name is not present in theme/segment_data)', + problem_mark=name.mark) + + return True, False, False + + +def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): + ext = data['ext'] + if ext not in data['colorscheme_configs']: + # No colorschemes. Error was already reported, no need to report it + # twice + return [] + r = [] + for colorscheme, cconfig in data['colorscheme_configs'][ext].items(): + if hl_group not in cconfig.get('groups', {}): + r.append(colorscheme) + elif not allow_gradients: + group_config = cconfig['groups'][hl_group] + hadgradient = False + for ckey in ('fg', 'bg'): + color = group_config.get(ckey) + if not color: + # No color. Error was already reported. + continue + # Gradients are only allowed for function segments. Note that + # whether *either* color or gradient exists should have been + # already checked + hascolor = color in data['colors_config'].get('colors', {}) + hasgradient = color in data['colors_config'].get('gradients', {}) + if hasgradient: + hadgradient = True + if allow_gradients is False and not hascolor and hasgradient: + echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)), + context_mark=hl_group.mark, + problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), + problem_mark=color.mark) + r.append(colorscheme) + continue + if allow_gradients == 'force' and not hadgradient: + echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)), + context_mark=hl_group.mark, + problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), + problem_mark=group_config.mark) + r.append(colorscheme) + return r + + +def check_highlight_group(hl_group, data, context, echoerr): + r = hl_exists(hl_group, data, context, echoerr) + if r: + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + hl_group, ', '.join(r)), + problem_mark=hl_group.mark) + return True, False, True + return True, False, False + + +def check_highlight_groups(hl_groups, data, context, echoerr): + rs = [hl_exists(hl_group, data, context, echoerr) for hl_group in hl_groups] + if all(rs): + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format( + ', '.join((unicode(h) for h in hl_groups))), + problem_mark=hl_groups.mark) + for r, hl_group in zip(rs, hl_groups): + echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + hl_group, ', '.join(r)), + problem_mark=hl_group.mark) + return True, False, True + return True, False, False + + +def check_segment_data_key(key, data, context, echoerr): + ext = data['ext'] + top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', {}) + is_top_theme = (data['theme'] == top_theme_name) + if is_top_theme: + themes = data['ext_theme_configs'].values() + else: + themes = [context[0][1]] + + for theme in themes: + for segments in theme.get('segments', {}).values(): + found = False + for segment in segments: + if 'name' in segment: + if key == segment['name']: + found = True + module = segment.get('module', theme.get('default_module', 'powerline.segments.'+ext)) + if key == unicode(module) + '.' + unicode(segment['name']): + found = True + if found: + break + if found: + break + else: + echoerr(context='Error while checking segment data', + problem='found key {0} that cannot be associated with any segment'.format(key), + problem_mark=key.mark) + return True, False, True + + return True, False, False + + +highlight_group_spec = Spec().type(unicode).copy +segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy +segments_spec = Spec().optional().list( + Spec( + type=Spec().oneof(type_keys).optional(), + name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(), + exclude_modes=Spec().list(vim_mode_spec()).optional(), + include_modes=Spec().list(vim_mode_spec()).optional(), + draw_divider=Spec().type(bool).optional(), + module=segment_module_spec(), + priority=Spec().cmp('ge', -1).optional(), + after=Spec().type(unicode).optional(), + before=Spec().type(unicode).optional(), + width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), + align=Spec().oneof(set('lr')).optional(), + # FIXME Check args + args=Spec().type(dict).optional(), + contents=Spec().type(unicode).optional(), + highlight_group=Spec().list( + highlight_group_spec().re('^(?:(?!:divider$).)+$', + lambda value: 'it is recommended that only divider highlight group names end with ":divider"') + ).func(check_highlight_groups).optional(), + divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$', + lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(), + ).func(check_full_segment_data), +).copy +theme_spec = (Spec( + default_module=segment_module_spec(), + segment_data=Spec().unknown_spec( + Spec().func(check_segment_data_key), + Spec( + after=Spec().type(unicode).optional(), + before=Spec().type(unicode).optional(), + # FIXME Check args + args=Spec().type(dict).optional(), + contents=Spec().type(unicode).optional(), + ), + ).optional().context_message('Error while loading segment data (key {key})'), + segments=Spec( + left=segments_spec().context_message('Error while loading segments from left side (key {key})'), + right=segments_spec().context_message('Error while loading segments from right side (key {key})'), + ).func( + lambda value, *args: (True, True, not (('left' in value) or ('right' in value))), + lambda value: 'segments dictionary must contain either left, right or both keys' + ).context_message('Error while loading segments (key {key})'), +).context_message('Error while loading theme')) + + +def check(path=None): + search_paths = [path] if path else Powerline.get_config_paths() + + dirs = { + 'themes': defaultdict(lambda: []), + 'colorschemes': defaultdict(lambda: []) + } + for path in reversed(search_paths): + for subdir in ('themes', 'colorschemes'): + d = os.path.join(path, subdir) + if os.path.isdir(d): + for ext in os.listdir(d): + extd = os.path.join(d, ext) + if os.path.isdir(extd): + dirs[subdir][ext].append(extd) + elif os.path.exists(d): + hadproblem = True + sys.stderr.write('Path {0} is supposed to be a directory, but it is not\n'.format(d)) + + configs = { + 'themes': defaultdict(lambda: {}), + 'colorschemes': defaultdict(lambda: {}) + } + for subdir in ('themes', 'colorschemes'): + for ext in dirs[subdir]: + for d in dirs[subdir][ext]: + for config in os.listdir(d): + if config.endswith('.json'): + configs[subdir][ext][config[:-5]] = os.path.join(d, config) + + diff = set(configs['themes']) ^ set(configs['colorschemes']) + if diff: + hadproblem = True + for ext in diff: + sys.stderr.write('{0} extension {1} present only in {2}\n'.format( + ext, + 'configuration' if (ext in dirs['themes'] and ext in dirs['colorschemes']) else 'directory', + 'themes' if ext in configs['themes'] else 'colorschemes', + )) + + main_config = load_json_config(search_paths, 'config', load=load, open=open_file) + hadproblem = main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),))[1] + + import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] + + colors_config = load_json_config(search_paths, 'colors', load=load, open=open_file) + if colors_spec.match(colors_config, context=(('', colors_config),))[1]: + hadproblem = True + + colorscheme_configs = defaultdict(lambda: {}) + for ext in configs['colorschemes']: + data = {'ext': ext, 'colors_config': colors_config} + for colorscheme, cfile in configs['colorschemes'][ext].items(): + with open_file(cfile) as config_file_fp: + config = load(config_file_fp) + colorscheme_configs[ext][colorscheme] = config + if ext == 'vim': + spec = vim_colorscheme_spec + else: + spec = colorscheme_spec + if spec.match(config, context=(('', config),), data=data)[1]: + hadproblem = True + + theme_configs = defaultdict(lambda: {}) + for ext in configs['themes']: + for theme, sfile in configs['themes'][ext].items(): + with open_file(sfile) as config_file_fp: + config = load(config_file_fp) + theme_configs[ext][theme] = config + for ext, configs in theme_configs.items(): + data = {'ext': ext, 'colorscheme_configs': colorscheme_configs, 'import_paths': import_paths, + 'main_config': main_config, 'ext_theme_configs': configs, 'colors_config': colors_config} + for theme, config in configs.items(): + data['theme'] = theme + if theme_spec.match(config, context=(('', config),), data=data)[1]: + hadproblem = True + return hadproblem diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index f3cd90cd..e2e85a53 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -58,6 +58,7 @@ class YAMLError(Exception): def echoerr(*args, **kwargs): + sys.stderr.write('\n') sys.stderr.write(format_error(*args, **kwargs) + '\n') def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None): diff --git a/scripts/powerline-lint b/scripts/powerline-lint new file mode 100755 index 00000000..6d2df701 --- /dev/null +++ b/scripts/powerline-lint @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +'''Powerline configuration checker.''' +import argparse +from powerline.lint import check +import sys + + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('-p', '--config_path', metavar='PATH') + +if __name__ == '__main__': + args = parser.parse_args() + sys.exit(check(args.config_path)) diff --git a/setup.py b/setup.py index 6c5b1b9e..61986522 100755 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ setup( url='https://github.com/Lokaltog/powerline', scripts=[ 'scripts/powerline', + 'scripts/powerline-lint', ], keywords='', packages=find_packages(exclude=('tests', 'tests.*')), diff --git a/tests/install.sh b/tests/install.sh index 9a5b1cd2..30ecb922 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -5,6 +5,6 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install mercurial bzr if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 - pip install unittest2 + pip install unittest2 argparse fi fi diff --git a/tests/test.sh b/tests/test.sh index 5e7cdaf5..f4f69442 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,5 +1,6 @@ #!/bin/sh : ${PYTHON:=python} +FAILED=0 if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then # Python 2.6 export PYTHONPATH="${PYTHONPATH}:`realpath .`" @@ -9,5 +10,11 @@ if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then fi done else - ${PYTHON} setup.py test + if ! ${PYTHON} setup.py test ; then + FAILED=1 + fi fi +if ! ${PYTHON} scripts/powerline-lint ; then + FAILED=1 +fi +exit $FAILED From f75bb9e65bd210e5c0b9d071158ab14128cb00af Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 22:28:09 +0400 Subject: [PATCH 0451/1472] Make parser less restrictive and able to report problems --- powerline/lint/__init__.py | 26 ++++++++++--- powerline/lint/markedjson/__init__.py | 48 +----------------------- powerline/lint/markedjson/constructor.py | 20 +++++++++- powerline/lint/markedjson/error.py | 2 +- powerline/lint/markedjson/loader.py | 6 +++ powerline/lint/markedjson/parser.py | 9 +++-- powerline/lint/markedjson/scanner.py | 6 +-- 7 files changed, 55 insertions(+), 62 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 67ef8b71..ca14de79 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -560,8 +560,8 @@ def check_full_segment_data(segment, data, context, echoerr): ext = data['ext'] theme_segment_data = context[0][1].get('segment_data', {}) - top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', {}) - if data['theme'] == top_theme_name: + top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + if not top_theme_name or data['theme'] == top_theme_name: top_segment_data = {} else: top_segment_data = data['ext_theme_configs'].get(top_theme_name, {}).get('segment_data', {}) @@ -860,21 +860,33 @@ def check(path=None): 'themes' if ext in configs['themes'] else 'colorschemes', )) - main_config = load_json_config(search_paths, 'config', load=load, open=open_file) + lhadproblem = [False] + def load_config(stream): + r, hadproblem = load(stream) + if hadproblem: + lhadproblem[0] = True + return r + + main_config = load_json_config(search_paths, 'config', load=load_config, open=open_file) hadproblem = main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),))[1] import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] - colors_config = load_json_config(search_paths, 'colors', load=load, open=open_file) + colors_config = load_json_config(search_paths, 'colors', load=load_config, open=open_file) if colors_spec.match(colors_config, context=(('', colors_config),))[1]: hadproblem = True + if lhadproblem[0]: + hadproblem = True + colorscheme_configs = defaultdict(lambda: {}) for ext in configs['colorschemes']: data = {'ext': ext, 'colors_config': colors_config} for colorscheme, cfile in configs['colorschemes'][ext].items(): with open_file(cfile) as config_file_fp: - config = load(config_file_fp) + config, lhadproblem = load(config_file_fp) + if lhadproblem: + hadproblem = True colorscheme_configs[ext][colorscheme] = config if ext == 'vim': spec = vim_colorscheme_spec @@ -887,7 +899,9 @@ def check(path=None): for ext in configs['themes']: for theme, sfile in configs['themes'][ext].items(): with open_file(sfile) as config_file_fp: - config = load(config_file_fp) + config, lhadproblem = load(config_file_fp) + if lhadproblem: + hadproblem = True theme_configs[ext][theme] = config for ext, configs in theme_configs.items(): data = {'ext': ext, 'colorscheme_configs': colorscheme_configs, 'import_paths': import_paths, diff --git a/powerline/lint/markedjson/__init__.py b/powerline/lint/markedjson/__init__.py index e63b4e1d..348f39e0 100644 --- a/powerline/lint/markedjson/__init__.py +++ b/powerline/lint/markedjson/__init__.py @@ -9,51 +9,6 @@ from .loader import * __version__ = '3.10' -def scan(stream, Loader=Loader): - """ - Scan a YAML stream and produce scanning tokens. - """ - loader = Loader(stream) - try: - while loader.check_token(): - yield loader.get_token() - finally: - loader.dispose() - -def parse(stream, Loader=Loader): - """ - Parse a YAML stream and produce parsing events. - """ - loader = Loader(stream) - try: - while loader.check_event(): - yield loader.get_event() - finally: - loader.dispose() - -def compose(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding representation tree. - """ - loader = Loader(stream) - try: - return loader.get_single_node() - finally: - loader.dispose() - -def compose_all(stream, Loader=Loader): - """ - Parse all YAML documents in a stream - and produce corresponding representation trees. - """ - loader = Loader(stream) - try: - while loader.check_node(): - yield loader.get_node() - finally: - loader.dispose() - def load(stream, Loader=Loader): """ Parse the first YAML document in a stream @@ -61,7 +16,8 @@ def load(stream, Loader=Loader): """ loader = Loader(stream) try: - return loader.get_single_data() + r = loader.get_single_data() + return r, loader.haserrors finally: loader.dispose() diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index c010ffaa..af483d67 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -9,6 +9,12 @@ import collections, datetime, base64, binascii, re, sys, types from functools import wraps + +try: + from __builtin__ import unicode +except ImportError: + unicode = str + def marked(func): @wraps(func) def f(self, node, *args, **kwargs): @@ -117,6 +123,7 @@ class BaseConstructor: return [self.construct_object(child, deep=deep) for child in node.value] + @marked def construct_mapping(self, node, deep=False): if not isinstance(node, MappingNode): raise ConstructorError(None, None, @@ -126,8 +133,17 @@ class BaseConstructor: for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) if not isinstance(key, collections.Hashable): - raise ConstructorError("while constructing a mapping", node.start_mark, - "found unhashable key", key_node.start_mark) + self.echoerr('While constructing a mapping', node.start_mark, + 'found unhashable key', key_node.start_mark) + continue + elif type(key.value) != unicode: + self.echoerr('Error while constructing a mapping', node.start_mark, + 'found key that is not a string', key_node.start_mark) + continue + elif key in mapping: + self.echoerr('Error while constructing a mapping', node.start_mark, + 'found duplicate key', key_node.start_mark) + continue value = self.construct_object(value_node, deep=deep) mapping[key] = value return mapping diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index e2e85a53..49cdda5e 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -83,5 +83,5 @@ class MarkedYAMLError(YAMLError): def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None): - YAMLError.__init__(format_error(context, context_mark, problem, + YAMLError.__init__(self, format_error(context, context_mark, problem, problem_mark, note)) diff --git a/powerline/lint/markedjson/loader.py b/powerline/lint/markedjson/loader.py index 0a0ac54a..6b6882ca 100644 --- a/powerline/lint/markedjson/loader.py +++ b/powerline/lint/markedjson/loader.py @@ -7,6 +7,7 @@ from .parser import * from .composer import * from .constructor import * from .resolver import * +from .error import echoerr class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): @@ -17,4 +18,9 @@ class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): Composer.__init__(self) Constructor.__init__(self) Resolver.__init__(self) + self.haserrors = False + + def echoerr(self, *args, **kwargs): + echoerr(*args, **kwargs) + self.haserrors = True diff --git a/powerline/lint/markedjson/parser.py b/powerline/lint/markedjson/parser.py index 4f373cee..bd74188c 100644 --- a/powerline/lint/markedjson/parser.py +++ b/powerline/lint/markedjson/parser.py @@ -1,6 +1,6 @@ __all__ = ['Parser', 'ParserError'] -from .error import MarkedYAMLError, echoerr +from .error import MarkedYAMLError from .tokens import * from .events import * from .scanner import * @@ -99,10 +99,11 @@ class Parser: if not self.check_token(StreamEndToken): token = self.peek_token() start_mark = token.start_mark - raise ParserError(None, None, + self.echoerr(None, None, "expected '', but found %r" % self.peek_token().id, self.peek_token().start_mark) + return StreamEndEvent(token.start_mark, token.end_mark) else: # Parse the end of the stream. token = self.get_token() @@ -173,7 +174,7 @@ class Parser: self.get_token() if self.check_token(FlowSequenceEndToken): token = self.peek_token() - echoerr("While parsing a flow sequence", self.marks[-1], + self.echoerr("While parsing a flow sequence", self.marks[-1], "expected sequence value, but got %r" % token.id, token.start_mark) else: token = self.peek_token() @@ -206,7 +207,7 @@ class Parser: self.get_token() if self.check_token(FlowMappingEndToken): token = self.peek_token() - echoerr("While parsing a flow mapping", self.marks[-1], + self.echoerr("While parsing a flow mapping", self.marks[-1], "expected mapping key, but got %r" % token.id, token.start_mark) else: token = self.peek_token() diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index bb5dd93a..a45d02c9 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -351,9 +351,9 @@ class Scanner: def fetch_plain(self): - # No simple keys after plain scalars. But note that `scan_plain` will - # change this flag if the scan is finished at the beginning of the - # line. + self.save_possible_simple_key() + + # No simple keys after plain scalars. self.allow_simple_key = False # Scan and add SCALAR. May change `allow_simple_key`. From 3fd121509103cc6f917080f8e321cd40d4079bc0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 22:35:38 +0400 Subject: [PATCH 0452/1472] Some changes to make it work in non-python2.7 --- powerline/lint/markedjson/error.py | 6 +++--- powerline/lint/markedjson/markedvalue.py | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index 49cdda5e..a18a5db8 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -48,10 +48,10 @@ class Mark: % (self.name, self.line+1, self.column+1) if snippet is not None: where += ":\n" + snippet - try: - return where.encode('utf-8') - except AttributeError: + if type(where) is str: return where + else: + return where.encode('utf-8') class YAMLError(Exception): pass diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index a6a9e7eb..91a252de 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -16,13 +16,11 @@ def gen_marked_value(value, mark): if func not in set(('__init__', '__new__', '__getattribute__')): if func in set(('__eq__',)): # HACK to make marked dictionaries always work - exec '''def {0}(self, *args): - return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args]) - '''.format(func) + exec (('def {0}(self, *args):\n' + ' return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args])').format(func)) else: - exec '''def {0}(self, *args, **kwargs): - return self.value.{0}(*args, **kwargs) - '''.format(func) + exec (('def {0}(self, *args, **kwargs):\n' + ' return self.value.{0}(*args, **kwargs)\n').format(func)) classcache[value.__class__] = Marked return Marked(value, mark) From 5da848fa4a39ef58082470822c6156561be1f387 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 23:12:07 +0400 Subject: [PATCH 0453/1472] Some fixes for flake8 --- powerline/lint/__init__.py | 23 +- powerline/lint/markedjson/__init__.py | 34 +- powerline/lint/markedjson/composer.py | 190 +++-- powerline/lint/markedjson/constructor.py | 488 ++++++------- powerline/lint/markedjson/error.py | 133 ++-- powerline/lint/markedjson/events.py | 118 +-- powerline/lint/markedjson/loader.py | 38 +- powerline/lint/markedjson/markedvalue.py | 41 +- powerline/lint/markedjson/nodes.py | 84 ++- powerline/lint/markedjson/parser.py | 427 ++++++----- powerline/lint/markedjson/reader.py | 248 +++---- powerline/lint/markedjson/resolver.py | 214 +++--- powerline/lint/markedjson/scanner.py | 891 +++++++++++------------ powerline/lint/markedjson/tokens.py | 76 +- 14 files changed, 1466 insertions(+), 1539 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index ca14de79..7ca24bac 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -108,7 +108,7 @@ class Spec(object): else: proceed, echo, fhadproblem = item_func(item, data, context, echoerr) if echo and fhadproblem: - echoerr(context=self.cmsg.format(key=context_key(context)+'/list item '+unicode(i)), + echoerr(context=self.cmsg.format(key=context_key(context) + '/list item ' + unicode(i)), context_mark=value.mark, problem=msg_func(item), problem_mark=item.mark) @@ -141,7 +141,7 @@ class Spec(object): def check_tuple(self, value, context_mark, data, context, echoerr, start, end): hadproblem = False for (i, item, spec) in zip(itertools.count(), value, self.specs[start:end]): - proceed, ihadproblem = spec.match(item, value.mark, data, context + (('tuple item '+unicode(i), item),), echoerr) + proceed, ihadproblem = spec.match(item, value.mark, data, context + (('tuple item ' + unicode(i), item),), echoerr) if ihadproblem: hadproblem = True if not proceed: @@ -154,9 +154,9 @@ class Spec(object): cmp_funcs = { 'le': lambda x, y: x <= y, - 'lt': lambda x, y: x < y, + 'lt': lambda x, y: x < y, 'ge': lambda x, y: x >= y, - 'gt': lambda x, y: x > y, + 'gt': lambda x, y: x > y, 'eq': lambda x, y: x == y, } @@ -424,8 +424,8 @@ main_spec = (Spec( .context_message('Error while loading extensions configuration (key {key})'), ).context_message('Error while loading main configuration')) -term_color_spec=Spec().unsigned().cmp('le', 255).copy -true_color_spec=Spec().re('^[0-9a-fA-F]{6}$', +term_color_spec = Spec().unsigned().cmp('le', 255).copy +true_color_spec = Spec().re('^[0-9a-fA-F]{6}$', lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value)).copy colors_spec = (Spec( colors=Spec().unknown_spec( @@ -568,7 +568,7 @@ def check_full_segment_data(segment, data, context, echoerr): names = [segment['name']] if segment.get('type', 'function') == 'function': - module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.'+ext)) + module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.' + ext)) names.insert(0, unicode(module) + '.' + unicode(names[0])) segment_copy = segment.copy() @@ -592,7 +592,7 @@ def check_full_segment_data(segment, data, context, echoerr): def check_segment_name(name, data, context, echoerr): ext = data['ext'] if context[-2][1].get('type', 'function') == 'function': - module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.'+ext)) + module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.' + ext)) with WithPath(data['import_paths']): try: func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name)) @@ -616,9 +616,9 @@ def check_segment_name(name, data, context, echoerr): D_H_G_USED_STR = 'Divider highlight group used: ' for line in func.__doc__.split('\n'): if H_G_USED_STR in line: - hl_groups.append(line[line.index(H_G_USED_STR)+len(H_G_USED_STR):]) + hl_groups.append(line[line.index(H_G_USED_STR) + len(H_G_USED_STR):]) elif D_H_G_USED_STR in line: - divider_hl_group = line[line.index(D_H_G_USED_STR)+len(D_H_G_USED_STR)+2:-3] + divider_hl_group = line[line.index(D_H_G_USED_STR) + len(D_H_G_USED_STR) + 2:-3] hadproblem = False @@ -756,7 +756,7 @@ def check_segment_data_key(key, data, context, echoerr): if 'name' in segment: if key == segment['name']: found = True - module = segment.get('module', theme.get('default_module', 'powerline.segments.'+ext)) + module = segment.get('module', theme.get('default_module', 'powerline.segments.' + ext)) if key == unicode(module) + '.' + unicode(segment['name']): found = True if found: @@ -861,6 +861,7 @@ def check(path=None): )) lhadproblem = [False] + def load_config(stream): r, hadproblem = load(stream) if hadproblem: diff --git a/powerline/lint/markedjson/__init__.py b/powerline/lint/markedjson/__init__.py index 348f39e0..f8ef7483 100644 --- a/powerline/lint/markedjson/__init__.py +++ b/powerline/lint/markedjson/__init__.py @@ -1,23 +1,17 @@ - -from .error import * - -from .tokens import * -from .events import * -from .nodes import * - -from .loader import * - __version__ = '3.10' -def load(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - """ - loader = Loader(stream) - try: - r = loader.get_single_data() - return r, loader.haserrors - finally: - loader.dispose() +from .loader import Loader + + +def load(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + """ + loader = Loader(stream) + try: + r = loader.get_single_data() + return r, loader.haserrors + finally: + loader.dispose() diff --git a/powerline/lint/markedjson/composer.py b/powerline/lint/markedjson/composer.py index 115a48dd..fe3c3b86 100644 --- a/powerline/lint/markedjson/composer.py +++ b/powerline/lint/markedjson/composer.py @@ -1,119 +1,117 @@ - __all__ = ['Composer', 'ComposerError'] from .error import MarkedYAMLError -from .events import * -from .nodes import * +from .events import * # NOQA +from .nodes import * # NOQA + class ComposerError(MarkedYAMLError): - pass + pass + class Composer: + def __init__(self): + pass - def __init__(self): - pass + def check_node(self): + # Drop the STREAM-START event. + if self.check_event(StreamStartEvent): + self.get_event() - def check_node(self): - # Drop the STREAM-START event. - if self.check_event(StreamStartEvent): - self.get_event() + # If there are more documents available? + return not self.check_event(StreamEndEvent) - # If there are more documents available? - return not self.check_event(StreamEndEvent) + def get_node(self): + # Get the root node of the next document. + if not self.check_event(StreamEndEvent): + return self.compose_document() - def get_node(self): - # Get the root node of the next document. - if not self.check_event(StreamEndEvent): - return self.compose_document() + def get_single_node(self): + # Drop the STREAM-START event. + self.get_event() - def get_single_node(self): - # Drop the STREAM-START event. - self.get_event() + # Compose a document if the stream is not empty. + document = None + if not self.check_event(StreamEndEvent): + document = self.compose_document() - # Compose a document if the stream is not empty. - document = None - if not self.check_event(StreamEndEvent): - document = self.compose_document() + # Ensure that the stream contains no more documents. + if not self.check_event(StreamEndEvent): + event = self.get_event() + raise ComposerError("expected a single document in the stream", + document.start_mark, "but found another document", + event.start_mark) - # Ensure that the stream contains no more documents. - if not self.check_event(StreamEndEvent): - event = self.get_event() - raise ComposerError("expected a single document in the stream", - document.start_mark, "but found another document", - event.start_mark) + # Drop the STREAM-END event. + self.get_event() - # Drop the STREAM-END event. - self.get_event() + return document - return document + def compose_document(self): + # Drop the DOCUMENT-START event. + self.get_event() - def compose_document(self): - # Drop the DOCUMENT-START event. - self.get_event() + # Compose the root node. + node = self.compose_node(None, None) - # Compose the root node. - node = self.compose_node(None, None) + # Drop the DOCUMENT-END event. + self.get_event() - # Drop the DOCUMENT-END event. - self.get_event() + return node - return node + def compose_node(self, parent, index): + self.descend_resolver(parent, index) + if self.check_event(ScalarEvent): + node = self.compose_scalar_node() + elif self.check_event(SequenceStartEvent): + node = self.compose_sequence_node() + elif self.check_event(MappingStartEvent): + node = self.compose_mapping_node() + self.ascend_resolver() + return node - def compose_node(self, parent, index): - event = self.peek_event() - self.descend_resolver(parent, index) - if self.check_event(ScalarEvent): - node = self.compose_scalar_node() - elif self.check_event(SequenceStartEvent): - node = self.compose_sequence_node() - elif self.check_event(MappingStartEvent): - node = self.compose_mapping_node() - self.ascend_resolver() - return node + def compose_scalar_node(self): + event = self.get_event() + tag = event.tag + if tag is None or tag == '!': + tag = self.resolve(ScalarNode, event.value, event.implicit, event.start_mark) + node = ScalarNode(tag, event.value, + event.start_mark, event.end_mark, style=event.style) + return node - def compose_scalar_node(self): - event = self.get_event() - tag = event.tag - if tag is None or tag == '!': - tag = self.resolve(ScalarNode, event.value, event.implicit, event.start_mark) - node = ScalarNode(tag, event.value, - event.start_mark, event.end_mark, style=event.style) - return node - - def compose_sequence_node(self): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == '!': - tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - index = 0 - while not self.check_event(SequenceEndEvent): - node.value.append(self.compose_node(node, index)) - index += 1 - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - - def compose_mapping_node(self): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == '!': - tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - while not self.check_event(MappingEndEvent): - #key_event = self.peek_event() - item_key = self.compose_node(node, None) - #if item_key in node.value: - # raise ComposerError("while composing a mapping", start_event.start_mark, - # "found duplicate key", key_event.start_mark) - item_value = self.compose_node(node, item_key) - #node.value[item_key] = item_value - node.value.append((item_key, item_value)) - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node + def compose_sequence_node(self): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(SequenceNode, None, start_event.implicit) + node = SequenceNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + index = 0 + while not self.check_event(SequenceEndEvent): + node.value.append(self.compose_node(node, index)) + index += 1 + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + def compose_mapping_node(self): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(MappingNode, None, start_event.implicit) + node = MappingNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + while not self.check_event(MappingEndEvent): + #key_event = self.peek_event() + item_key = self.compose_node(node, None) + #if item_key in node.value: + # raise ComposerError("while composing a mapping", start_event.start_mark, + # "found duplicate key", key_event.start_mark) + item_value = self.compose_node(node, item_key) + #node.value[item_key] = item_value + node.value.append((item_key, item_value)) + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index af483d67..35429a5d 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -1,324 +1,274 @@ - __all__ = ['BaseConstructor', 'Constructor', 'ConstructorError'] -from .error import * -from .nodes import * -from .markedvalue import * +from .error import MarkedYAMLError +from .nodes import * # NOQA +from .markedvalue import gen_marked_value -import collections, datetime, base64, binascii, re, sys, types +import collections +import types from functools import wraps try: - from __builtin__ import unicode + from __builtin__ import unicode except ImportError: - unicode = str + unicode = str # NOQA + def marked(func): - @wraps(func) - def f(self, node, *args, **kwargs): - return gen_marked_value(func(self, node, *args, **kwargs), node.start_mark) - return f + @wraps(func) + def f(self, node, *args, **kwargs): + return gen_marked_value(func(self, node, *args, **kwargs), node.start_mark) + return f + class ConstructorError(MarkedYAMLError): - pass + pass + class BaseConstructor: + yaml_constructors = {} - yaml_constructors = {} - yaml_multi_constructors = {} + def __init__(self): + self.constructed_objects = {} + self.state_generators = [] + self.deep_construct = False - def __init__(self): - self.constructed_objects = {} - self.state_generators = [] - self.deep_construct = False + def check_data(self): + # If there are more documents available? + return self.check_node() - def check_data(self): - # If there are more documents available? - return self.check_node() + def get_data(self): + # Construct and return the next document. + if self.check_node(): + return self.construct_document(self.get_node()) - def get_data(self): - # Construct and return the next document. - if self.check_node(): - return self.construct_document(self.get_node()) + def get_single_data(self): + # Ensure that the stream contains a single document and construct it. + node = self.get_single_node() + if node is not None: + return self.construct_document(node) + return None - def get_single_data(self): - # Ensure that the stream contains a single document and construct it. - node = self.get_single_node() - if node is not None: - return self.construct_document(node) - return None + def construct_document(self, node): + data = self.construct_object(node) + while self.state_generators: + state_generators = self.state_generators + self.state_generators = [] + for generator in state_generators: + for dummy in generator: + pass + self.constructed_objects = {} + self.deep_construct = False + return data - def construct_document(self, node): - data = self.construct_object(node) - while self.state_generators: - state_generators = self.state_generators - self.state_generators = [] - for generator in state_generators: - for dummy in generator: - pass - self.constructed_objects = {} - self.deep_construct = False - return data + def construct_object(self, node, deep=False): + if node in self.constructed_objects: + return self.constructed_objects[node] + if deep: + old_deep = self.deep_construct + self.deep_construct = True + constructor = None + tag_suffix = None + if node.tag in self.yaml_constructors: + constructor = self.yaml_constructors[node.tag] + else: + raise ConstructorError(None, None, 'no constructor for tag %s' % node.tag) + if tag_suffix is None: + data = constructor(self, node) + else: + data = constructor(self, tag_suffix, node) + if isinstance(data, types.GeneratorType): + generator = data + data = next(generator) + if self.deep_construct: + for dummy in generator: + pass + else: + self.state_generators.append(generator) + self.constructed_objects[node] = data + if deep: + self.deep_construct = old_deep + return data - def construct_object(self, node, deep=False): - if node in self.constructed_objects: - return self.constructed_objects[node] - if deep: - old_deep = self.deep_construct - self.deep_construct = True - constructor = None - tag_suffix = None - if node.tag in self.yaml_constructors: - constructor = self.yaml_constructors[node.tag] - else: - for tag_prefix in self.yaml_multi_constructors: - if node.tag.startswith(tag_prefix): - tag_suffix = node.tag[len(tag_prefix):] - constructor = self.yaml_multi_constructors[tag_prefix] - break - else: - if None in self.yaml_multi_constructors: - tag_suffix = node.tag - constructor = self.yaml_multi_constructors[None] - elif None in self.yaml_constructors: - constructor = self.yaml_constructors[None] - elif isinstance(node, ScalarNode): - constructor = self.__class__.construct_scalar - elif isinstance(node, SequenceNode): - constructor = self.__class__.construct_sequence - elif isinstance(node, MappingNode): - constructor = self.__class__.construct_mapping - if tag_suffix is None: - data = constructor(self, node) - else: - data = constructor(self, tag_suffix, node) - if isinstance(data, types.GeneratorType): - generator = data - data = next(generator) - if self.deep_construct: - for dummy in generator: - pass - else: - self.state_generators.append(generator) - self.constructed_objects[node] = data - if deep: - self.deep_construct = old_deep - return data + @marked + def construct_scalar(self, node): + if not isinstance(node, ScalarNode): + raise ConstructorError(None, None, + "expected a scalar node, but found %s" % node.id, + node.start_mark) + return node.value - @marked - def construct_scalar(self, node): - if not isinstance(node, ScalarNode): - raise ConstructorError(None, None, - "expected a scalar node, but found %s" % node.id, - node.start_mark) - return node.value + def construct_sequence(self, node, deep=False): + if not isinstance(node, SequenceNode): + raise ConstructorError(None, None, + "expected a sequence node, but found %s" % node.id, + node.start_mark) + return [self.construct_object(child, deep=deep) + for child in node.value] - def construct_sequence(self, node, deep=False): - if not isinstance(node, SequenceNode): - raise ConstructorError(None, None, - "expected a sequence node, but found %s" % node.id, - node.start_mark) - return [self.construct_object(child, deep=deep) - for child in node.value] + @marked + def construct_mapping(self, node, deep=False): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark) + mapping = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + if not isinstance(key, collections.Hashable): + self.echoerr('While constructing a mapping', node.start_mark, + 'found unhashable key', key_node.start_mark) + continue + elif type(key.value) != unicode: + self.echoerr('Error while constructing a mapping', node.start_mark, + 'found key that is not a string', key_node.start_mark) + continue + elif key in mapping: + self.echoerr('Error while constructing a mapping', node.start_mark, + 'found duplicate key', key_node.start_mark) + continue + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping - @marked - def construct_mapping(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - mapping = {} - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - if not isinstance(key, collections.Hashable): - self.echoerr('While constructing a mapping', node.start_mark, - 'found unhashable key', key_node.start_mark) - continue - elif type(key.value) != unicode: - self.echoerr('Error while constructing a mapping', node.start_mark, - 'found key that is not a string', key_node.start_mark) - continue - elif key in mapping: - self.echoerr('Error while constructing a mapping', node.start_mark, - 'found duplicate key', key_node.start_mark) - continue - value = self.construct_object(value_node, deep=deep) - mapping[key] = value - return mapping + @classmethod + def add_constructor(cls, tag, constructor): + if not 'yaml_constructors' in cls.__dict__: + cls.yaml_constructors = cls.yaml_constructors.copy() + cls.yaml_constructors[tag] = constructor - @classmethod - def add_constructor(cls, tag, constructor): - if not 'yaml_constructors' in cls.__dict__: - cls.yaml_constructors = cls.yaml_constructors.copy() - cls.yaml_constructors[tag] = constructor class Constructor(BaseConstructor): + def construct_scalar(self, node): + if isinstance(node, MappingNode): + for key_node, value_node in node.value: + if key_node.tag == 'tag:yaml.org,2002:value': + return self.construct_scalar(value_node) + return BaseConstructor.construct_scalar(self, node) - def construct_scalar(self, node): - if isinstance(node, MappingNode): - for key_node, value_node in node.value: - if key_node.tag == 'tag:yaml.org,2002:value': - return self.construct_scalar(value_node) - return BaseConstructor.construct_scalar(self, node) + def flatten_mapping(self, node): + merge = [] + index = 0 + while index < len(node.value): + key_node, value_node = node.value[index] + if key_node.tag == 'tag:yaml.org,2002:merge': + del node.value[index] + if isinstance(value_node, MappingNode): + self.flatten_mapping(value_node) + merge.extend(value_node.value) + elif isinstance(value_node, SequenceNode): + submerge = [] + for subnode in value_node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found %s" + % subnode.id, subnode.start_mark) + self.flatten_mapping(subnode) + submerge.append(subnode.value) + submerge.reverse() + for value in submerge: + merge.extend(value) + else: + raise ConstructorError("while constructing a mapping", node.start_mark, + "expected a mapping or list of mappings for merging, but found %s" + % value_node.id, value_node.start_mark) + elif key_node.tag == 'tag:yaml.org,2002:value': + key_node.tag = 'tag:yaml.org,2002:str' + index += 1 + else: + index += 1 + if merge: + node.value = merge + node.value - def flatten_mapping(self, node): - merge = [] - index = 0 - while index < len(node.value): - key_node, value_node = node.value[index] - if key_node.tag == 'tag:yaml.org,2002:merge': - del node.value[index] - if isinstance(value_node, MappingNode): - self.flatten_mapping(value_node) - merge.extend(value_node.value) - elif isinstance(value_node, SequenceNode): - submerge = [] - for subnode in value_node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found %s" - % subnode.id, subnode.start_mark) - self.flatten_mapping(subnode) - submerge.append(subnode.value) - submerge.reverse() - for value in submerge: - merge.extend(value) - else: - raise ConstructorError("while constructing a mapping", node.start_mark, - "expected a mapping or list of mappings for merging, but found %s" - % value_node.id, value_node.start_mark) - elif key_node.tag == 'tag:yaml.org,2002:value': - key_node.tag = 'tag:yaml.org,2002:str' - index += 1 - else: - index += 1 - if merge: - node.value = merge + node.value + def construct_mapping(self, node, deep=False): + if isinstance(node, MappingNode): + self.flatten_mapping(node) + return BaseConstructor.construct_mapping(self, node, deep=deep) - def construct_mapping(self, node, deep=False): - if isinstance(node, MappingNode): - self.flatten_mapping(node) - return BaseConstructor.construct_mapping(self, node, deep=deep) + @marked + def construct_yaml_null(self, node): + self.construct_scalar(node) + return None - @marked - def construct_yaml_null(self, node): - self.construct_scalar(node) - return None + @marked + def construct_yaml_bool(self, node): + value = self.construct_scalar(node).value + return bool(value) - bool_values = { - 'yes': True, - 'no': False, - 'true': True, - 'false': False, - 'on': True, - 'off': False, - } + @marked + def construct_yaml_int(self, node): + value = self.construct_scalar(node).value + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '0': + return 0 + else: + return sign * int(value) - @marked - def construct_yaml_bool(self, node): - value = self.construct_scalar(node) - return self.bool_values[value.lower()] + @marked + def construct_yaml_float(self, node): + value = self.construct_scalar(node).value + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + else: + return sign * float(value) - @marked - def construct_yaml_int(self, node): - value = self.construct_scalar(node) - value = value.replace('_', '') - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '0': - return 0 - elif value.startswith('0b'): - return sign*int(value[2:], 2) - elif value.startswith('0x'): - return sign*int(value[2:], 16) - elif value[0] == '0': - return sign*int(value, 8) - elif ':' in value: - digits = [int(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*int(value) + def construct_yaml_str(self, node): + return self.construct_scalar(node) - @marked - def construct_yaml_float(self, node): - value = self.construct_scalar(node) - value = value.replace('_', '').lower() - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - elif ':' in value: - digits = [float(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0.0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*float(value) + def construct_yaml_seq(self, node): + data = gen_marked_value([], node.start_mark) + yield data + data.extend(self.construct_sequence(node)) - def construct_yaml_str(self, node): - return self.construct_scalar(node) + def construct_yaml_map(self, node): + data = gen_marked_value({}, node.start_mark) + yield data + value = self.construct_mapping(node) + data.update(value) - def construct_yaml_seq(self, node): - data = gen_marked_value([], node.start_mark) - yield data - data.extend(self.construct_sequence(node)) + def construct_undefined(self, node): + raise ConstructorError(None, None, + "could not determine a constructor for the tag %r" % node.tag, + node.start_mark) - def construct_yaml_map(self, node): - data = gen_marked_value({}, node.start_mark) - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_undefined(self, node): - raise ConstructorError(None, None, - "could not determine a constructor for the tag %r" % node.tag, - node.start_mark) Constructor.add_constructor( - 'tag:yaml.org,2002:null', - Constructor.construct_yaml_null) + 'tag:yaml.org,2002:null', + Constructor.construct_yaml_null) Constructor.add_constructor( - 'tag:yaml.org,2002:bool', - Constructor.construct_yaml_bool) + 'tag:yaml.org,2002:bool', + Constructor.construct_yaml_bool) Constructor.add_constructor( - 'tag:yaml.org,2002:int', - Constructor.construct_yaml_int) + 'tag:yaml.org,2002:int', + Constructor.construct_yaml_int) Constructor.add_constructor( - 'tag:yaml.org,2002:float', - Constructor.construct_yaml_float) + 'tag:yaml.org,2002:float', + Constructor.construct_yaml_float) Constructor.add_constructor( - 'tag:yaml.org,2002:str', - Constructor.construct_yaml_str) + 'tag:yaml.org,2002:str', + Constructor.construct_yaml_str) Constructor.add_constructor( - 'tag:yaml.org,2002:seq', - Constructor.construct_yaml_seq) + 'tag:yaml.org,2002:seq', + Constructor.construct_yaml_seq) Constructor.add_constructor( - 'tag:yaml.org,2002:map', - Constructor.construct_yaml_map) + 'tag:yaml.org,2002:map', + Constructor.construct_yaml_map) Constructor.add_constructor(None, - Constructor.construct_undefined) - + Constructor.construct_undefined) diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index a18a5db8..0828c2c2 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -1,4 +1,3 @@ - __all__ = ['Mark', 'YAMLError', 'MarkedYAMLError', 'echoerr'] @@ -6,82 +5,84 @@ import sys def strtrans(s): - return s.replace('\t', '>---') + return s.replace('\t', '>---') + class Mark: + def __init__(self, name, index, line, column, buffer, pointer): + self.name = name + self.index = index + self.line = line + self.column = column + self.buffer = buffer + self.pointer = pointer - def __init__(self, name, index, line, column, buffer, pointer): - self.name = name - self.index = index - self.line = line - self.column = column - self.buffer = buffer - self.pointer = pointer + def get_snippet(self, indent=4, max_length=75): + if self.buffer is None: + return None + head = '' + start = self.pointer + while start > 0 and self.buffer[start - 1] not in '\0\n': + start -= 1 + if self.pointer - start > max_length / 2 - 1: + head = ' ... ' + start += 5 + break + tail = '' + end = self.pointer + while end < len(self.buffer) and self.buffer[end] not in '\0\n': + end += 1 + if end - self.pointer > max_length / 2 - 1: + tail = ' ... ' + end -= 5 + break + snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer + 1:end]] + snippet = [strtrans(s) for s in snippet] + return ' ' * indent + head + ''.join(snippet) + tail + '\n' \ + + ' ' * (indent + len(head) + len(snippet[0])) + '^' - def get_snippet(self, indent=4, max_length=75): - if self.buffer is None: - return None - head = '' - start = self.pointer - while start > 0 and self.buffer[start-1] not in '\0\n': - start -= 1 - if self.pointer-start > max_length/2-1: - head = ' ... ' - start += 5 - break - tail = '' - end = self.pointer - while end < len(self.buffer) and self.buffer[end] not in '\0\n': - end += 1 - if end-self.pointer > max_length/2-1: - tail = ' ... ' - end -= 5 - break - snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer+1:end]] - snippet = [strtrans(s) for s in snippet] - return ' ' * indent + head + ''.join(snippet) + tail + '\n' \ - + ' ' * (indent + len(head) + len(snippet[0])) + '^' + def __str__(self): + snippet = self.get_snippet() + where = " in \"%s\", line %d, column %d" \ + % (self.name, self.line + 1, self.column + 1) + if snippet is not None: + where += ":\n" + snippet + if type(where) is str: + return where + else: + return where.encode('utf-8') - def __str__(self): - snippet = self.get_snippet() - where = " in \"%s\", line %d, column %d" \ - % (self.name, self.line+1, self.column+1) - if snippet is not None: - where += ":\n" + snippet - if type(where) is str: - return where - else: - return where.encode('utf-8') class YAMLError(Exception): - pass + pass def echoerr(*args, **kwargs): - sys.stderr.write('\n') - sys.stderr.write(format_error(*args, **kwargs) + '\n') + sys.stderr.write('\n') + sys.stderr.write(format_error(*args, **kwargs) + '\n') + def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None): - lines = [] - if context is not None: - lines.append(context) - if context_mark is not None \ - and (problem is None or problem_mark is None - or context_mark.name != problem_mark.name - or context_mark.line != problem_mark.line - or context_mark.column != problem_mark.column): - lines.append(str(context_mark)) - if problem is not None: - lines.append(problem) - if problem_mark is not None: - lines.append(str(problem_mark)) - if note is not None: - lines.append(note) - return '\n'.join(lines) + lines = [] + if context is not None: + lines.append(context) + if context_mark is not None \ + and (problem is None or problem_mark is None + or context_mark.name != problem_mark.name + or context_mark.line != problem_mark.line + or context_mark.column != problem_mark.column): + lines.append(str(context_mark)) + if problem is not None: + lines.append(problem) + if problem_mark is not None: + lines.append(str(problem_mark)) + if note is not None: + lines.append(note) + return '\n'.join(lines) + class MarkedYAMLError(YAMLError): - - def __init__(self, context=None, context_mark=None, - problem=None, problem_mark=None, note=None): - YAMLError.__init__(self, format_error(context, context_mark, problem, - problem_mark, note)) + def __init__(self, context=None, context_mark=None, + problem=None, problem_mark=None, note=None): + YAMLError.__init__(self, format_error(context, context_mark, problem, + problem_mark, note)) diff --git a/powerline/lint/markedjson/events.py b/powerline/lint/markedjson/events.py index d1fe2d76..47e2667f 100644 --- a/powerline/lint/markedjson/events.py +++ b/powerline/lint/markedjson/events.py @@ -1,83 +1,97 @@ - # Abstract classes. + class Event(object): - def __init__(self, start_mark=None, end_mark=None): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in ['implicit', 'value'] - if hasattr(self, key)] - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + + def __repr__(self): + attributes = [key for key in ['implicit', 'value'] + if hasattr(self, key)] + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + class NodeEvent(Event): - def __init__(self, start_mark=None, end_mark=None): - self.start_mark = start_mark - self.end_mark = end_mark + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + class CollectionStartEvent(NodeEvent): - def __init__(self, implicit, start_mark=None, end_mark=None, - flow_style=None): - self.tag = None - self.implicit = implicit - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style + def __init__(self, implicit, start_mark=None, end_mark=None, + flow_style=None): + self.tag = None + self.implicit = implicit + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + class CollectionEndEvent(Event): - pass + pass + # Implementations. + class StreamStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding + def __init__(self, start_mark=None, end_mark=None, encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + class StreamEndEvent(Event): - pass + pass + class DocumentStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None, version=None, tags=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - self.version = version - self.tags = tags + def __init__(self, start_mark=None, end_mark=None, + explicit=None, version=None, tags=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + self.version = version + self.tags = tags + class DocumentEndEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit + def __init__(self, start_mark=None, end_mark=None, + explicit=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + class AliasEvent(NodeEvent): - pass + pass + class ScalarEvent(NodeEvent): - def __init__(self, implicit, value, - start_mark=None, end_mark=None, style=None): - self.tag = None - self.implicit = implicit - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style + def __init__(self, implicit, value, + start_mark=None, end_mark=None, style=None): + self.tag = None + self.implicit = implicit + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + class SequenceStartEvent(CollectionStartEvent): - pass + pass + class SequenceEndEvent(CollectionEndEvent): - pass + pass + class MappingStartEvent(CollectionStartEvent): - pass + pass + class MappingEndEvent(CollectionEndEvent): - pass - + pass diff --git a/powerline/lint/markedjson/loader.py b/powerline/lint/markedjson/loader.py index 6b6882ca..50ae6d05 100644 --- a/powerline/lint/markedjson/loader.py +++ b/powerline/lint/markedjson/loader.py @@ -1,26 +1,24 @@ - __all__ = ['Loader'] -from .reader import * -from .scanner import * -from .parser import * -from .composer import * -from .constructor import * -from .resolver import * +from .reader import Reader +from .scanner import Scanner +from .parser import Parser +from .composer import Composer +from .constructor import Constructor +from .resolver import Resolver from .error import echoerr + class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self) + self.haserrors = False - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - Constructor.__init__(self) - Resolver.__init__(self) - self.haserrors = False - - def echoerr(self, *args, **kwargs): - echoerr(*args, **kwargs) - self.haserrors = True - + def echoerr(self, *args, **kwargs): + echoerr(*args, **kwargs) + self.haserrors = True diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 91a252de..db45fd13 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -1,26 +1,29 @@ __all__ = ['gen_marked_value', 'MarkedValue'] + class MarkedValue: - def __init__(self, value, mark): - self.mark = mark - self.value = value + def __init__(self, value, mark): + self.mark = mark + self.value = value + classcache = {} -def gen_marked_value(value, mark): - if value.__class__ in classcache: - Marked = classcache[value.__class__] - else: - class Marked(MarkedValue): - for func in value.__class__.__dict__: - if func not in set(('__init__', '__new__', '__getattribute__')): - if func in set(('__eq__',)): - # HACK to make marked dictionaries always work - exec (('def {0}(self, *args):\n' - ' return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args])').format(func)) - else: - exec (('def {0}(self, *args, **kwargs):\n' - ' return self.value.{0}(*args, **kwargs)\n').format(func)) - classcache[value.__class__] = Marked - return Marked(value, mark) +def gen_marked_value(value, mark): + if value.__class__ in classcache: + Marked = classcache[value.__class__] + else: + class Marked(MarkedValue): + for func in value.__class__.__dict__: + if func not in set(('__init__', '__new__', '__getattribute__')): + if func in set(('__eq__',)): + # HACK to make marked dictionaries always work + exec (('def {0}(self, *args):\n' + ' return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args])').format(func)) + else: + exec (('def {0}(self, *args, **kwargs):\n' + ' return self.value.{0}(*args, **kwargs)\n').format(func)) + classcache[value.__class__] = Marked + + return Marked(value, mark) diff --git a/powerline/lint/markedjson/nodes.py b/powerline/lint/markedjson/nodes.py index c4f070c4..11ebb3ea 100644 --- a/powerline/lint/markedjson/nodes.py +++ b/powerline/lint/markedjson/nodes.py @@ -1,49 +1,53 @@ - class Node(object): - def __init__(self, tag, value, start_mark, end_mark): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - value = self.value - #if isinstance(value, list): - # if len(value) == 0: - # value = '' - # elif len(value) == 1: - # value = '<1 item>' - # else: - # value = '<%d items>' % len(value) - #else: - # if len(value) > 75: - # value = repr(value[:70]+u' ... ') - # else: - # value = repr(value) - value = repr(value) - return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) + def __init__(self, tag, value, start_mark, end_mark): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + + def __repr__(self): + value = self.value + #if isinstance(value, list): + # if len(value) == 0: + # value = '' + # elif len(value) == 1: + # value = '<1 item>' + # else: + # value = '<%d items>' % len(value) + #else: + # if len(value) > 75: + # value = repr(value[:70]+u' ... ') + # else: + # value = repr(value) + value = repr(value) + return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) + class ScalarNode(Node): - id = 'scalar' - def __init__(self, tag, value, - start_mark=None, end_mark=None, style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style + id = 'scalar' + + def __init__(self, tag, value, + start_mark=None, end_mark=None, style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + class CollectionNode(Node): - def __init__(self, tag, value, - start_mark=None, end_mark=None, flow_style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style + def __init__(self, tag, value, + start_mark=None, end_mark=None, flow_style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + class SequenceNode(CollectionNode): - id = 'sequence' + id = 'sequence' + class MappingNode(CollectionNode): - id = 'mapping' - + id = 'mapping' diff --git a/powerline/lint/markedjson/parser.py b/powerline/lint/markedjson/parser.py index bd74188c..5e0d473a 100644 --- a/powerline/lint/markedjson/parser.py +++ b/powerline/lint/markedjson/parser.py @@ -1,255 +1,240 @@ __all__ = ['Parser', 'ParserError'] from .error import MarkedYAMLError -from .tokens import * -from .events import * -from .scanner import * +from .tokens import * # NOQA +from .events import * # NOQA + class ParserError(MarkedYAMLError): - pass + pass + class Parser: - # Since writing a recursive-descendant parser is a straightforward task, we - # do not give many comments here. + def __init__(self): + self.current_event = None + self.yaml_version = None + self.states = [] + self.marks = [] + self.state = self.parse_stream_start - DEFAULT_TAGS = { - '!': '!', - '!!': 'tag:yaml.org,2002:', - } + def dispose(self): + # Reset the state attributes (to clear self-references) + self.states = [] + self.state = None - def __init__(self): - self.current_event = None - self.yaml_version = None - self.tag_handles = {} - self.states = [] - self.marks = [] - self.state = self.parse_stream_start + def check_event(self, *choices): + # Check the type of the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + if self.current_event is not None: + if not choices: + return True + for choice in choices: + if isinstance(self.current_event, choice): + return True + return False - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None + def peek_event(self): + # Get the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + return self.current_event - def check_event(self, *choices): - # Check the type of the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - if self.current_event is not None: - if not choices: - return True - for choice in choices: - if isinstance(self.current_event, choice): - return True - return False + def get_event(self): + # Get the next event and proceed further. + if self.current_event is None: + if self.state: + self.current_event = self.state() + value = self.current_event + self.current_event = None + return value - def peek_event(self): - # Get the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - return self.current_event + # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END + # implicit_document ::= block_node DOCUMENT-END* + # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - def get_event(self): - # Get the next event and proceed further. - if self.current_event is None: - if self.state: - self.current_event = self.state() - value = self.current_event - self.current_event = None - return value + def parse_stream_start(self): + # Parse the stream start. + token = self.get_token() + event = StreamStartEvent(token.start_mark, token.end_mark, + encoding=token.encoding) - # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END - # implicit_document ::= block_node DOCUMENT-END* - # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + # Prepare the next state. + self.state = self.parse_implicit_document_start - def parse_stream_start(self): + return event - # Parse the stream start. - token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, - encoding=token.encoding) + def parse_implicit_document_start(self): + # Parse an implicit document. + if not self.check_token(StreamEndToken): + token = self.peek_token() + start_mark = end_mark = token.start_mark + event = DocumentStartEvent(start_mark, end_mark, explicit=False) - # Prepare the next state. - self.state = self.parse_implicit_document_start + # Prepare the next state. + self.states.append(self.parse_document_end) + self.state = self.parse_node - return event + return event - def parse_implicit_document_start(self): + else: + return self.parse_document_start() - # Parse an implicit document. - if not self.check_token(StreamEndToken): - self.tag_handles = self.DEFAULT_TAGS - token = self.peek_token() - start_mark = end_mark = token.start_mark - event = DocumentStartEvent(start_mark, end_mark, explicit=False) + def parse_document_start(self): + # Parse an explicit document. + if not self.check_token(StreamEndToken): + token = self.peek_token() + self.echoerr(None, None, + "expected '', but found %r" % token.id, + token.start_mark) + return StreamEndEvent(token.start_mark, token.end_mark) + else: + # Parse the end of the stream. + token = self.get_token() + event = StreamEndEvent(token.start_mark, token.end_mark) + assert not self.states + assert not self.marks + self.state = None + return event - # Prepare the next state. - self.states.append(self.parse_document_end) - self.state = self.parse_node + def parse_document_end(self): + # Parse the document end. + token = self.peek_token() + start_mark = end_mark = token.start_mark + explicit = False + event = DocumentEndEvent(start_mark, end_mark, explicit=explicit) - return event + # Prepare the next state. + self.state = self.parse_document_start - else: - return self.parse_document_start() + return event - def parse_document_start(self): + def parse_document_content(self): + return self.parse_node() - # Parse an explicit document. - if not self.check_token(StreamEndToken): - token = self.peek_token() - start_mark = token.start_mark - self.echoerr(None, None, - "expected '', but found %r" - % self.peek_token().id, - self.peek_token().start_mark) - return StreamEndEvent(token.start_mark, token.end_mark) - else: - # Parse the end of the stream. - token = self.get_token() - event = StreamEndEvent(token.start_mark, token.end_mark) - assert not self.states - assert not self.marks - self.state = None - return event + def parse_node(self, indentless_sequence=False): + start_mark = end_mark = None + if start_mark is None: + start_mark = end_mark = self.peek_token().start_mark + event = None + implicit = True + if self.check_token(ScalarToken): + token = self.get_token() + end_mark = token.end_mark + if token.plain: + implicit = (True, False) + else: + implicit = (False, True) + event = ScalarEvent(implicit, token.value, + start_mark, end_mark, style=token.style) + self.state = self.states.pop() + elif self.check_token(FlowSequenceStartToken): + end_mark = self.peek_token().end_mark + event = SequenceStartEvent(implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_sequence_first_entry + elif self.check_token(FlowMappingStartToken): + end_mark = self.peek_token().end_mark + event = MappingStartEvent(implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_mapping_first_key + else: + token = self.peek_token() + raise ParserError("while parsing a flow node", start_mark, + "expected the node content, but found %r" % token.id, + token.start_mark) + return event - def parse_document_end(self): + def parse_flow_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_sequence_entry(first=True) - # Parse the document end. - token = self.peek_token() - start_mark = end_mark = token.start_mark - explicit = False - event = DocumentEndEvent(start_mark, end_mark, explicit=explicit) + def parse_flow_sequence_entry(self, first=False): + if not self.check_token(FlowSequenceEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + if self.check_token(FlowSequenceEndToken): + token = self.peek_token() + self.echoerr("While parsing a flow sequence", self.marks[-1], + "expected sequence value, but got %r" % token.id, token.start_mark) + else: + token = self.peek_token() + raise ParserError("while parsing a flow sequence", self.marks[-1], + "expected ',' or ']', but got %r" % token.id, token.start_mark) - # Prepare the next state. - self.state = self.parse_document_start + if not self.check_token(FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry) + return self.parse_node() + token = self.get_token() + event = SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event - return event + def parse_flow_sequence_entry_mapping_end(self): + self.state = self.parse_flow_sequence_entry + token = self.peek_token() + return MappingEndEvent(token.start_mark, token.start_mark) - def parse_document_content(self): - return self.parse_node() + def parse_flow_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_mapping_key(first=True) - def parse_node(self, indentless_sequence=False): - start_mark = end_mark = tag_mark = None - if start_mark is None: - start_mark = end_mark = self.peek_token().start_mark - event = None - implicit = True - if self.check_token(ScalarToken): - token = self.get_token() - end_mark = token.end_mark - if token.plain: - implicit = (True, False) - else: - implicit = (False, True) - event = ScalarEvent(implicit, token.value, - start_mark, end_mark, style=token.style) - self.state = self.states.pop() - elif self.check_token(FlowSequenceStartToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_sequence_first_entry - elif self.check_token(FlowMappingStartToken): - end_mark = self.peek_token().end_mark - event = MappingStartEvent(implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_mapping_first_key - else: - token = self.peek_token() - raise ParserError("while parsing a flow node", start_mark, - "expected the node content, but found %r" % token.id, - token.start_mark) - return event + def parse_flow_mapping_key(self, first=False): + if not self.check_token(FlowMappingEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + if self.check_token(FlowMappingEndToken): + token = self.peek_token() + self.echoerr("While parsing a flow mapping", self.marks[-1], + "expected mapping key, but got %r" % token.id, token.start_mark) + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected ',' or '}', but got %r" % token.id, token.start_mark) + if self.check_token(KeyToken): + token = self.get_token() + if not self.check_token(ValueToken, + FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_value) + return self.parse_node() + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected value, but got %r" % token.id, token.start_mark) + elif not self.check_token(FlowMappingEndToken): + token = self.peek_token() + expect_key = self.check_token(ValueToken, FlowEntryToken) + if not expect_key: + self.get_token() + expect_key = self.check_token(ValueToken) - def parse_flow_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_sequence_entry(first=True) + if expect_key: + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected string key, but got %r" % token.id, token.start_mark) + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected ':', but got %r" % token.id, token.start_mark) + token = self.get_token() + event = MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event - def parse_flow_sequence_entry(self, first=False): - if not self.check_token(FlowSequenceEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - if self.check_token(FlowSequenceEndToken): - token = self.peek_token() - self.echoerr("While parsing a flow sequence", self.marks[-1], - "expected sequence value, but got %r" % token.id, token.start_mark) - else: - token = self.peek_token() - raise ParserError("while parsing a flow sequence", self.marks[-1], - "expected ',' or ']', but got %r" % token.id, token.start_mark) + def parse_flow_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_key) + return self.parse_node() - if not self.check_token(FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry) - return self.parse_node() - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_sequence_entry_mapping_end(self): - self.state = self.parse_flow_sequence_entry - token = self.peek_token() - return MappingEndEvent(token.start_mark, token.start_mark) - - def parse_flow_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_mapping_key(first=True) - - def parse_flow_mapping_key(self, first=False): - if not self.check_token(FlowMappingEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - if self.check_token(FlowMappingEndToken): - token = self.peek_token() - self.echoerr("While parsing a flow mapping", self.marks[-1], - "expected mapping key, but got %r" % token.id, token.start_mark) - else: - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ',' or '}', but got %r" % token.id, token.start_mark) - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_value) - return self.parse_node() - else: - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected value, but got %r" % token.id, token.start_mark) - elif not self.check_token(FlowMappingEndToken): - token = self.peek_token() - expect_key = self.check_token(ValueToken, FlowEntryToken) - if not expect_key: - self.get_token() - expect_key = self.check_token(ValueToken) - - if expect_key: - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected string key, but got %r" % token.id, token.start_mark) - else: - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ':', but got %r" % token.id, token.start_mark) - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_key) - return self.parse_node() - - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected mapping value, but got %r" % token.id, token.start_mark) + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected mapping value, but got %r" % token.id, token.start_mark) diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index a1cf3b97..1570efa2 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -1,160 +1,144 @@ # This module contains abstractions for the input stream. You don't have to # looks further, there are no pretty code. -# -# We define two classes here. -# -# Mark(source, line, column) -# It's just a record and its only use is producing nice error messages. -# Parser does not use it for any other purposes. -# -# Reader(source, data) -# Reader determines the encoding of `data` and converts it to unicode. -# Reader provides the following methods and attributes: -# reader.peek(length=1) - return the next `length` characters -# reader.forward(length=1) - move the current position to `length` characters. -# reader.index - the number of the current character. -# reader.line, stream.column - the line and the column of the current character. __all__ = ['Reader', 'ReaderError'] from .error import YAMLError, Mark -import codecs, re - +import codecs +import re try: - from __builtin__ import unicode, unichr + from __builtin__ import unicode, unichr except ImportError: - unicode = str - unichr = chr + unicode = str # NOQA + unichr = chr # NOQA + class ReaderError(YAMLError): + def __init__(self, name, position, character, encoding, reason): + self.name = name + self.character = character + self.position = position + self.encoding = encoding + self.reason = reason - def __init__(self, name, position, character, encoding, reason): - self.name = name - self.character = character - self.position = position - self.encoding = encoding - self.reason = reason + def __str__(self): + if isinstance(self.character, bytes): + return "'%s' codec can't decode byte #x%02x: %s\n" \ + " in \"%s\", position %d" \ + % (self.encoding, ord(self.character), self.reason, + self.name, self.position) + else: + return "unacceptable character #x%04x: %s\n" \ + " in \"%s\", position %d" \ + % (self.character, self.reason, + self.name, self.position) - def __str__(self): - if isinstance(self.character, bytes): - return "'%s' codec can't decode byte #x%02x: %s\n" \ - " in \"%s\", position %d" \ - % (self.encoding, ord(self.character), self.reason, - self.name, self.position) - else: - return "unacceptable character #x%04x: %s\n" \ - " in \"%s\", position %d" \ - % (self.character, self.reason, - self.name, self.position) class Reader(object): - # Reader: - # - determines the data encoding and converts it to a unicode string, - # - checks if characters are in allowed range, - # - adds '\0' to the end. + # Reader: + # - determines the data encoding and converts it to a unicode string, + # - checks if characters are in allowed range, + # - adds '\0' to the end. - # Reader accepts - # - a file-like object with its `read` method returning `str`, + # Reader accepts + # - a file-like object with its `read` method returning `str`, - # Yeah, it's ugly and slow. + # Yeah, it's ugly and slow. + def __init__(self, stream): + self.name = None + self.stream = None + self.stream_pointer = 0 + self.eof = True + self.buffer = '' + self.pointer = 0 + self.full_buffer = unicode('') + self.full_pointer = 0 + self.raw_buffer = None + self.raw_decode = codecs.utf_8_decode + self.encoding = 'utf-8' + self.index = 0 + self.line = 0 + self.column = 0 - def __init__(self, stream): - self.name = None - self.stream = None - self.stream_pointer = 0 - self.eof = True - self.buffer = '' - self.pointer = 0 - self.full_buffer = unicode('') - self.full_pointer = 0 - self.raw_buffer = None - self.raw_decode = codecs.utf_8_decode - self.encoding = 'utf-8' - self.index = 0 - self.line = 0 - self.column = 0 + self.stream = stream + self.name = getattr(stream, 'name', "") + self.eof = False + self.raw_buffer = None - self.stream = stream - self.name = getattr(stream, 'name', "") - self.eof = False - self.raw_buffer = None + while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): + self.update_raw() + self.update(1) - while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): - self.update_raw() - self.update(1) + def peek(self, index=0): + try: + return self.buffer[self.pointer + index] + except IndexError: + self.update(index + 1) + return self.buffer[self.pointer + index] - def peek(self, index=0): - try: - return self.buffer[self.pointer+index] - except IndexError: - self.update(index+1) - return self.buffer[self.pointer+index] + def prefix(self, length=1): + if self.pointer + length >= len(self.buffer): + self.update(length) + return self.buffer[self.pointer:self.pointer + length] - def prefix(self, length=1): - if self.pointer+length >= len(self.buffer): - self.update(length) - return self.buffer[self.pointer:self.pointer+length] + def forward(self, length=1): + if self.pointer + length + 1 >= len(self.buffer): + self.update(length + 1) + while length: + ch = self.buffer[self.pointer] + self.pointer += 1 + self.full_pointer += 1 + self.index += 1 + if ch == '\n': + self.line += 1 + self.column = 0 + length -= 1 - def forward(self, length=1): - if self.pointer+length+1 >= len(self.buffer): - self.update(length+1) - while length: - ch = self.buffer[self.pointer] - self.pointer += 1 - self.full_pointer += 1 - self.index += 1 - if ch == '\n': - self.line += 1 - self.column = 0 - length -= 1 + def get_mark(self): + return Mark(self.name, self.index, self.line, self.column, + self.full_buffer, self.full_pointer) - def get_mark(self): - return Mark(self.name, self.index, self.line, self.column, - self.full_buffer, self.full_pointer) + NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0) + '-' + unichr(0xD7FF)) + (unichr(0xE000) + '-' + unichr(0xFFFD)) + ']') - NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0)+'-'+unichr(0xD7FF)) + (unichr(0xE000)+'-'+unichr(0xFFFD)) + ']') - def check_printable(self, data): - match = self.NON_PRINTABLE.search(data) - if match: - character = match.group() - position = self.index+(len(self.buffer)-self.pointer)+match.start() - raise ReaderError(self.name, position, ord(character), - 'unicode', "special characters are not allowed") + def check_printable(self, data): + match = self.NON_PRINTABLE.search(data) + if match: + character = match.group() + position = self.index + (len(self.buffer) - self.pointer) + match.start() + raise ReaderError(self.name, position, ord(character), 'unicode', "special characters are not allowed") - def update(self, length): - if self.raw_buffer is None: - return - self.buffer = self.buffer[self.pointer:] - self.pointer = 0 - while len(self.buffer) < length: - if not self.eof: - self.update_raw() - try: - data, converted = self.raw_decode(self.raw_buffer, - 'strict', self.eof) - except UnicodeDecodeError as exc: - character = self.raw_buffer[exc.start] - position = self.stream_pointer-len(self.raw_buffer)+exc.start - raise ReaderError(self.name, position, character, - exc.encoding, exc.reason) - self.check_printable(data) - self.buffer += data - self.full_buffer += data - self.raw_buffer = self.raw_buffer[converted:] - if self.eof: - self.buffer += '\0' - self.raw_buffer = None - break - - def update_raw(self, size=4096): - data = self.stream.read(size) - if self.raw_buffer is None: - self.raw_buffer = data - else: - self.raw_buffer += data - self.stream_pointer += len(data) - if not data: - self.eof = True + def update(self, length): + if self.raw_buffer is None: + return + self.buffer = self.buffer[self.pointer:] + self.pointer = 0 + while len(self.buffer) < length: + if not self.eof: + self.update_raw() + try: + data, converted = self.raw_decode(self.raw_buffer, + 'strict', self.eof) + except UnicodeDecodeError as exc: + character = self.raw_buffer[exc.start] + position = self.stream_pointer - len(self.raw_buffer) + exc.start + raise ReaderError(self.name, position, character, exc.encoding, exc.reason) + self.check_printable(data) + self.buffer += data + self.full_buffer += data + self.raw_buffer = self.raw_buffer[converted:] + if self.eof: + self.buffer += '\0' + self.raw_buffer = None + break + def update_raw(self, size=4096): + data = self.stream.read(size) + if self.raw_buffer is None: + self.raw_buffer = data + else: + self.raw_buffer += data + self.stream_pointer += len(data) + if not data: + self.eof = True diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py index 3a139b42..2121fb84 100644 --- a/powerline/lint/markedjson/resolver.py +++ b/powerline/lint/markedjson/resolver.py @@ -1,135 +1,131 @@ - __all__ = ['BaseResolver', 'Resolver'] -from .error import * -from .nodes import * +from .error import MarkedYAMLError +from .nodes import * # NOQA import re + class ResolverError(MarkedYAMLError): - pass + pass + class BaseResolver: + DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' + DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' + DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' - DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' - DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' - DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' + yaml_implicit_resolvers = {} + yaml_path_resolvers = {} - yaml_implicit_resolvers = {} - yaml_path_resolvers = {} + def __init__(self): + self.resolver_exact_paths = [] + self.resolver_prefix_paths = [] - def __init__(self): - self.resolver_exact_paths = [] - self.resolver_prefix_paths = [] + @classmethod + def add_implicit_resolver(cls, tag, regexp, first): + if not 'yaml_implicit_resolvers' in cls.__dict__: + cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() + if first is None: + first = [None] + for ch in first: + cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) - @classmethod - def add_implicit_resolver(cls, tag, regexp, first): - if not 'yaml_implicit_resolvers' in cls.__dict__: - cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() - if first is None: - first = [None] - for ch in first: - cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) + def descend_resolver(self, current_node, current_index): + if not self.yaml_path_resolvers: + return + exact_paths = {} + prefix_paths = [] + if current_node: + depth = len(self.resolver_prefix_paths) + for path, kind in self.resolver_prefix_paths[-1]: + if self.check_resolver_prefix(depth, path, kind, + current_node, current_index): + if len(path) > depth: + prefix_paths.append((path, kind)) + else: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + for path, kind in self.yaml_path_resolvers: + if not path: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + prefix_paths.append((path, kind)) + self.resolver_exact_paths.append(exact_paths) + self.resolver_prefix_paths.append(prefix_paths) - def descend_resolver(self, current_node, current_index): - if not self.yaml_path_resolvers: - return - exact_paths = {} - prefix_paths = [] - if current_node: - depth = len(self.resolver_prefix_paths) - for path, kind in self.resolver_prefix_paths[-1]: - if self.check_resolver_prefix(depth, path, kind, - current_node, current_index): - if len(path) > depth: - prefix_paths.append((path, kind)) - else: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - for path, kind in self.yaml_path_resolvers: - if not path: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - prefix_paths.append((path, kind)) - self.resolver_exact_paths.append(exact_paths) - self.resolver_prefix_paths.append(prefix_paths) + def ascend_resolver(self): + if not self.yaml_path_resolvers: + return + self.resolver_exact_paths.pop() + self.resolver_prefix_paths.pop() - def ascend_resolver(self): - if not self.yaml_path_resolvers: - return - self.resolver_exact_paths.pop() - self.resolver_prefix_paths.pop() + def check_resolver_prefix(self, depth, path, kind, + current_node, current_index): + node_check, index_check = path[depth - 1] + if isinstance(node_check, str): + if current_node.tag != node_check: + return + elif node_check is not None: + if not isinstance(current_node, node_check): + return + if index_check is True and current_index is not None: + return + if (index_check is False or index_check is None) \ + and current_index is None: + return + if isinstance(index_check, str): + if not (isinstance(current_index, ScalarNode) + and index_check == current_index.value): + return + elif isinstance(index_check, int) and not isinstance(index_check, bool): + if index_check != current_index: + return + return True - def check_resolver_prefix(self, depth, path, kind, - current_node, current_index): - node_check, index_check = path[depth-1] - if isinstance(node_check, str): - if current_node.tag != node_check: - return - elif node_check is not None: - if not isinstance(current_node, node_check): - return - if index_check is True and current_index is not None: - return - if (index_check is False or index_check is None) \ - and current_index is None: - return - if isinstance(index_check, str): - if not (isinstance(current_index, ScalarNode) - and index_check == current_index.value): - return - elif isinstance(index_check, int) and not isinstance(index_check, bool): - if index_check != current_index: - return - return True + def resolve(self, kind, value, implicit, mark=None): + if kind is ScalarNode and implicit[0]: + if value == '': + resolvers = self.yaml_implicit_resolvers.get('', []) + else: + resolvers = self.yaml_implicit_resolvers.get(value[0], []) + resolvers += self.yaml_implicit_resolvers.get(None, []) + for tag, regexp in resolvers: + if regexp.match(value): + return tag + else: + self.echoerr('While resolving plain scalar', None, + 'expected floating-point value, integer, null or boolean, but got %r' % value, + mark) + return self.DEFAULT_SCALAR_TAG + if kind is ScalarNode: + return self.DEFAULT_SCALAR_TAG + elif kind is SequenceNode: + return self.DEFAULT_SEQUENCE_TAG + elif kind is MappingNode: + return self.DEFAULT_MAPPING_TAG - def resolve(self, kind, value, implicit, mark=None): - if kind is ScalarNode and implicit[0]: - if value == '': - resolvers = self.yaml_implicit_resolvers.get('', []) - else: - resolvers = self.yaml_implicit_resolvers.get(value[0], []) - resolvers += self.yaml_implicit_resolvers.get(None, []) - for tag, regexp in resolvers: - if regexp.match(value): - return tag - else: - raise ResolverError('while resolving plain scalar', None, - "expected floating-point value, integer, null or boolean, but got %r" % value, - mark) - implicit = implicit[1] - if self.yaml_path_resolvers: - exact_paths = self.resolver_exact_paths[-1] - if kind in exact_paths: - return exact_paths[kind] - if None in exact_paths: - return exact_paths[None] - if kind is ScalarNode: - return self.DEFAULT_SCALAR_TAG - elif kind is SequenceNode: - return self.DEFAULT_SEQUENCE_TAG - elif kind is MappingNode: - return self.DEFAULT_MAPPING_TAG class Resolver(BaseResolver): - pass + pass + Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:bool', - re.compile(r'''^(?:true|false)$''', re.X), - list('yYnNtTfFoO')) + 'tag:yaml.org,2002:bool', + re.compile(r'''^(?:true|false)$''', re.X), + list('yYnNtTfFoO')) Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:float', - re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X), - list('-0123456789')) + 'tag:yaml.org,2002:float', + re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X), + list('-0123456789')) Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:int', - re.compile(r'^(?:0|-?[1-9]\d*)$', re.X), - list('-0123456789')) + 'tag:yaml.org,2002:int', + re.compile(r'^(?:0|-?[1-9]\d*)$', re.X), + list('-0123456789')) Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:null', - re.compile(r'^null$', re.X), - ['n']) + 'tag:yaml.org,2002:null', + re.compile(r'^null$', re.X), + ['n']) diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index a45d02c9..ffbaa07c 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -1,4 +1,3 @@ - # Scanner produces tokens of the following types: # STREAM-START # STREAM-END @@ -11,467 +10,459 @@ # FLOW-ENTRY # KEY # VALUE -# ALIAS(value) -# ANCHOR(value) -# TAG(value) # SCALAR(value, plain, style) # # Read comments in the Scanner code for more details. -# __all__ = ['Scanner', 'ScannerError'] from .error import MarkedYAMLError -from .tokens import * +from .tokens import * # NOQA + class ScannerError(MarkedYAMLError): - pass + pass + class SimpleKey: - # See below simple keys treatment. + # See below simple keys treatment. + def __init__(self, token_number, index, line, column, mark): + self.token_number = token_number + self.index = index + self.line = line + self.column = column + self.mark = mark - def __init__(self, token_number, index, line, column, mark): - self.token_number = token_number - self.index = index - self.line = line - self.column = column - self.mark = mark class Scanner: + def __init__(self): + """Initialize the scanner.""" + # It is assumed that Scanner and Reader will have a common descendant. + # Reader do the dirty work of checking for BOM and converting the + # input data to Unicode. It also adds NUL to the end. + # + # Reader supports the following methods + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. + + # Had we reached the end of the stream? + self.done = False + + # The number of unclosed '{' and '['. `flow_level == 0` means block + # context. + self.flow_level = 0 + + # List of processed tokens that are not yet emitted. + self.tokens = [] + + # Add the STREAM-START token. + self.fetch_stream_start() + + # Number of tokens that were emitted through the `get_token` method. + self.tokens_taken = 0 + + # Variables related to simple keys treatment. + + # A simple key is a key that is not denoted by the '?' indicator. + # We emit the KEY token before all keys, so when we find a potential + # simple key, we try to locate the corresponding ':' indicator. + # Simple keys should be limited to a single line. + + # Can a simple key start at the current position? A simple key may + # start: + # - after '{', '[', ',' (in the flow context), + self.allow_simple_key = False + + # Keep track of possible simple keys. This is a dictionary. The key + # is `flow_level`; there can be no more that one possible simple key + # for each level. The value is a SimpleKey record: + # (token_number, index, line, column, mark) + # A simple key may start with SCALAR(flow), '[', or '{' tokens. + self.possible_simple_keys = {} + + # Public methods. + + def check_token(self, *choices): + # Check if the next token is one of the given types. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + if not choices: + return True + for choice in choices: + if isinstance(self.tokens[0], choice): + return True + return False + + def peek_token(self): + # Return the next token, but do not delete if from the queue. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + return self.tokens[0] + + def get_token(self): + # Return the next token. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + self.tokens_taken += 1 + return self.tokens.pop(0) + + # Private methods. + + def need_more_tokens(self): + if self.done: + return False + if not self.tokens: + return True + # The current token may be a potential simple key, so we + # need to look further. + self.stale_possible_simple_keys() + if self.next_possible_simple_key() == self.tokens_taken: + return True + + def fetch_more_tokens(self): + + # Eat whitespaces and comments until we reach the next token. + self.scan_to_next_token() + + # Remove obsolete possible simple keys. + self.stale_possible_simple_keys() + + # Peek the next character. + ch = self.peek() + + # Is it the end of stream? + if ch == '\0': + return self.fetch_stream_end() + + # Note: the order of the following checks is NOT significant. + + # Is it the flow sequence start indicator? + if ch == '[': + return self.fetch_flow_sequence_start() + + # Is it the flow mapping start indicator? + if ch == '{': + return self.fetch_flow_mapping_start() + + # Is it the flow sequence end indicator? + if ch == ']': + return self.fetch_flow_sequence_end() + + # Is it the flow mapping end indicator? + if ch == '}': + return self.fetch_flow_mapping_end() + + # Is it the flow entry indicator? + if ch == ',': + return self.fetch_flow_entry() + + # Is it the value indicator? + if ch == ':' and self.flow_level: + return self.fetch_value() + + # Is it a double quoted scalar? + if ch == '\"': + return self.fetch_double() + + # It must be a plain scalar then. + if self.check_plain(): + return self.fetch_plain() + + # No? It's an error. Let's produce a nice error message. + raise ScannerError("while scanning for the next token", None, + "found character %r that cannot start any token" % ch, + self.get_mark()) + + # Simple keys treatment. + + def next_possible_simple_key(self): + # Return the number of the nearest possible simple key. Actually we + # don't need to loop through the whole dictionary. We may replace it + # with the following code: + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number + min_token_number = None + for level in self.possible_simple_keys: + key = self.possible_simple_keys[level] + if min_token_number is None or key.token_number < min_token_number: + min_token_number = key.token_number + return min_token_number + + def stale_possible_simple_keys(self): + # Remove entries that are no longer possible simple keys. According to + # the YAML specification, simple keys + # - should be limited to a single line, + # Disabling this procedure will allow simple keys of any length and + # height (may cause problems if indentation is broken though). + for level in list(self.possible_simple_keys): + key = self.possible_simple_keys[level] + if key.line != self.line: + del self.possible_simple_keys[level] + + def save_possible_simple_key(self): + # The next token may start a simple key. We check if it's possible + # and save its position. This function is called for + # SCALAR(flow), '[', and '{'. + + # The next token might be a simple key. Let's save it's number and + # position. + if self.allow_simple_key: + self.remove_possible_simple_key() + token_number = self.tokens_taken + len(self.tokens) + key = SimpleKey(token_number, + self.index, self.line, self.column, self.get_mark()) + self.possible_simple_keys[self.flow_level] = key + + def remove_possible_simple_key(self): + # Remove the saved possible key position at the current flow level. + if self.flow_level in self.possible_simple_keys: + del self.possible_simple_keys[self.flow_level] + + # Fetchers. + + def fetch_stream_start(self): + # We always add STREAM-START as the first token and STREAM-END as the + # last token. + + # Read the token. + mark = self.get_mark() + + # Add STREAM-START. + self.tokens.append(StreamStartToken(mark, mark, + encoding=self.encoding)) + + def fetch_stream_end(self): + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + self.possible_simple_keys = {} + + # Read the token. + mark = self.get_mark() + + # Add STREAM-END. + self.tokens.append(StreamEndToken(mark, mark)) + + # The steam is finished. + self.done = True - def __init__(self): - """Initialize the scanner.""" - # It is assumed that Scanner and Reader will have a common descendant. - # Reader do the dirty work of checking for BOM and converting the - # input data to Unicode. It also adds NUL to the end. - # - # Reader supports the following methods - # self.peek(i=0) # peek the next i-th character - # self.prefix(l=1) # peek the next l characters - # self.forward(l=1) # read the next l characters and move the pointer. - - # Had we reached the end of the stream? - self.done = False - - # The number of unclosed '{' and '['. `flow_level == 0` means block - # context. - self.flow_level = 0 - - # List of processed tokens that are not yet emitted. - self.tokens = [] - - # Add the STREAM-START token. - self.fetch_stream_start() - - # Number of tokens that were emitted through the `get_token` method. - self.tokens_taken = 0 - - # Variables related to simple keys treatment. - - # A simple key is a key that is not denoted by the '?' indicator. - # We emit the KEY token before all keys, so when we find a potential - # simple key, we try to locate the corresponding ':' indicator. - # Simple keys should be limited to a single line. - - # Can a simple key start at the current position? A simple key may - # start: - # - after '{', '[', ',' (in the flow context), - self.allow_simple_key = False - - # Keep track of possible simple keys. This is a dictionary. The key - # is `flow_level`; there can be no more that one possible simple key - # for each level. The value is a SimpleKey record: - # (token_number, index, line, column, mark) - # A simple key may start with SCALAR(flow), '[', or '{' tokens. - self.possible_simple_keys = {} - - # Public methods. - - def check_token(self, *choices): - # Check if the next token is one of the given types. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - if not choices: - return True - for choice in choices: - if isinstance(self.tokens[0], choice): - return True - return False - - def peek_token(self): - # Return the next token, but do not delete if from the queue. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - return self.tokens[0] - - def get_token(self): - # Return the next token. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - self.tokens_taken += 1 - return self.tokens.pop(0) - - # Private methods. - - def need_more_tokens(self): - if self.done: - return False - if not self.tokens: - return True - # The current token may be a potential simple key, so we - # need to look further. - self.stale_possible_simple_keys() - if self.next_possible_simple_key() == self.tokens_taken: - return True - - def fetch_more_tokens(self): - - # Eat whitespaces and comments until we reach the next token. - self.scan_to_next_token() - - # Remove obsolete possible simple keys. - self.stale_possible_simple_keys() - - # Peek the next character. - ch = self.peek() - - # Is it the end of stream? - if ch == '\0': - return self.fetch_stream_end() - - # Note: the order of the following checks is NOT significant. - - # Is it the flow sequence start indicator? - if ch == '[': - return self.fetch_flow_sequence_start() - - # Is it the flow mapping start indicator? - if ch == '{': - return self.fetch_flow_mapping_start() - - # Is it the flow sequence end indicator? - if ch == ']': - return self.fetch_flow_sequence_end() - - # Is it the flow mapping end indicator? - if ch == '}': - return self.fetch_flow_mapping_end() - - # Is it the flow entry indicator? - if ch == ',': - return self.fetch_flow_entry() - - # Is it the value indicator? - if ch == ':' and self.flow_level: - return self.fetch_value() - - # Is it a double quoted scalar? - if ch == '\"': - return self.fetch_double() - - # It must be a plain scalar then. - if self.check_plain(): - return self.fetch_plain() - - # No? It's an error. Let's produce a nice error message. - raise ScannerError("while scanning for the next token", None, - "found character %r that cannot start any token" % ch, - self.get_mark()) - - # Simple keys treatment. - - def next_possible_simple_key(self): - # Return the number of the nearest possible simple key. Actually we - # don't need to loop through the whole dictionary. We may replace it - # with the following code: - # if not self.possible_simple_keys: - # return None - # return self.possible_simple_keys[ - # min(self.possible_simple_keys.keys())].token_number - min_token_number = None - for level in self.possible_simple_keys: - key = self.possible_simple_keys[level] - if min_token_number is None or key.token_number < min_token_number: - min_token_number = key.token_number - return min_token_number - - def stale_possible_simple_keys(self): - # Remove entries that are no longer possible simple keys. According to - # the YAML specification, simple keys - # - should be limited to a single line, - # Disabling this procedure will allow simple keys of any length and - # height (may cause problems if indentation is broken though). - for level in list(self.possible_simple_keys): - key = self.possible_simple_keys[level] - if key.line != self.line: - del self.possible_simple_keys[level] - - def save_possible_simple_key(self): - # The next token may start a simple key. We check if it's possible - # and save its position. This function is called for - # SCALAR(flow), '[', and '{'. - - # The next token might be a simple key. Let's save it's number and - # position. - if self.allow_simple_key: - self.remove_possible_simple_key() - token_number = self.tokens_taken+len(self.tokens) - key = SimpleKey(token_number, - self.index, self.line, self.column, self.get_mark()) - self.possible_simple_keys[self.flow_level] = key - - def remove_possible_simple_key(self): - # Remove the saved possible key position at the current flow level. - if self.flow_level in self.possible_simple_keys: - key = self.possible_simple_keys[self.flow_level] - - del self.possible_simple_keys[self.flow_level] - - # Fetchers. - - def fetch_stream_start(self): - # We always add STREAM-START as the first token and STREAM-END as the - # last token. - - # Read the token. - mark = self.get_mark() - - # Add STREAM-START. - self.tokens.append(StreamStartToken(mark, mark, - encoding=self.encoding)) - - - def fetch_stream_end(self): - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - self.possible_simple_keys = {} - - # Read the token. - mark = self.get_mark() - - # Add STREAM-END. - self.tokens.append(StreamEndToken(mark, mark)) - - # The steam is finished. - self.done = True - - def fetch_flow_sequence_start(self): - self.fetch_flow_collection_start(FlowSequenceStartToken) - - def fetch_flow_mapping_start(self): - self.fetch_flow_collection_start(FlowMappingStartToken) - - def fetch_flow_collection_start(self, TokenClass): - - # '[' and '{' may start a simple key. - self.save_possible_simple_key() - - # Increase the flow level. - self.flow_level += 1 - - # Simple keys are allowed after '[' and '{'. - self.allow_simple_key = True - - # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_end(self): - self.fetch_flow_collection_end(FlowSequenceEndToken) - - def fetch_flow_mapping_end(self): - self.fetch_flow_collection_end(FlowMappingEndToken) - - def fetch_flow_collection_end(self, TokenClass): - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Decrease the flow level. - self.flow_level -= 1 - - # No simple keys after ']' or '}'. - self.allow_simple_key = False - - # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_value(self): - # Do we determine a simple key? - if self.flow_level in self.possible_simple_keys: - - # Add KEY. - key = self.possible_simple_keys[self.flow_level] - del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number-self.tokens_taken, - KeyToken(key.mark, key.mark)) - - # There cannot be two simple keys one after another. - self.allow_simple_key = False - - # Add VALUE. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(ValueToken(start_mark, end_mark)) - - def fetch_flow_entry(self): - - # Simple keys are allowed after ','. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add FLOW-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(FlowEntryToken(start_mark, end_mark)) - - def fetch_double(self): - # A flow scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after flow scalars. - self.allow_simple_key = False - - # Scan and add SCALAR. - self.tokens.append(self.scan_flow_scalar()) - - def fetch_plain(self): - - self.save_possible_simple_key() - - # No simple keys after plain scalars. - self.allow_simple_key = False - - # Scan and add SCALAR. May change `allow_simple_key`. - self.tokens.append(self.scan_plain()) - - # Checkers. - - def check_plain(self): - return self.peek() in '0123456789-ntf' - - # Scanners. - - def scan_to_next_token(self): - while self.peek() in ' \t\n': - self.forward() - - def scan_flow_scalar(self): - # See the specification for details. - # Note that we loose indentation rules for quoted scalars. Quoted - # scalars don't need to adhere indentation because " and ' clearly - # mark the beginning and the end of them. Therefore we are less - # restrictive then the specification requires. We only need to check - # that document separators are not included in scalars. - chunks = [] - start_mark = self.get_mark() - quote = self.peek() - self.forward() - chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) - while self.peek() != quote: - chunks.extend(self.scan_flow_scalar_spaces(start_mark)) - chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) - self.forward() - end_mark = self.get_mark() - return ScalarToken(''.join(chunks), False, start_mark, end_mark, '"') - - ESCAPE_REPLACEMENTS = { - 'b': '\x08', - 't': '\x09', - 'n': '\x0A', - 'f': '\x0C', - 'r': '\x0D', - '\"': '\"', - '\\': '\\', - } - - ESCAPE_CODES = { - 'u': 4, - } - - def scan_flow_scalar_non_spaces(self, start_mark): - # See the specification for details. - chunks = [] - while True: - length = 0 - while self.peek(length) not in '\"\\\0 \t\n': - length += 1 - if length: - chunks.append(self.prefix(length)) - self.forward(length) - ch = self.peek() - if ch == '\\': - self.forward() - ch = self.peek() - if ch in self.ESCAPE_REPLACEMENTS: - chunks.append(self.ESCAPE_REPLACEMENTS[ch]) - self.forward() - elif ch in self.ESCAPE_CODES: - length = self.ESCAPE_CODES[ch] - self.forward() - for k in range(length): - if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexdecimal numbers, but found %r" % - (length, self.peek(k)), self.get_mark()) - code = int(self.prefix(length), 16) - chunks.append(chr(code)) - self.forward(length) - else: - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "found unknown escape character %r" % ch, self.get_mark()) - else: - return chunks - - def scan_flow_scalar_spaces(self, start_mark): - # See the specification for details. - chunks = [] - length = 0 - while self.peek(length) in ' \t': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch == '\0': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected end of stream", self.get_mark()) - elif ch == '\n': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected line end", self.get_mark()) - else: - chunks.append(whitespaces) - return chunks - - def scan_plain(self): - chunks = [] - start_mark = self.get_mark() - spaces = [] - while True: - length = 0 - while True: - ch = self.peek(length) - if self.peek(length) not in 'eE.0123456789nul-tr+fas': - break - length += 1 - if length == 0: - break - self.allow_simple_key = False - chunks.extend(spaces) - chunks.append(self.prefix(length)) - self.forward(length) - end_mark = self.get_mark() - return ScalarToken(''.join(chunks), True, start_mark, end_mark) + def fetch_flow_sequence_start(self): + self.fetch_flow_collection_start(FlowSequenceStartToken) + + def fetch_flow_mapping_start(self): + self.fetch_flow_collection_start(FlowMappingStartToken) + + def fetch_flow_collection_start(self, TokenClass): + + # '[' and '{' may start a simple key. + self.save_possible_simple_key() + + # Increase the flow level. + self.flow_level += 1 + + # Simple keys are allowed after '[' and '{'. + self.allow_simple_key = True + + # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_end(self): + self.fetch_flow_collection_end(FlowSequenceEndToken) + + def fetch_flow_mapping_end(self): + self.fetch_flow_collection_end(FlowMappingEndToken) + + def fetch_flow_collection_end(self, TokenClass): + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Decrease the flow level. + self.flow_level -= 1 + + # No simple keys after ']' or '}'. + self.allow_simple_key = False + + # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_value(self): + # Do we determine a simple key? + if self.flow_level in self.possible_simple_keys: + + # Add KEY. + key = self.possible_simple_keys[self.flow_level] + del self.possible_simple_keys[self.flow_level] + self.tokens.insert(key.token_number - self.tokens_taken, + KeyToken(key.mark, key.mark)) + + # There cannot be two simple keys one after another. + self.allow_simple_key = False + + # Add VALUE. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(ValueToken(start_mark, end_mark)) + + def fetch_flow_entry(self): + + # Simple keys are allowed after ','. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add FLOW-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(FlowEntryToken(start_mark, end_mark)) + + def fetch_double(self): + # A flow scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after flow scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. + self.tokens.append(self.scan_flow_scalar()) + + def fetch_plain(self): + + self.save_possible_simple_key() + + # No simple keys after plain scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. May change `allow_simple_key`. + self.tokens.append(self.scan_plain()) + + # Checkers. + + def check_plain(self): + return self.peek() in '0123456789-ntf' + + # Scanners. + + def scan_to_next_token(self): + while self.peek() in ' \t\n': + self.forward() + + def scan_flow_scalar(self): + # See the specification for details. + # Note that we loose indentation rules for quoted scalars. Quoted + # scalars don't need to adhere indentation because " and ' clearly + # mark the beginning and the end of them. Therefore we are less + # restrictive then the specification requires. We only need to check + # that document separators are not included in scalars. + chunks = [] + start_mark = self.get_mark() + quote = self.peek() + self.forward() + chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) + while self.peek() != quote: + chunks.extend(self.scan_flow_scalar_spaces(start_mark)) + chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) + self.forward() + end_mark = self.get_mark() + return ScalarToken(''.join(chunks), False, start_mark, end_mark, '"') + + ESCAPE_REPLACEMENTS = { + 'b': '\x08', + 't': '\x09', + 'n': '\x0A', + 'f': '\x0C', + 'r': '\x0D', + '\"': '\"', + '\\': '\\', + } + + ESCAPE_CODES = { + 'u': 4, + } + + def scan_flow_scalar_non_spaces(self, start_mark): + # See the specification for details. + chunks = [] + while True: + length = 0 + while self.peek(length) not in '\"\\\0 \t\n': + length += 1 + if length: + chunks.append(self.prefix(length)) + self.forward(length) + ch = self.peek() + if ch == '\\': + self.forward() + ch = self.peek() + if ch in self.ESCAPE_REPLACEMENTS: + chunks.append(self.ESCAPE_REPLACEMENTS[ch]) + self.forward() + elif ch in self.ESCAPE_CODES: + length = self.ESCAPE_CODES[ch] + self.forward() + for k in range(length): + if self.peek(k) not in '0123456789ABCDEFabcdef': + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexdecimal numbers, but found %r" % + (length, self.peek(k)), self.get_mark()) + code = int(self.prefix(length), 16) + chunks.append(chr(code)) + self.forward(length) + else: + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "found unknown escape character %r" % ch, self.get_mark()) + else: + return chunks + + def scan_flow_scalar_spaces(self, start_mark): + # See the specification for details. + chunks = [] + length = 0 + while self.peek(length) in ' \t': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch == '\0': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark()) + elif ch == '\n': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected line end", self.get_mark()) + else: + chunks.append(whitespaces) + return chunks + + def scan_plain(self): + chunks = [] + start_mark = self.get_mark() + spaces = [] + while True: + length = 0 + while True: + if self.peek(length) not in 'eE.0123456789nul-tr+fas': + break + length += 1 + if length == 0: + break + self.allow_simple_key = False + chunks.extend(spaces) + chunks.append(self.prefix(length)) + self.forward(length) + end_mark = self.get_mark() + return ScalarToken(''.join(chunks), True, start_mark, end_mark) diff --git a/powerline/lint/markedjson/tokens.py b/powerline/lint/markedjson/tokens.py index 98ba90e8..8c5b38c8 100644 --- a/powerline/lint/markedjson/tokens.py +++ b/powerline/lint/markedjson/tokens.py @@ -1,57 +1,65 @@ - class Token(object): - def __init__(self, start_mark, end_mark): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in self.__dict__ - if not key.endswith('_mark')] - attributes.sort() - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) + def __init__(self, start_mark, end_mark): + self.start_mark = start_mark + self.end_mark = end_mark + + def __repr__(self): + attributes = [key for key in self.__dict__ + if not key.endswith('_mark')] + attributes.sort() + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) -#class BOMToken(Token): -# id = '' class StreamStartToken(Token): - id = '' - def __init__(self, start_mark=None, end_mark=None, - encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding + id = '' + + def __init__(self, start_mark=None, end_mark=None, + encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + class StreamEndToken(Token): - id = '' + id = '' + class FlowSequenceStartToken(Token): - id = '[' + id = '[' + class FlowMappingStartToken(Token): - id = '{' + id = '{' + class FlowSequenceEndToken(Token): - id = ']' + id = ']' + class FlowMappingEndToken(Token): - id = '}' + id = '}' + class KeyToken(Token): - id = '?' + id = '?' + class ValueToken(Token): - id = ':' + id = ':' + class FlowEntryToken(Token): - id = ',' + id = ',' + class ScalarToken(Token): - id = '' - def __init__(self, value, plain, start_mark, end_mark, style=None): - self.value = value - self.plain = plain - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style + id = '' + def __init__(self, value, plain, start_mark, end_mark, style=None): + self.value = value + self.plain = plain + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style From 49ad855eb517e546ba69130a89cde0da58fa090a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 08:00:40 +0400 Subject: [PATCH 0454/1472] Rename YAMLError to JSONError and MarkedYAMLError to MarkedError --- powerline/lint/markedjson/composer.py | 4 ++-- powerline/lint/markedjson/constructor.py | 4 ++-- powerline/lint/markedjson/error.py | 8 ++++---- powerline/lint/markedjson/parser.py | 4 ++-- powerline/lint/markedjson/reader.py | 4 ++-- powerline/lint/markedjson/resolver.py | 4 ++-- powerline/lint/markedjson/scanner.py | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/powerline/lint/markedjson/composer.py b/powerline/lint/markedjson/composer.py index fe3c3b86..303e6f23 100644 --- a/powerline/lint/markedjson/composer.py +++ b/powerline/lint/markedjson/composer.py @@ -1,11 +1,11 @@ __all__ = ['Composer', 'ComposerError'] -from .error import MarkedYAMLError +from .error import MarkedError from .events import * # NOQA from .nodes import * # NOQA -class ComposerError(MarkedYAMLError): +class ComposerError(MarkedError): pass diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index 35429a5d..bdc5c6e3 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -1,6 +1,6 @@ __all__ = ['BaseConstructor', 'Constructor', 'ConstructorError'] -from .error import MarkedYAMLError +from .error import MarkedError from .nodes import * # NOQA from .markedvalue import gen_marked_value @@ -23,7 +23,7 @@ def marked(func): return f -class ConstructorError(MarkedYAMLError): +class ConstructorError(MarkedError): pass diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index 0828c2c2..b9e10b34 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -1,4 +1,4 @@ -__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError', 'echoerr'] +__all__ = ['Mark', 'JSONError', 'MarkedError', 'echoerr'] import sys @@ -53,7 +53,7 @@ class Mark: return where.encode('utf-8') -class YAMLError(Exception): +class JSONError(Exception): pass @@ -81,8 +81,8 @@ def format_error(context=None, context_mark=None, problem=None, problem_mark=Non return '\n'.join(lines) -class MarkedYAMLError(YAMLError): +class MarkedError(JSONError): def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None): - YAMLError.__init__(self, format_error(context, context_mark, problem, + JSONError.__init__(self, format_error(context, context_mark, problem, problem_mark, note)) diff --git a/powerline/lint/markedjson/parser.py b/powerline/lint/markedjson/parser.py index 5e0d473a..998de6db 100644 --- a/powerline/lint/markedjson/parser.py +++ b/powerline/lint/markedjson/parser.py @@ -1,11 +1,11 @@ __all__ = ['Parser', 'ParserError'] -from .error import MarkedYAMLError +from .error import MarkedError from .tokens import * # NOQA from .events import * # NOQA -class ParserError(MarkedYAMLError): +class ParserError(MarkedError): pass diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index 1570efa2..536f16aa 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -3,7 +3,7 @@ __all__ = ['Reader', 'ReaderError'] -from .error import YAMLError, Mark +from .error import JSONError, Mark import codecs import re @@ -15,7 +15,7 @@ except ImportError: unichr = chr # NOQA -class ReaderError(YAMLError): +class ReaderError(JSONError): def __init__(self, name, position, character, encoding, reason): self.name = name self.character = character diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py index 2121fb84..f628a872 100644 --- a/powerline/lint/markedjson/resolver.py +++ b/powerline/lint/markedjson/resolver.py @@ -1,12 +1,12 @@ __all__ = ['BaseResolver', 'Resolver'] -from .error import MarkedYAMLError +from .error import MarkedError from .nodes import * # NOQA import re -class ResolverError(MarkedYAMLError): +class ResolverError(MarkedError): pass diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index ffbaa07c..a5750e82 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -16,11 +16,11 @@ __all__ = ['Scanner', 'ScannerError'] -from .error import MarkedYAMLError +from .error import MarkedError from .tokens import * # NOQA -class ScannerError(MarkedYAMLError): +class ScannerError(MarkedError): pass From 9dc69d91ad0db9d9b1ce3335cb2ff39512445fff Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 08:40:16 +0400 Subject: [PATCH 0455/1472] Make Reader also produce MarkedError, remove JSONError --- powerline/lint/markedjson/error.py | 23 ++++++----- powerline/lint/markedjson/reader.py | 59 +++++++++++++---------------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index b9e10b34..6627831b 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -1,17 +1,24 @@ -__all__ = ['Mark', 'JSONError', 'MarkedError', 'echoerr'] +__all__ = ['Mark', 'MarkedError', 'echoerr', 'NON_PRINTABLE'] import sys +import re + + +NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0) + '-' + unichr(0xD7FF)) + (unichr(0xE000) + '-' + unichr(0xFFFD)) + ']') + + +def repl(s): + return '' % ord(s.group()) def strtrans(s): - return s.replace('\t', '>---') + return NON_PRINTABLE.sub(repl, s.replace('\t', '>---')) class Mark: - def __init__(self, name, index, line, column, buffer, pointer): + def __init__(self, name, line, column, buffer, pointer): self.name = name - self.index = index self.line = line self.column = column self.buffer = buffer @@ -53,10 +60,6 @@ class Mark: return where.encode('utf-8') -class JSONError(Exception): - pass - - def echoerr(*args, **kwargs): sys.stderr.write('\n') sys.stderr.write(format_error(*args, **kwargs) + '\n') @@ -81,8 +84,8 @@ def format_error(context=None, context_mark=None, problem=None, problem_mark=Non return '\n'.join(lines) -class MarkedError(JSONError): +class MarkedError(Exception): def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None): - JSONError.__init__(self, format_error(context, context_mark, problem, + Exception.__init__(self, format_error(context, context_mark, problem, problem_mark, note)) diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index 536f16aa..7c8a7b91 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -3,10 +3,9 @@ __all__ = ['Reader', 'ReaderError'] -from .error import JSONError, Mark +from .error import MarkedError, Mark, NON_PRINTABLE import codecs -import re try: from __builtin__ import unicode, unichr @@ -15,25 +14,8 @@ except ImportError: unichr = chr # NOQA -class ReaderError(JSONError): - def __init__(self, name, position, character, encoding, reason): - self.name = name - self.character = character - self.position = position - self.encoding = encoding - self.reason = reason - - def __str__(self): - if isinstance(self.character, bytes): - return "'%s' codec can't decode byte #x%02x: %s\n" \ - " in \"%s\", position %d" \ - % (self.encoding, ord(self.character), self.reason, - self.name, self.position) - else: - return "unacceptable character #x%04x: %s\n" \ - " in \"%s\", position %d" \ - % (self.character, self.reason, - self.name, self.position) +class ReaderError(MarkedError): + pass class Reader(object): @@ -83,9 +65,7 @@ class Reader(object): self.update(length) return self.buffer[self.pointer:self.pointer + length] - def forward(self, length=1): - if self.pointer + length + 1 >= len(self.buffer): - self.update(length + 1) + def update_pointer(self, length): while length: ch = self.buffer[self.pointer] self.pointer += 1 @@ -94,20 +74,26 @@ class Reader(object): if ch == '\n': self.line += 1 self.column = 0 + else: + self.column += 1 length -= 1 - def get_mark(self): - return Mark(self.name, self.index, self.line, self.column, - self.full_buffer, self.full_pointer) + def forward(self, length=1): + if self.pointer + length + 1 >= len(self.buffer): + self.update(length + 1) + self.update_pointer(length) - NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0) + '-' + unichr(0xD7FF)) + (unichr(0xE000) + '-' + unichr(0xFFFD)) + ']') + def get_mark(self): + return Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer) def check_printable(self, data): - match = self.NON_PRINTABLE.search(data) + match = NON_PRINTABLE.search(data) if match: character = match.group() - position = self.index + (len(self.buffer) - self.pointer) + match.start() - raise ReaderError(self.name, position, ord(character), 'unicode', "special characters are not allowed") + self.update_pointer(match.start()) + raise ReaderError('while reading from stream', None, + 'found special characters which are not allowed', + Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer)) def update(self, length): if self.raw_buffer is None: @@ -123,11 +109,18 @@ class Reader(object): except UnicodeDecodeError as exc: character = self.raw_buffer[exc.start] position = self.stream_pointer - len(self.raw_buffer) + exc.start - raise ReaderError(self.name, position, character, exc.encoding, exc.reason) - self.check_printable(data) + data, converted = self.raw_decode(self.raw_buffer[:exc.start], 'strict', self.eof) + self.buffer += data + self.full_buffer += data + '<' + str(ord(character)) + '>' + self.raw_buffer = self.raw_buffer[converted:] + self.update_pointer(exc.start - 1) + raise ReaderError('while reading from stream', None, + 'found character #x%04x that cannot be decoded by UTF-8 codec' % ord(character), + Mark(self.name, self.line, self.column, self.full_buffer, position)) self.buffer += data self.full_buffer += data self.raw_buffer = self.raw_buffer[converted:] + self.check_printable(data) if self.eof: self.buffer += '\0' self.raw_buffer = None From 24d662c8234352a2a1d685254b01c50476926bf1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 08:49:07 +0400 Subject: [PATCH 0456/1472] Make lint more error-prone Target: report as much errors as possible in a single run without failure --- powerline/lint/__init__.py | 50 +++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 7ca24bac..00b20318 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,6 +1,6 @@ from powerline.lint.markedjson import load from powerline import load_json_config, Powerline -from powerline.lint.markedjson.error import echoerr +from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes import itertools import sys @@ -659,7 +659,7 @@ def check_segment_name(name, data, context, echoerr): return True, False, hadproblem else: if name not in context[0][1].get('segment_data', {}): - top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', {}) + top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) if data['theme'] == top_theme_name: top_theme = {} else: @@ -742,7 +742,7 @@ def check_highlight_groups(hl_groups, data, context, echoerr): def check_segment_data_key(key, data, context, echoerr): ext = data['ext'] - top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', {}) + top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) is_top_theme = (data['theme'] == top_theme_name) if is_top_theme: themes = data['ext_theme_configs'].values() @@ -868,14 +868,36 @@ def check(path=None): lhadproblem[0] = True return r - main_config = load_json_config(search_paths, 'config', load=load_config, open=open_file) - hadproblem = main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),))[1] + hadproblem = False + try: + main_config = load_json_config(search_paths, 'config', load=load_config, open=open_file) + except IOError: + main_config = {} + sys.stderr.write('\nConfiguration file not found: config.json\n') + hadproblem = True + except MarkedError as e: + main_config = {} + sys.stderr.write(str(e) + '\n') + hadproblem = True + else: + if main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),))[1]: + hadproblem = True import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] - colors_config = load_json_config(search_paths, 'colors', load=load_config, open=open_file) - if colors_spec.match(colors_config, context=(('', colors_config),))[1]: + try: + colors_config = load_json_config(search_paths, 'colors', load=load_config, open=open_file) + except IOError: + colors_config = {} + sys.stderr.write('\nConfiguration file not found: colors.json\n') hadproblem = True + except MarkedError as e: + colors_config = {} + sys.stderr.write(str(e) + '\n') + hadproblem = True + else: + if colors_spec.match(colors_config, context=(('', colors_config),))[1]: + hadproblem = True if lhadproblem[0]: hadproblem = True @@ -885,7 +907,12 @@ def check(path=None): data = {'ext': ext, 'colors_config': colors_config} for colorscheme, cfile in configs['colorschemes'][ext].items(): with open_file(cfile) as config_file_fp: - config, lhadproblem = load(config_file_fp) + try: + config, lhadproblem = load(config_file_fp) + except MarkedError as e: + sys.stderr.write(str(e) + '\n') + hadproblem = True + continue if lhadproblem: hadproblem = True colorscheme_configs[ext][colorscheme] = config @@ -900,7 +927,12 @@ def check(path=None): for ext in configs['themes']: for theme, sfile in configs['themes'][ext].items(): with open_file(sfile) as config_file_fp: - config, lhadproblem = load(config_file_fp) + try: + config, lhadproblem = load(config_file_fp) + except MarkedError as e: + sys.stderr.write(str(e) + '\n') + hadproblem = True + continue if lhadproblem: hadproblem = True theme_configs[ext][theme] = config From 1ef1ad7cfe80012eafd4be4b3d4cb2e888b7dfc4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 08:52:23 +0400 Subject: [PATCH 0457/1472] Add missing import --- powerline/lint/markedjson/error.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index 6627831b..66fcacef 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -4,6 +4,11 @@ __all__ = ['Mark', 'MarkedError', 'echoerr', 'NON_PRINTABLE'] import sys import re +try: + from __builtin__ import unichr +except ImportError: + unichr = chr # NOQA + NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0) + '-' + unichr(0xD7FF)) + (unichr(0xE000) + '-' + unichr(0xFFFD)) + ']') From fe6f1bf4d579058d2665911c85411bdc23df3d11 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 18:24:15 +0400 Subject: [PATCH 0458/1472] Handle AttributeError also when importing segment function --- powerline/lint/__init__.py | 7 ++++++- powerline/lint/markedjson/reader.py | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 00b20318..05108d57 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -598,9 +598,14 @@ def check_segment_name(name, data, context, echoerr): func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name)) except ImportError: echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='failed to import function {0} from module {1}'.format(name, module), + problem='failed to import module {0}'.format(module), problem_mark=module.mark) return True, False, True + except AttributeError: + echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), + problem='failed to load function {0} from module {1}'.format(name, module), + problem_mark=match_name.mark) + return True, False, True if not callable(func): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index 7c8a7b91..47b99362 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -8,10 +8,9 @@ from .error import MarkedError, Mark, NON_PRINTABLE import codecs try: - from __builtin__ import unicode, unichr + from __builtin__ import unicode except ImportError: unicode = str # NOQA - unichr = chr # NOQA class ReaderError(MarkedError): From 2c445a935621124e33ddd1e2140b9c5c982ed7db Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:09:34 +0400 Subject: [PATCH 0459/1472] Add more tests to test_configuration Dynamic configuration tests. Purpose: make sure that default configuration does not throw. Tests catch only very trivial problems (like missing imports after refactoring) --- tests/test_configuration.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 5851ec3f..1ae26355 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -57,6 +57,35 @@ class TestConfig(TestCase): from powerline.shell import ShellPowerline with replace_module_attr(common, 'urllib_read', urllib_read): ShellPowerline(Args(ext=['tmux'])).renderer.render() + reload(common) + + def test_zsh(self): + from powerline.shell import ShellPowerline + ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt')).renderer.render() + + def test_bash(self): + from powerline.shell import ShellPowerline + ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})])).renderer.render() + + def test_ipython(self): + from powerline.ipython import IpythonPowerline + + class IpyPowerline(IpythonPowerline): + path = None + config_overrides = None + theme_overrides = {} + + IpyPowerline().renderer.render() + + def test_wm(self): + from powerline.segments import common + from imp import reload + reload(common) + from powerline.shell import ShellPowerline + with replace_module_attr(common, 'urllib_read', urllib_read): + from powerline import Powerline + Powerline(ext='wm', renderer_module='pango_markup').renderer.render() + reload(common) old_cwd = None From 696478593d3c9f0ee8352ce5760796866ad5f43d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:13:43 +0400 Subject: [PATCH 0460/1472] Move some functions out of classes No need to have static methods that are not supposed to be overridden and that do not benefit from `self' argument they do not receive --- powerline/renderer.py | 25 +++++++++---------------- powerline/renderers/shell.py | 11 +++++++++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index e91be682..4d3c497b 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -9,6 +9,13 @@ except NameError: NBSP = ' ' +def construct_returned_value(rendered_highlighted, segments, output_raw): + if output_raw: + return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) + else: + return rendered_highlighted + + class Renderer(object): def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, **options): self.__dict__.update(options) @@ -52,7 +59,7 @@ class Renderer(object): if not width: # No width specified, so we don't need to crop or pad anything - return self._returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) + return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] @@ -77,7 +84,7 @@ class Renderer(object): rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() - return self._returned_value(rendered_highlighted, segments, output_raw) + return construct_returned_value(rendered_highlighted, segments, output_raw) def _render_segments(self, theme, segments, render_highlighted=True): '''Internal segment rendering method. @@ -154,24 +161,10 @@ class Renderer(object): segment['_len'] = len(segment['_rendered_raw']) yield segment - @staticmethod - def _returned_value(rendered_highlighted, segments, output_raw): - if output_raw: - return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) - else: - return rendered_highlighted - @staticmethod def escape(string): return string - @staticmethod - def _int_to_rgb(int): - r = (int >> 16) & 0xff - g = (int >> 8) & 0xff - b = int & 0xff - return r, g, b - def hlstyle(fg=None, bg=None, attr=None): raise NotImplementedError diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index b2ad0197..e6e82019 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -4,6 +4,13 @@ from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +def int_to_rgb(num): + r = (num >> 16) & 0xff + g = (num >> 8) & 0xff + b = num & 0xff + return r, g, b + + class ShellRenderer(Renderer): '''Powerline shell segment renderer.''' escape_hl_start = '' @@ -25,7 +32,7 @@ class ShellRenderer(Renderer): ansi += [39] else: if self.term_truecolor: - ansi += [38, 2] + list(self._int_to_rgb(fg[1])) + ansi += [38, 2] + list(int_to_rgb(fg[1])) else: ansi += [38, 5, fg[0]] if bg is not None: @@ -33,7 +40,7 @@ class ShellRenderer(Renderer): ansi += [49] else: if self.term_truecolor: - ansi += [48, 2] + list(self._int_to_rgb(bg[1])) + ansi += [48, 2] + list(int_to_rgb(bg[1])) else: ansi += [48, 5, bg[0]] if attr is not None: From b2b05cb81cff8da74b4a2ac47e30fe23b904db14 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:22:18 +0400 Subject: [PATCH 0461/1472] Removed underscore_to_camelcase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to bother both developers (that need to create class names compatible with file name, which is not obvious if not looking into main Powerline class) and computer (that needs to recompute class name each time) if it is possible to bother only developers (or only computer, but this contributes to higher startup times). About not obvious: when you look into zsh_prompt.py and see only ZshPromptRenderer name you only think that powerline upstream is following strict code style. You don’t think there is a technical reason for such naming, like you don’t think there is technical reason for having blank lines. When you look into zsh_prompt.py and see `renderer = ZshPromptRenderer` it is obvious that there is technical reason for writing code this way because new variable is never used in the module itself. --- powerline/__init__.py | 8 +++----- powerline/lib/__init__.py | 5 ----- powerline/renderers/bash_prompt.py | 3 +++ powerline/renderers/ipython.py | 3 +++ powerline/renderers/pango_markup.py | 3 +++ powerline/renderers/shell.py | 3 +++ powerline/renderers/tmux.py | 3 +++ powerline/renderers/vim.py | 4 ++++ powerline/renderers/zsh_prompt.py | 3 +++ tests/test_configuration.py | 3 +-- tests/test_lib.py | 6 +----- 11 files changed, 27 insertions(+), 17 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 752d845a..3ef4c554 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -6,19 +6,18 @@ import os import sys from powerline.colorscheme import Colorscheme -from powerline.lib import underscore_to_camelcase def open_file(path): return open(path, 'r') -def load_json_config(search_paths, config_file, load=json.load, open=open_file): +def load_json_config(search_paths, config_file, load=json.load, open_file=open_file): config_file += '.json' for path in search_paths: config_file_path = os.path.join(path, config_file) if os.path.isfile(config_file_path): - with open(config_file_path) as config_file_fp: + with open_file(config_file_path) as config_file_fp: return load(config_file_fp) raise IOError('Config file not found in search path: {0}'.format(config_file)) @@ -65,9 +64,8 @@ class Powerline(object): # Load and initialize extension renderer renderer_module_name = renderer_module or ext renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) - renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) try: - Renderer = getattr(__import__(renderer_module_import, fromlist=[renderer_class_name]), renderer_class_name) + Renderer = __import__(renderer_module_import, fromlist=['renderer']).renderer except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 549d7455..fc97407d 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -7,11 +7,6 @@ from powerline.lib.humanize_bytes import humanize_bytes # NOQA from powerline.lib.url import urllib_read, urllib_urlencode # NOQA -def underscore_to_camelcase(string): - '''Return a underscore_separated_string as CamelCase.''' - return ''.join(word.capitalize() or '_' for word in string.split('_')) - - def mergedicts(d1, d2): '''Recursively merge two dictionaries. First dictionary is modified in-place. ''' diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index b9f911ad..01f42f74 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -11,3 +11,6 @@ class BashPromptRenderer(ShellRenderer): @staticmethod def escape(string): return string.replace('\\', '\\\\').replace('$', '\\$').replace('`', '\\`') + + +renderer = BashPromptRenderer diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index d1e4c7bd..2fa63acd 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -7,3 +7,6 @@ class IpythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' escape_hl_start = '\x01' escape_hl_end = '\x02' + + +renderer = IpythonRenderer diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index 54e78868..f5e85b6a 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -29,3 +29,6 @@ class PangoMarkupRenderer(Renderer): if attr & ATTR_UNDERLINE: awesome_attr += ['underline="single"'] return '' + contents + '' + + +renderer = PangoMarkupRenderer diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index e6e82019..40777c46 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -65,3 +65,6 @@ class ShellRenderer(Renderer): @staticmethod def escape(string): return string.replace('\\', '\\\\') + + +renderer = ShellRenderer diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 57dd3cd8..34e4329e 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -39,3 +39,6 @@ class TmuxRenderer(Renderer): else: tmux_attr += ['nounderscore'] return '#[' + ','.join(tmux_attr) + ']' + + +renderer = TmuxRenderer diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 5099e4dc..13f80d21 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -9,6 +9,7 @@ from powerline.theme import Theme import vim + vim_mode = vim_get_func('mode') vim_getwinvar = vim_get_func('getwinvar') vim_setwinvar = vim_get_func('setwinvar') @@ -120,3 +121,6 @@ class VimRenderer(Renderer): attr=','.join(hl_group['attr']), )) return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' + + +renderer = VimRenderer diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index f66f7359..2e6e5e77 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -11,3 +11,6 @@ class ZshPromptRenderer(ShellRenderer): @staticmethod def escape(string): return string.replace('%', '%%').replace('\\', '\\\\') + + +renderer = ZshPromptRenderer diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 1ae26355..bd5f9e89 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -81,9 +81,8 @@ class TestConfig(TestCase): from powerline.segments import common from imp import reload reload(common) - from powerline.shell import ShellPowerline + from powerline import Powerline with replace_module_attr(common, 'urllib_read', urllib_read): - from powerline import Powerline Powerline(ext='wm', renderer_module='pango_markup').renderer.render() reload(common) diff --git a/tests/test_lib.py b/tests/test_lib.py index e48730d1..5b3e2192 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from powerline.lib import mergedicts, underscore_to_camelcase, add_divider_highlight_group, humanize_bytes +from powerline.lib import mergedicts, add_divider_highlight_group, humanize_bytes from powerline.lib.vcs import guess from subprocess import call, PIPE import os @@ -8,10 +8,6 @@ from tests import TestCase class TestLib(TestCase): - def test_underscore_to_camelcase(self): - self.assertEqual(underscore_to_camelcase('abc_def_ghi'), 'AbcDefGhi') - self.assertEqual(underscore_to_camelcase('abc_def__ghi'), 'AbcDef_Ghi') - def test_mergedicts(self): d = {} mergedicts(d, {'abc': {'def': 'ghi'}}) From 417e823e9695ab7e669314bc7a7d93b4669b9fc3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:24:00 +0400 Subject: [PATCH 0462/1472] Fix two flake8 errors --- powerline/lint/__init__.py | 2 +- powerline/lint/markedjson/reader.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 05108d57..0242e1da 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -604,7 +604,7 @@ def check_segment_name(name, data, context, echoerr): except AttributeError: echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), problem='failed to load function {0} from module {1}'.format(name, module), - problem_mark=match_name.mark) + problem_mark=name.mark) return True, False, True if not callable(func): diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index 47b99362..f59605ee 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -88,7 +88,6 @@ class Reader(object): def check_printable(self, data): match = NON_PRINTABLE.search(data) if match: - character = match.group() self.update_pointer(match.start()) raise ReaderError('while reading from stream', None, 'found special characters which are not allowed', From 6f3703e312a5cdedebf28037b0f9f90e2eff5217 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Mar 2013 13:08:01 +0400 Subject: [PATCH 0463/1472] Improve ambiwidth handling, readd additional_escapes Fixes #307 --- docs/source/configuration.rst | 19 +++++++++++++++++++ docs/source/installation/osx.rst | 3 ++- .../installation/troubleshooting-common.rst | 12 +++++++++++- powerline/__init__.py | 6 +++++- powerline/renderer.py | 16 ++++++++++++++-- powerline/renderers/vim.py | 12 ++++++++++++ tests/vim.py | 5 +++-- 7 files changed, 66 insertions(+), 7 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 05e10a53..e4d8bcbc 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -120,6 +120,25 @@ Common configuration is a subdictionary that is a value of ``common`` key in to the terminal emulator. See the :ref:`term-feature-support-matrix` for information on whether your terminal emulator supports 24-bit colors. +.. _config-common-ambiwidth: + +``ambiwidth`` + Tells powerline what to do with characters with East Asian Width Class + Ambigious (such as Euro, Registered Sign, Copyright Sign, Greek + letters, Cyrillic letters). Valid values: any positive integer; it is + suggested that you only set it to 1 (default) or 2. + +.. _config-common-additional_escapes: + +``additional_escapes`` + Valid for shell extensions, makes sense only if :ref:`term_truecolor + ` is enabled. Is to be set from command-line + (unless you are sure you always need it). Controls additional escaping that + is needed for tmux/screen to work with terminal true color escape codes: + normally tmux/screen prevent terminal emulator from receiving these control + codes thus rendering powerline prompt colorless. Valid values: ``"tmux"``, + ``"screen"``, ``null`` (default). + ``dividers`` Defines the dividers used in all Powerline extensions. This option should usually only be changed if you don't have a patched font, or if diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index b76bcf19..aa029f11 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -90,7 +90,8 @@ Statusline is getting wrapped to the next line in iTerm2 * Turn off “Treat ambigious-width characters as double width” in `Preferences --> Text`. -* Alternative: remove fancy dividers and other fancy symbols from configuration. +* Alternative: remove fancy dividers (they suck in this case), set + :ref:`ambiwidth ` to 2. I receive a ``NameError`` when trying to use Powerline with MacVim! ------------------------------------------------------------------- diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index ef3da240..a0142e38 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -10,6 +10,15 @@ I'm using tmux and Powerline looks like crap, what's wrong? :guilabel:`Set locale variables automatically` in :menuselection:`Profiles --> Terminal --> Environment`. +I’m using tmux/screen and Powerline is colorless +------------------------------------------------ + +* If the above advices do not help, then you need to disable + :ref:`term_truecolor `. +* Alternative: set :ref:`additional_escapes ` + to ``"tmux"`` or ``"screen"``. Note that it is known to work perfectly in + screen, but in tmux it may produce ugly spaces. + My vim statusline has strange characters like ``^B`` in it! ----------------------------------------------------------- @@ -49,4 +58,5 @@ My vim statusline is not displayed completely and has too much spaces --------------------------------------------------------------------- * Be sure you have ``ambiwidth`` option set to ``single``. -* Alternative: remove fancy dividers and other fancy symbols from configuration. +* Alternative: set :ref:`ambiwidth ` to 2, remove fancy + dividers (they suck when ``ambiwidth`` is set to double). diff --git a/powerline/__init__.py b/powerline/__init__.py index 752d845a..935190ac 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -71,7 +71,11 @@ class Powerline(object): except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) - options = {'term_truecolor': common_config.get('term_truecolor', False)} + options = {'term_truecolor': common_config.get('term_truecolor', False), + 'ambiwidth': common_config.get('ambiwidth', 1), + 'tmux_escape': common_config.get('additional_escapes') == 'tmux', + 'screen_escape': common_config.get('additional_escapes') == 'screen', + } self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) @staticmethod diff --git a/powerline/renderer.py b/powerline/renderer.py index e91be682..4ecacd24 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet -from powerline.theme import Theme +from powerline.theme import Theme, u +from unicodedata import east_asian_width, combining try: @@ -17,6 +18,17 @@ class Renderer(object): self.local_themes = local_themes self.theme_kwargs = theme_kwargs self.colorscheme = colorscheme + self.width_data = { + 'N': 1, # Neutral + 'Na': 1, # Narrow + 'A': getattr(self, 'ambiwidth', 1), # Ambigious + 'H': 1, # Half-width + 'W': 2, # Wide + 'F': 2, # Fullwidth + } + + def strwidth(self, string): + return sum((0 if combining(symbol) else self.width_data[east_asian_width(symbol)] for symbol in string)) def get_theme(self, matcher_info): return self.theme @@ -151,7 +163,7 @@ class Renderer(object): else: segment['_rendered_raw'] += contents_raw segment['_rendered_hl'] += contents_highlighted - segment['_len'] = len(segment['_rendered_raw']) + segment['_len'] = self.strwidth(segment['_rendered_raw']) yield segment @staticmethod diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 5099e4dc..245d7293 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -22,6 +22,11 @@ class VimRenderer(Renderer): '''Powerline vim segment renderer.''' def __init__(self, *args, **kwargs): + if not hasattr(vim, 'strwidth'): + # Hope nobody want to change this at runtime + if vim.eval('&ambiwidth') == 'double': + kwargs = dict(**kwargs) + kwargs['ambigious'] = 2 super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} @@ -40,6 +45,13 @@ class VimRenderer(Renderer): else: return self.theme + if hasattr(vim, 'strwidth'): + @staticmethod + def strwidth(string): + # Does not work with tabs, but neither is strwidth from default + # renderer + return vim.strwidth(string.encode('utf-8')) + def render(self, window_id, winidx, current): '''Render all segments. diff --git a/tests/vim.py b/tests/vim.py index 6253956f..1b9b820c 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -5,8 +5,9 @@ _window = 0 _mode = 'n' _buf_purge_events = set() _options = { - 'paste': 0, - } + 'paste': 0, + 'ambiwidth': 'single', +} _last_bufnr = 0 _highlights = {} From 1960e8329fe14454a30d05566713977e3b6b8445 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 21:27:28 +0400 Subject: [PATCH 0464/1472] Remove useless import --- powerline/renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 4ecacd24..bdd995b9 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from powerline.theme import Theme, u +from powerline.theme import Theme from unicodedata import east_asian_width, combining From 271cfe06b1164fc98fa15581b46645adf994f987 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 00:58:27 +0400 Subject: [PATCH 0465/1472] Add parameter to disable current and user directories shortening Fixes #322 --- powerline/segments/vim.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c70eb3a1..ed794e10 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -151,16 +151,23 @@ def readonly_indicator(segment_info, text=''): @requires_segment_info -def file_directory(segment_info, shorten_home=False): +def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False): '''Return file directory (head component of the file path). + :param bool shorten_user: + shorten ``$HOME`` directory to :file:`~/` + + :param bool shorten_cwd: + shorten current directory to :file:`./` + :param bool shorten_home: shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. ''' name = segment_info['buffer'].name if not name: return None - file_directory = vim_funcs['fnamemodify'](name, ':~:.:h') + file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') + + (':.' if shorten_home else '') + ':h') if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory + os.sep if file_directory else None From d8c64c5e3a8f6c5acd7f8fda8c042e6ea1e5610c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 07:33:47 +0400 Subject: [PATCH 0466/1472] Fix keyword argument name --- powerline/lint/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 0242e1da..6267e2f9 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -875,7 +875,7 @@ def check(path=None): hadproblem = False try: - main_config = load_json_config(search_paths, 'config', load=load_config, open=open_file) + main_config = load_json_config(search_paths, 'config', load=load_config, open_file=open_file) except IOError: main_config = {} sys.stderr.write('\nConfiguration file not found: config.json\n') @@ -891,7 +891,7 @@ def check(path=None): import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] try: - colors_config = load_json_config(search_paths, 'colors', load=load_config, open=open_file) + colors_config = load_json_config(search_paths, 'colors', load=load_config, open_file=open_file) except IOError: colors_config = {} sys.stderr.write('\nConfiguration file not found: colors.json\n') From e24703dbdd5f0da4336e9af392be12472822cad4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 14:44:35 +0400 Subject: [PATCH 0467/1472] Add status_colors argument to branch segments Closes #206 --- .../colorschemes/shell/default.json | 2 ++ .../colorschemes/shell/solarized.json | 2 ++ .../colorschemes/vim/default.json | 2 ++ .../colorschemes/vim/solarized.json | 2 ++ powerline/segments/common.py | 19 ++++++++++++++++--- powerline/segments/vim.py | 10 ++++++++-- tests/test_segments.py | 10 ++++++++-- 7 files changed, 40 insertions(+), 7 deletions(-) diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 0320d807..639c1f9b 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -5,6 +5,8 @@ "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "virtualenv": { "fg": "white", "bg": "darkcyan" }, "branch": { "fg": "gray9", "bg": "gray2" }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray2" }, + "branch_clean": { "fg": "gray9", "bg": "gray2" }, "cwd": { "fg": "gray9", "bg": "gray4" }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, "cwd:divider": { "fg": "gray7", "bg": "gray4" }, diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index d847cef6..5bf86723 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -5,6 +5,8 @@ "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, "virtualenv": { "fg": "oldlace", "bg": "green" }, "branch": { "fg": "gray61", "bg": "royalblue5" }, + "branch_dirty": { "fg": "yellow", "bg": "royalblue5" }, + "branch_clean": { "fg": "gray61", "bg": "royalblue5" }, "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 14a3e780..ec158793 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -8,6 +8,8 @@ "paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] }, "readonly_indicator": { "fg": "brightestred", "bg": "gray4" }, "branch": { "fg": "gray9", "bg": "gray4" }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray4" }, + "branch_clean": { "fg": "gray9", "bg": "gray4" }, "branch:divider": { "fg": "gray7", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 0a441994..57f4bfb1 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -8,6 +8,8 @@ "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, "readonly_indicator": { "fg": "red", "bg": "darkgreencopper" }, "branch": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper" }, + "branch_clean": { "fg": "lightyellow", "bg": "darkgreencopper" }, "branch:divider": { "fg": "gray61", "bg": "darkgreencopper" }, "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper" }, "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index df931ca8..106df1dd 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -41,11 +41,24 @@ def user(): }] -def branch(): - '''Return the current VCS branch.''' +def branch(status_colors=True): + '''Return the current VCS branch.@ + + :param bool status_colors: + determines whether repository status will be used to determine highlighting. Default: True. + + Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. + ''' repo = guess(path=os.path.abspath(os.getcwd())) if repo: - return repo.branch() + branch = repo.branch() + if status_colors: + return [{ + 'contents': branch, + 'highlight_group': ['branch_dirty' if repo.status().strip() else 'branch_clean', 'branch'], + }] + else: + return branch return None diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c70eb3a1..54dbad92 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -306,15 +306,21 @@ def modified_buffers(text='+ ', join_str=','): @requires_segment_info @memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell) -def branch(segment_info): +def branch(segment_info, status_colors=True): '''Return the current working branch. + :param bool status_colors: + determines whether repository status will be used to determine highlighting. Default: True. + + Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. + Divider highlight group used: ``branch:divider``. ''' repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return [{ 'contents': repo.branch(), + 'highlight_group': (['branch_dirty' if repo.status().strip() else 'branch_clean'] if status_colors else []) + ['branch'], 'divider_highlight_group': 'branch:divider', }] return None @@ -351,5 +357,5 @@ def repository_status(segment_info): '''Return the status for the current repo.''' repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: - return repo.status() + return repo.status().strip() or None return None diff --git a/tests/test_segments.py b/tests/test_segments.py index d41c145e..5211fe01 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -46,8 +46,14 @@ class TestCommon(TestCase): self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): - with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path))): - self.assertEqual(common.branch(), 'tests') + with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: ' ')): + self.assertEqual(common.branch(status_colors=False), 'tests') + self.assertEqual(common.branch(status_colors=True), + [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) + with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ')): + self.assertEqual(common.branch(status_colors=False), 'tests') + self.assertEqual(common.branch(), + [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) with replace_module_attr(common, 'guess', lambda path: None): self.assertEqual(common.branch(), None) From b1b0f2427ee916da8d36cd65ff1f0fa17f244853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 13 Mar 2013 14:09:26 +0100 Subject: [PATCH 0468/1472] Install fonts to OTF instead of TTF in PKGBUILDs Fixes #320 --- packages/archlinux/python-powerline-git/PKGBUILD | 6 +++--- packages/archlinux/python2-powerline-git/PKGBUILD | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index a0c2d717..980964fb 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=python-powerline-git -pkgver=20130308 +pkgver=20130313 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -43,10 +43,10 @@ package() { python setup.py install --root="${pkgdir}" --optimize=1 || return 1 msg2 "Installing fonts..." - install -dm755 "${pkgdir}/usr/share/fonts/TTF/" + install -dm755 "${pkgdir}/usr/share/fonts/OTF/" install -dm755 "${pkgdir}/etc/fonts/conf.avail" install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" + install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 8239b93d..05c0e7aa 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=python2-powerline-git -pkgver=20130308 +pkgver=20130313 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -44,10 +44,10 @@ package() { python2 setup.py install --root="${pkgdir}" --optimize=1 || return 1 msg2 "Installing fonts..." - install -dm755 "${pkgdir}/usr/share/fonts/TTF/" + install -dm755 "${pkgdir}/usr/share/fonts/OTF/" install -dm755 "${pkgdir}/etc/fonts/conf.avail" install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/TTF/PowerlineSymbols.otf" + install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" From 48470221f015bf1d0fbf47b6ea2fa99d34014497 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Mar 2013 20:02:02 +0400 Subject: [PATCH 0469/1472] Make git.Repository.status() also return None Fixes #326 --- powerline/lib/vcs/git.py | 5 +++-- powerline/segments/common.py | 2 +- powerline/segments/vim.py | 2 +- tests/test_lib.py | 3 ++- tests/test_segments.py | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index ec99f0c6..f7ea2e83 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -73,7 +73,8 @@ try: | git.GIT_STATUS_INDEX_MODIFIED | git.GIT_STATUS_INDEX_DELETED): index_column = 'I' - return wt_column + index_column + untracked_column + r = wt_column + index_column + untracked_column + return r if r != ' ' else None def branch(self): try: @@ -132,7 +133,7 @@ except ImportError: wt_column = 'D' r = wt_column + index_column + untracked_column - return r + return r if r != ' ' else None def branch(self): for line in self._gitcmd('branch', '-l'): diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 106df1dd..63142a8b 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -55,7 +55,7 @@ def branch(status_colors=True): if status_colors: return [{ 'contents': branch, - 'highlight_group': ['branch_dirty' if repo.status().strip() else 'branch_clean', 'branch'], + 'highlight_group': ['branch_dirty' if repo.status() else 'branch_clean', 'branch'], }] else: return branch diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 54dbad92..52d30cc5 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -320,7 +320,7 @@ def branch(segment_info, status_colors=True): if repo: return [{ 'contents': repo.branch(), - 'highlight_group': (['branch_dirty' if repo.status().strip() else 'branch_clean'] if status_colors else []) + ['branch'], + 'highlight_group': (['branch_dirty' if repo.status() else 'branch_clean'] if status_colors else []) + ['branch'], 'divider_highlight_group': 'branch:divider', }] return None diff --git a/tests/test_lib.py b/tests/test_lib.py index e48730d1..23f7d53c 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -48,7 +48,7 @@ class TestVCS(TestCase): repo = guess(path=GIT_REPO) self.assertNotEqual(repo, None) self.assertEqual(repo.branch(), 'master') - self.assertEqual(repo.status(), ' ') + self.assertEqual(repo.status(), None) self.assertEqual(repo.status('file'), None) with open(os.path.join(GIT_REPO, 'file'), 'w') as f: f.write('abc') @@ -69,6 +69,7 @@ class TestVCS(TestCase): repo = guess(path=HG_REPO) self.assertNotEqual(repo, None) self.assertEqual(repo.branch(), 'default') + self.assertEqual(repo.status(), None) with open(os.path.join(HG_REPO, 'file'), 'w') as f: f.write('abc') f.flush() diff --git a/tests/test_segments.py b/tests/test_segments.py index 5211fe01..491e4a3a 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -46,7 +46,7 @@ class TestCommon(TestCase): self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): - with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: ' ')): + with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None)): self.assertEqual(common.branch(status_colors=False), 'tests') self.assertEqual(common.branch(status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) From 24097293e52d47ca1532820ccd66ae773788474e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 15 Mar 2013 08:31:47 +0100 Subject: [PATCH 0470/1472] Catch all urllib errors in urrlib_read() --- powerline/lib/url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/url.py b/powerline/lib/url.py index ef46df70..0ff53ade 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -13,7 +13,7 @@ def urllib_read(url): import urllib2 try: return urllib2.urlopen(url, timeout=5).read() - except urllib2.HTTPError: + except: return From dda500a673bac129f9877fbf14b2029363bf6955 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Mar 2013 20:25:55 +0530 Subject: [PATCH 0471/1472] Only use locally stored bzr branch nickname Fixes #324 (slow vcs branch segment with bzr checkout) --- powerline/lib/vcs/bzr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index feb7844c..89c22823 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -58,6 +58,6 @@ class Repository(object): def branch(self): try: b = branch.Branch.open(self.directory) - return b.nick or None + return b._get_nick(local=True) or None except: pass From f141574d89877f7ad0fbffb1c41d8d1c32ad2f89 Mon Sep 17 00:00:00 2001 From: Ali B Date: Fri, 15 Mar 2013 10:45:59 +1100 Subject: [PATCH 0472/1472] Make vim Visual Block mode orange in Solarized colourscheme. This makes it consistent with the styles of the other visual modes. Reference issue #147. --- powerline/config_files/colorschemes/vim/solarized.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 57f4bfb1..9a55a237 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -75,6 +75,11 @@ "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } } }, + "^V": { + "groups": { + "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + } + }, "R": { "groups": { "mode": { "fg": "oldlace", "bg": "red", "attr": ["bold"] } From c4e618e17c6839d61234e8a695330dd5a7a06251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 15 Mar 2013 14:35:08 +0100 Subject: [PATCH 0473/1472] Update authors and contributors list --- docs/source/license-credits.rst | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index 167c613e..5b344ddc 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -11,14 +11,20 @@ Powerline is licensed under the `MIT license Credits ======= -:Authors: - * `Kim Silkebækken `_ - * `ZyX-I `_ -:Main contributors: - * `Liam Curry `_ +Authors +------- -The glyphs in the font patcher are created by Fabrizio Schiavi, creator of -the excellent coding font `Pragmata Pro`_. +* `Kim Silkebækken `_ +* `ZyX-I `_ +* `Kovid Goyal `_ + +Contributors +------------ + +* `List of contributors + `_ +* The glyphs in the font patcher are created by Fabrizio Schiavi, creator of + the excellent coding font `Pragmata Pro`_. .. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm From edfc090ddb977752557febb1c99981a4efb612da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 15 Mar 2013 15:20:00 +0100 Subject: [PATCH 0474/1472] Add initial CONTRIBUTING document Based on https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md Ref #287 --- CONTRIBUTING.rst | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 CONTRIBUTING.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..96ebab7f --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,77 @@ +***************** +How to contribute +***************** + +So you want to contribute to the Powerline project? Awesome! This document +describes the guidelines you should follow when making contributions to the +project. + +**Please note that these guidelines aren't mandatory in any way, but your +pull request will be merged a lot faster if you follow them.** + +Getting started +=============== + +* Make sure you have a `GitHub account `_. +* Submit an `issue on GitHub + `_, assuming one does not + already exist. + + * Clearly describe the issue. + * If the issue is a bug: Make sure you include steps to reproduce, and + include the earliest revision that you know has the issue. + +* Fork the repository on GitHub. + +Making changes +============== + +* Create a topic branch from where you want to base your work. + + * Powerline uses the `Git Flow + `_ branching + model. + * Most contributions should be based off the ``develop`` branch. + * Prefix your branch with ``feature/`` if you're working on a new feature. + * Include the issue number in your topic branch, e.g. + ``321-fix-some-error`` or ``feature/123-a-cool-feature``. + +* Make commits of logical units. +* Run your code through ``flake8`` and fix any programming style errors. Use + common sense regarding whitespace warnings, not all warnings need to be + fixed. +* Make sure your commit messages are in the `proper format + `_. + The summary must be no longer than 70 characters. Refer to any related + issues with e.g. ``Ref #123`` or ``Fixes #234`` at the bottom of the + commit message. Commit messages can use Markdown with the following + exceptions: + + * No HTML extensions. + * Only indented code blocks (no ``````` blocks). + * Long links should be moved to the bottom if they make the text wrap or + extend past 72 columns. + +* Make sure you have added the necessary tests for your changes. +* Run *all* the tests to assure nothing else was accidentally broken. + +Programming style +----------------- + +* The project uses *tabs for indentation* and *spaces for alignment*, this + is also included in a vim modeline on top of every script file. +* Run your code through ``flake8 + --ignore=W191,E501,E121,E122,E123,E128,E225`` to fix any style errors. Use + common sense regarding whitespace warnings, not all ``flake8`` warnings + need to be fixed. +* Trailing whitespace to indicate a continuing paragraph is OK in comments, + documentation and commit messages. + +Submitting changes +================== + +* Push your changes to a topic branch in your fork of the repository. +* If necessary, use ``git rebase -i `` to squash or reword commits + before submitting a pull request. +* Submit a pull request to `Lokaltog's repository + `_. From d966921f0422c8d6322d89739ae5963d48ef08ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 15 Mar 2013 15:20:19 +0100 Subject: [PATCH 0475/1472] Remove contibuting info from docs --- docs/source/license-credits.rst | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/docs/source/license-credits.rst b/docs/source/license-credits.rst index 5b344ddc..8fafc92c 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-credits.rst @@ -27,25 +27,3 @@ Contributors the excellent coding font `Pragmata Pro`_. .. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm - -Contributing -============ - -If you experience any bugs or have any feature requests, please `open an -issue on GitHub `_. - -Pull request guidelines -======================= - -This project uses `Git Flow`_ for maintaining a clean history and -a consistent way of branching and merging new features. All commit messages -must follow the guidelines described in `Tim Pope's blog post about git -commit messages`_. - -All code must use tabs for indentation and spaces for alignment. - -Python code should pass flake8 tests with ``flake8 ---ignore=W191,E501,E121,E122,E123,E128``. - -.. _`Git Flow`: http://nvie.com/posts/a-successful-git-branching-model/ -.. _`Tim Pope's blog post about git commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html From 137a415a9bc4708ee4e4ee0c96e5551506758240 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Mar 2013 08:19:59 +0400 Subject: [PATCH 0476/1472] Add .local.vimrc with configuration for the lint checker Ref #314 --- .local.vimrc | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .local.vimrc diff --git a/.local.vimrc b/.local.vimrc new file mode 100644 index 00000000..766efbfd --- /dev/null +++ b/.local.vimrc @@ -0,0 +1,2 @@ +setlocal noexpandtab +let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128' From df19981f656554ec6fe6ecfbf6e36502d82e50ac Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Mar 2013 08:23:22 +0400 Subject: [PATCH 0477/1472] Add tools/generate_gradients.py and tools/colors_find.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First helps in generating gradients. Usage: python tools/generate_gradients.py '[color1, …]' itemnum[ "show"] where color is either [100, 127, 46] (cterm colors) or ["ff00ff", "feffef"] (RGB colors) Second is to determine what name will be better suitable for some RGB color --- tools/colors.map | 646 ++++++++++++++++++++++++++++++++++++ tools/colors_find.py | 38 +++ tools/generate_gradients.py | 105 ++++++ 3 files changed, 789 insertions(+) create mode 100644 tools/colors.map create mode 100644 tools/colors_find.py create mode 100644 tools/generate_gradients.py diff --git a/tools/colors.map b/tools/colors.map new file mode 100644 index 00000000..61d8c4a7 --- /dev/null +++ b/tools/colors.map @@ -0,0 +1,646 @@ +Grey 545454 +Grey, Silver C0C0C0 +grey BEBEBE +LightGray D3D3D3 +LightSlateGrey 778899 +SlateGray 708090 +SlateGray1 C6E2FF +SlateGray2 B9D3EE +SlateGray3 9FB6CD +SlateGray4 6C7B8B +black 000000 +grey0 000000 +grey1 030303 +grey2 050505 +grey3 080808 +grey4 0A0A0A +grey5 0D0D0D +grey6 0F0F0F +grey7 121212 +grey8 141414 +grey9 171717 +grey10 1A1A1A +grey11 1C1C1C +grey12 1F1F1F +grey13 212121 +grey14 242424 +grey15 262626 +grey16 292929 +grey17 2B2B2B +grey18 2E2E2E +grey19 303030 +grey20 333333 +grey21 363636 +grey22 383838 +grey23 3B3B3B +grey24 3D3D3D +grey25 404040 +grey26 424242 +grey27 454545 +grey28 474747 +grey29 4A4A4A +grey30 4D4D4D +grey31 4F4F4F +grey32 525252 +grey33 545454 +grey34 575757 +grey35 595959 +grey36 5C5C5C +grey37 5E5E5E +grey38 616161 +grey39 636363 +grey40 666666 +grey41, DimGrey 696969 +grey42 6B6B6B +grey43 6E6E6E +grey44 707070 +grey45 737373 +grey46 757575 +grey47 787878 +grey48 7A7A7A +grey49 7D7D7D +grey50 7F7F7F +grey51 828282 +grey52 858585 +grey53 878787 +grey54 8A8A8A +grey55 8C8C8C +grey56 8F8F8F +grey57 919191 +grey58 949494 +grey59 969696 +grey60 999999 +grey61 9C9C9C +grey62 9E9E9E +grey63 A1A1A1 +grey64 A3A3A3 +grey65 A6A6A6 +grey66 A8A8A8 +grey67 ABABAB +grey68 ADADAD +grey69 B0B0B0 +grey70 B3B3B3 +grey71 B5B5B5 +grey72 B8B8B8 +grey73 BABABA +grey74 BDBDBD +grey75 BFBFBF +grey76 C2C2C2 +grey77 C4C4C4 +grey78 C7C7C7 +grey79 C9C9C9 +grey80 CCCCCC +grey81 CFCFCF +grey82 D1D1D1 +grey83 D4D4D4 +grey84 D6D6D6 +grey85 D9D9D9 +grey86 DBDBDB +grey87 DEDEDE +grey88 E0E0E0 +grey89 E3E3E3 +grey90 E5E5E5 +grey91 E8E8E8 +grey92 EBEBEB +grey93 EDEDED +grey94 F0F0F0 +grey95 F2F2F2 +grey96 F5F5F5 +grey97 F7F7F7 +grey98 FAFAFA +grey99 FCFCFC +grey100, White FFFFFF +Dark Slate Grey 2F4F4F +Dim Grey 545454 +Very Light Grey CDCDCD +Free Speech Grey 635688 +AliceBlue F0F8FF +BlueViolet 8A2BE2 +Cadet Blue 5F9F9F +CadetBlue 5F9EA0 +CadetBlue 5F9EA0 +CadetBlue1 98F5FF +CadetBlue2 8EE5EE +CadetBlue3 7AC5CD +CadetBlue4 53868B +Corn Flower Blue 42426F +CornflowerBlue 6495ED +DarkSlateBlue 483D8B +DarkTurquoise 00CED1 +DeepSkyBlue 00BFFF +DeepSkyBlue1 00BFFF +DeepSkyBlue2 00B2EE +DeepSkyBlue3 009ACD +DeepSkyBlue4 00688B +DodgerBlue 1E90FF +DodgerBlue1 1E90FF +DodgerBlue2 1C86EE +DodgerBlue3 1874CD +DodgerBlue4 104E8B +LightBlue ADD8E6 +LightBlue1 BFEFFF +LightBlue2 B2DFEE +LightBlue3 9AC0CD +LightBlue4 68838B +LightCyan E0FFFF +LightCyan1 E0FFFF +LightCyan2 D1EEEE +LightCyan3 B4CDCD +LightCyan4 7A8B8B +LightSkyBlue 87CEFA +LightSkyBlue1 B0E2FF +LightSkyBlue2 A4D3EE +LightSkyBlue3 8DB6CD +LightSkyBlue4 607B8B +LightSlateBlue 8470FF +LightSteelBlue B0C4DE +LightSteelBlue1 CAE1FF +LightSteelBlue2 BCD2EE +LightSteelBlue3 A2B5CD +LightSteelBlue4 6E7B8B +Aquamarine 70DB93 +MediumBlue 0000CD +MediumSlateBlue 7B68EE +MediumTurquoise 48D1CC +MidnightBlue 191970 +NavyBlue 000080 +PaleTurquoise AFEEEE +PaleTurquoise1 BBFFFF +PaleTurquoise2 AEEEEE +PaleTurquoise3 96CDCD +PaleTurquoise4 668B8B +PowderBlue B0E0E6 +RoyalBlue 4169E1 +RoyalBlue1 4876FF +RoyalBlue2 436EEE +RoyalBlue3 3A5FCD +RoyalBlue4 27408B +RoyalBlue5 002266 +SkyBlue 87CEEB +SkyBlue1 87CEFF +SkyBlue2 7EC0EE +SkyBlue3 6CA6CD +SkyBlue4 4A708B +SlateBlue 6A5ACD +SlateBlue1 836FFF +SlateBlue2 7A67EE +SlateBlue3 6959CD +SlateBlue4 473C8B +SteelBlue 4682B4 +SteelBlue1 63B8FF +SteelBlue2 5CACEE +SteelBlue3 4F94CD +SteelBlue4 36648B +aquamarine 7FFFD4 +aquamarine1 7FFFD4 +aquamarine2 76EEC6 +aquamarine3, MediumAquamarine 66CDAA +aquamarine4 458B74 +azure F0FFFF +azure1 F0FFFF +azure2 E0EEEE +azure3 C1CDCD +azure4 838B8B +blue 0000FF +blue1 0000FF +blue2 0000EE +blue3 0000CD +blue4 00008B +aqua 00FFFF +True Iris Blue 03B4CC +cyan 00FFFF +cyan1 00FFFF +cyan2 00EEEE +cyan3 00CDCD +cyan4 008B8B +navy 000080 +teal 008080 +turquoise 40E0D0 +turquoise1 00F5FF +turquoise2 00E5EE +turquoise3 00C5CD +turquoise4 00868B +DarkSlateGray 2F4F4F +DarkSlateGray1 97FFFF +DarkSlateGray2 8DEEEE +DarkSlateGray3 79CDCD +DarkSlateGray4 528B8B +Dark Slate Blue 241882 +Dark Turquoise 7093DB +Medium Slate Blue 7F00FF +Medium Turquoise 70DBDB +Midnight Blue 2F2F4F +Navy Blue 23238E +Neon Blue 4D4DFF +New Midnight Blue 00009C +Rich Blue 5959AB +Sky Blue 3299CC +Slate Blue 007FFF +Summer Sky 38B0DE +Iris Blue 03B4C8 +Free Speech Blue 4156C5 +RosyBrown BC8F8F +RosyBrown1 FFC1C1 +RosyBrown2 EEB4B4 +RosyBrown3 CD9B9B +RosyBrown4 8B6969 +SaddleBrown 8B4513 +SandyBrown F4A460 +beige F5F5DC +brown A52A2A +brown A62A2A +brown1 FF4040 +brown2 EE3B3B +brown3 CD3333 +brown4 8B2323 +dark brown 5C4033 +burlywood DEB887 +burlywood1 FFD39B +burlywood2 EEC591 +burlywood3 CDAA7D +burlywood4 8B7355 +baker's chocolate 5C3317 +chocolate D2691E +chocolate1 FF7F24 +chocolate2 EE7621 +chocolate3 CD661D +chocolate4 8B4513 +peru CD853F +tan D2B48C +tan1 FFA54F +tan2 EE9A49 +tan3 CD853F +tan4 8B5A2B +Dark Tan 97694F +Dark Wood 855E42 +Light Wood 856363 +Medium Wood A68064 +New Tan EBC79E +Semi-Sweet Chocolate 6B4226 +Sienna 8E6B23 +Tan DB9370 +Very Dark Brown 5C4033 +Dark Green 2F4F2F +DarkGreen 006400 +dark green copper 4A766E +DarkKhaki BDB76B +DarkOliveGreen 556B2F +DarkOliveGreen1 CAFF70 +DarkOliveGreen2 BCEE68 +DarkOliveGreen3 A2CD5A +DarkOliveGreen4 6E8B3D +olive 808000 +DarkSeaGreen 8FBC8F +DarkSeaGreen1 C1FFC1 +DarkSeaGreen2 B4EEB4 +DarkSeaGreen3 9BCD9B +DarkSeaGreen4 698B69 +ForestGreen 228B22 +GreenYellow ADFF2F +LawnGreen 7CFC00 +LightSeaGreen 20B2AA +LimeGreen 32CD32 +MediumSeaGreen 3CB371 +MediumSpringGreen 00FA9A +MintCream F5FFFA +OliveDrab 6B8E23 +OliveDrab1 C0FF3E +OliveDrab2 B3EE3A +OliveDrab3 9ACD32 +OliveDrab4 698B22 +PaleGreen 98FB98 +PaleGreen1 9AFF9A +PaleGreen2 90EE90 +PaleGreen3 7CCD7C +PaleGreen4 548B54 +SeaGreen, SeaGreen4 2E8B57 +SeaGreen1 54FF9F +SeaGreen2 4EEE94 +SeaGreen3 43CD80 +SpringGreen 00FF7F +SpringGreen1 00FF7F +SpringGreen2 00EE76 +SpringGreen3 00CD66 +SpringGreen4 008B45 +YellowGreen 9ACD32 +chartreuse 7FFF00 +chartreuse1 7FFF00 +chartreuse2 76EE00 +chartreuse3 66CD00 +chartreuse4 458B00 +green 00FF00 +green 008000 +lime 00FF00 +green1 00FF00 +green2 00EE00 +green3 00CD00 +green4 008B00 +khaki F0E68C +khaki1 FFF68F +khaki2 EEE685 +khaki3 CDC673 +khaki4 8B864E +Dark Olive Green 4F4F2F +Green Yellow [sic] D19275 +Hunter Green [sic] 8E2323 +Forest Green, Khaki, Medium Aquamarine 238E23 +Medium Forest Green DBDB70 +Medium Sea Green 426F42 +Medium Spring Green 7FFF00 +Pale Green 8FBC8F +Sea Green 238E68 +Spring Green 00FF7F +Free Speech Green 09F911 +Free Speech Aquamarine 029D74 +DarkOrange FF8C00 +DarkOrange1 FF7F00 +DarkOrange2 EE7600 +DarkOrange3 CD6600 +DarkOrange4 8B4500 +DarkSalmon E9967A +LightCoral F08080 +LightSalmon FFA07A +LightSalmon1 FFA07A +LightSalmon2 EE9572 +LightSalmon3 CD8162 +LightSalmon4 8B5742 +PeachPuff FFDAB9 +PeachPuff1 FFDAB9 +PeachPuff2 EECBAD +PeachPuff3 CDAF95 +PeachPuff4 8B7765 +bisque FFE4C4 +bisque1 FFE4C4 +bisque2 EED5B7 +bisque3 CDB79E +bisque4 8B7D6B +coral FF7F00 +coral FF7F50 +coral1 FF7256 +coral2 EE6A50 +coral3 CD5B45 +coral4 8B3E2F +honeydew F0FFF0 +honeydew1 F0FFF0 +honeydew2 E0EEE0 +honeydew3 C1CDC1 +honeydew4 838B83 +orange FFA500 +orange1 FFA500 +orange2 EE9A00 +orange3 CD8500 +orange4 8B5A00 +salmon FA8072 +salmon1 FF8C69 +salmon2 EE8262 +salmon3 CD7054 +salmon4 8B4C39 +sienna A0522D +sienna1 FF8247 +sienna2 EE7942 +sienna3 CD6839 +sienna4 8B4726 +Mandarian Orange 8E2323 +Orange FF7F00 +Orange Red FF2400 +DeepPink FF1493 +DeepPink1 FF1493 +DeepPink2 EE1289 +DeepPink3 CD1076 +DeepPink4 8B0A50 +HotPink FF69B4 +HotPink1 FF6EB4 +HotPink2 EE6AA7 +HotPink3 CD6090 +HotPink4 8B3A62 +IndianRed CD5C5C +IndianRed1 FF6A6A +IndianRed2 EE6363 +IndianRed3 CD5555 +IndianRed4 8B3A3A +LightPink FFB6C1 +LightPink1 FFAEB9 +LightPink2 EEA2AD +LightPink3 CD8C95 +LightPink4 8B5F65 +MediumVioletRed C71585 +MistyRose FFE4E1 +MistyRose1 FFE4E1 +MistyRose2 EED5D2 +MistyRose3 CDB7B5 +MistyRose4 8B7D7B +OrangeRed FF4500 +OrangeRed1 FF4500 +OrangeRed2 EE4000 +OrangeRed3 CD3700 +OrangeRed4 8B2500 +PaleVioletRed DB7093 +PaleVioletRed1 FF82AB +PaleVioletRed2 EE799F +PaleVioletRed3 CD6889 +PaleVioletRed4 8B475D +VioletRed D02090 +VioletRed1 FF3E96 +VioletRed2 EE3A8C +VioletRed3 CD3278 +VioletRed4 8B2252 +firebrick B22222 +firebrick1 FF3030 +firebrick2 EE2C2C +firebrick3 CD2626 +firebrick4 8B1A1A +pink FFC0CB +pink1 FFB5C5 +pink2 EEA9B8 +pink3 CD919E +pink4 8B636C +Flesh F5CCB0 +Feldspar D19275 +red FF0000 +red1 FF0000 +red2 EE0000 +red3 CD0000 +red4 8B0000 +tomato FF6347 +tomato1 FF6347 +tomato2 EE5C42 +tomato3 CD4F39 +tomato4 8B3626 +Dusty Rose 856363 +Firebrick 8E2323 +Indian Red F5CCB0 +Pink BC8F8F +Salmon 6F4242 +Scarlet 8C1717 +Spicy Pink FF1CAE +Free Speech Magenta E35BD8 +Free Speech Red C00000 +DarkOrchid 9932CC +DarkOrchid1 BF3EFF +DarkOrchid2 B23AEE +DarkOrchid3 9A32CD +DarkOrchid4 68228B +DarkViolet 9400D3 +LavenderBlush FFF0F5 +LavenderBlush1 FFF0F5 +LavenderBlush2 EEE0E5 +LavenderBlush3 CDC1C5 +LavenderBlush4 8B8386 +MediumOrchid BA55D3 +MediumOrchid1 E066FF +MediumOrchid2 D15FEE +MediumOrchid3 B452CD +MediumOrchid4 7A378B +MediumPurple 9370DB +Medium Orchid 9370DB +MediumPurple1 AB82FF +Dark Orchid 9932CD +MediumPurple2 9F79EE +MediumPurple3 8968CD +MediumPurple4 5D478B +lavender E6E6FA +magenta FF00FF +fuchsia FF00FF +magenta1 FF00FF +magenta2 EE00EE +magenta3 CD00CD +magenta4 8B008B +maroon B03060 +maroon1 FF34B3 +maroon2 EE30A7 +maroon3 CD2990 +maroon4 8B1C62 +orchid DA70D6 +Orchid DB70DB +orchid1 FF83FA +orchid2 EE7AE9 +orchid3 CD69C9 +orchid4 8B4789 +plum DDA0DD +plum1 FFBBFF +plum2 EEAEEE +plum3 CD96CD +plum4 8B668B +purple A020F0 +purple 800080 +purple1 9B30FF +purple2 912CEE +purple3 7D26CD +purple4 551A8B +thistle D8BFD8 +thistle1 FFE1FF +thistle2 EED2EE +thistle3 CDB5CD +thistle4 8B7B8B +violet EE82EE +violet blue 9F5F9F +Dark Purple 871F78 +Maroon 800000 +Medium Violet Red DB7093 +Neon Pink FF6EC7 +Plum EAADEA +Thistle D8BFD8 +Turquoise ADEAEA +Violet 4F2F4F +Violet Red CC3299 +AntiqueWhite FAEBD7 +AntiqueWhite1 FFEFDB +AntiqueWhite2 EEDFCC +AntiqueWhite3 CDC0B0 +AntiqueWhite4 8B8378 +FloralWhite FFFAF0 +GhostWhite F8F8FF +NavajoWhite FFDEAD +NavajoWhite1 FFDEAD +NavajoWhite2 EECFA1 +NavajoWhite3 CDB38B +NavajoWhite4 8B795E +OldLace FDF5E6 +WhiteSmoke F5F5F5 +gainsboro DCDCDC +ivory FFFFF0 +ivory1 FFFFF0 +ivory2 EEEEE0 +ivory3 CDCDC1 +ivory4 8B8B83 +linen FAF0E6 +seashell FFF5EE +seashell1 FFF5EE +seashell2 EEE5DE +seashell3 CDC5BF +seashell4 8B8682 +snow FFFAFA +snow1 FFFAFA +snow2 EEE9E9 +snow3 CDC9C9 +snow4 8B8989 +wheat F5DEB3 +wheat1 FFE7BA +wheat2 EED8AE +wheat3 CDBA96 +wheat4 8B7E66 +white FFFFFF +Quartz D9D9F3 +Wheat D8D8BF +BlanchedAlmond FFEBCD +DarkGoldenrod B8860B +DarkGoldenrod1 FFB90F +DarkGoldenrod2 EEAD0E +DarkGoldenrod3 CD950C +DarkGoldenrod4 8B6508 +LemonChiffon FFFACD +LemonChiffon1 FFFACD +LemonChiffon2 EEE9BF +LemonChiffon3 CDC9A5 +LemonChiffon4 8B8970 +LightGoldenrod EEDD82 +LightGoldenrod1 FFEC8B +LightGoldenrod2 EEDC82 +LightGoldenrod3 CDBE70 +LightGoldenrod4 8B814C +LightGoldenrodYellow FAFAD2 +LightYellow FFFFE0 +LightYellow1 FFFFE0 +LightYellow2 EEEED1 +LightYellow3 CDCDB4 +LightYellow4 8B8B7A +PaleGoldenrod EEE8AA +PapayaWhip FFEFD5 +cornsilk FFF8DC +cornsilk1 FFF8DC +cornsilk2 EEE8CD +cornsilk3 CDC8B1 +cornsilk4 8B8878 +goldenrod DAA520 +goldenrod1 FFC125 +goldenrod2 EEB422 +goldenrod3 CD9B1D +goldenrod4 8B6914 +moccasin FFE4B5 +yellow FFFF00 +yellow1 FFFF00 +yellow2 EEEE00 +yellow3 CDCD00 +yellow4 8B8B00 +gold FFD700 +gold1 FFD700 +gold2 EEC900 +gold3 CDAD00 +gold4 8B7500 +Goldenrod DBDB70 +Medium Goldenrod EAEAAE +Yellow Green 99CC32 +copper B87333 +cool copper D98719 +Green Copper 856363 +brass B5A642 +bronze 8C7853 +bronze II A67D3D +bright gold D9D919 +Old Gold CFB53B +CSS Gold CC9900 +gold CD7F32 +silver E6E8FA +Silver, Grey C0C0C0 +Light Steel Blue 545454 +Steel Blue 236B8E diff --git a/tools/colors_find.py b/tools/colors_find.py new file mode 100644 index 00000000..d93bf455 --- /dev/null +++ b/tools/colors_find.py @@ -0,0 +1,38 @@ +import sys +import os + + +def get_color(name, rgb): + return name, (int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16)) + + +with open(os.path.join(os.path.dirname(__file__), 'colors.map'), 'r') as f: + colors = [get_color(*line.split('\t')) for line in f] + + +urgb = get_color(None, sys.argv[1])[1] + + +def col_distance(rgb1, rgb2): + return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3))) + + +def find_color(urgb, colors): + cur_distance = 3 * (255 ** 2 + 1) + cur_color = None + for color, crgb in colors: + dist = col_distance(urgb, crgb) + if dist < cur_distance: + cur_distance = dist + cur_color = (color, crgb) + return cur_color + + +cur_color = find_color(urgb, colors) + +print urgb, ':', cur_color + +col_1 = ';2;' + ';'.join((str(i) for i in urgb)) + 'm' +col_2 = ';2;' + ';'.join((str(i) for i in cur_color[1])) + 'm' +sys.stdout.write('\033[48' + col_1 + '\033[38' + col_2 + 'abc\033[0m <-- bg:urgb, fg:crgb\n') +sys.stdout.write('\033[48' + col_2 + '\033[38' + col_1 + 'abc\033[0m <-- bg:crgb, fg:urgb\n') diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py new file mode 100644 index 00000000..46f418ca --- /dev/null +++ b/tools/generate_gradients.py @@ -0,0 +1,105 @@ +import sys +import json +from powerline.colorscheme import cterm_to_hex +from itertools import groupby + + +if len(sys.argv) == 1: + sys.stderr.write(''' + Usage: generate_gradients.py colors itemnum[ "show"] + + colors: JSON list with either cterm ([200, 42, 6]) or RGB (["abcdef", + "feffef"]) colors. + + itemnum: number of items in generated gradient. + + "show": static string, determines whether gradient sample should be + printed to stdout as well. + ''') + + +def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset): + return start_value + ((offset - start_offset) * (stop_value - start_value) / (stop_offset - start_offset)) + + +def gradient(DATA): + def gradient_function(y): + initial_offset = 0 + for offset, start, end in DATA: + if y <= offset: + return [linear_gradient(start[i], end[i], initial_offset, offset, y) for i in range(3)] + initial_offset = offset + return gradient_function + + +def get_color(rgb): + if type(rgb) is unicode: + return int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16) + else: + return rgbint_to_rgb(cterm_to_hex[rgb]) + + +def get_rgb(*args): + return "%02x%02x%02x" % args + + +def col_distance(rgb1, rgb2): + return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3))) + + +def rgbint_to_rgb(rgbint): + return ((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF) + + +def find_color(urgb, colors): + cur_distance = 3 * (255 ** 2 + 1) + cur_color = None + i = 0 + for crgbint in colors: + crgb = rgbint_to_rgb(crgbint) + dist = col_distance(urgb, crgb) + if dist < cur_distance: + cur_distance = dist + cur_color = (i, crgb) + i += 1 + return cur_color + + +def print_color(color): + if type(color) is int: + colstr = '5;' + str(color) + else: + colstr = '2;' + ';'.join((str(i) for i in color)) + sys.stdout.write('\033[48;' + colstr + 'm ') + + +def print_colors(colors): + for i in range(101): + color = colors[int(round(i * (len(colors) - 1) / 100))] + print_color(color) + sys.stdout.write('\033[0m\n') + + +c = [get_color(color) for color in json.loads(sys.argv[1])] +m = int(sys.argv[2]) if len(sys.argv) > 2 else 100 +m += m % (len(c) - 1) +step = m / (len(c) - 1) +data = [(i * step, c[i - 1], c[i]) for i in range(1, len(c))] +gr_func = gradient(data) +gradient = [gr_func(y) for y in range(0, m - 1)] +r = [get_rgb(*color) for color in gradient] +r2 = [find_color(color, cterm_to_hex)[0] for color in gradient] +r3 = [i[0] for i in groupby(r2)] +print json.dumps(r) +print json.dumps(r2) +print json.dumps(r3) +if len(sys.argv) > 3 and sys.argv[3] == 'show': + print_colors(gradient) + print_colors(r2) + print_colors(r3) + sys.stdout.write('0') + sys.stdout.write(''.join(('%10u' % (i * 10) for i in range(1, 11)))) + sys.stdout.write('\n') + nums = (''.join((str(i) for i in range(10)))) + sys.stdout.write(''.join(((('\033[1m' if j % 2 else '\033[0m') + nums) for j in range(10)))) + sys.stdout.write('\033[0m0\n') From 6f679e08aa60923be05e5c85b36e16f98d4cdfa1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 21:15:39 +0400 Subject: [PATCH 0478/1472] Refactor powerline.lib.url --- powerline/lib/url.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 0ff53ade..7387acc9 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -1,26 +1,16 @@ # vim:fileencoding=utf-8:noet +try: + from urllib.error import HTTPError + from urllib.request import urlopen + from urllib.parse import urlencode as urllib_urlencode # NOQA +except ImportError: + from urllib2 import urlopen, HTTPError + from urllib import urlencode as urllib_urlencode # NOQA + def urllib_read(url): try: - import urllib.error - import urllib.request - try: - return urllib.request.urlopen(url, timeout=5).read().decode('utf-8') - except: - return - except ImportError: - import urllib2 - try: - return urllib2.urlopen(url, timeout=5).read() - except: - return - - -def urllib_urlencode(string): - try: - import urllib.parse - return urllib.parse.urlencode(string) - except ImportError: - import urllib - return urllib.urlencode(string) + return urlopen(url, timeout=100).read().decode('utf-8') + except HTTPError: + return From c237e6695807c9e5d5aaed3e6bc830ceb9900708 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 19:49:53 +0400 Subject: [PATCH 0479/1472] Make file_size segment output buffer size --- powerline/lib/vcs/__init__.py | 2 +- powerline/segments/vim.py | 18 +++++++----------- tests/test_segments.py | 2 +- tests/vim.py | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 2bbc9bdf..aa53abf8 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -16,7 +16,7 @@ def generate_directories(path): while True: old_path = path path = os.path.dirname(path) - if path == old_path: + if path == old_path or not path: break yield path diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 06da9e5d..09b4c636 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -20,6 +20,7 @@ vim_funcs = { 'fnamemodify': vim_get_func('fnamemodify'), 'expand': vim_get_func('expand'), 'bufnr': vim_get_func('bufnr', rettype=int), + 'line2byte': vim_get_func('line2byte', rettype=int), } vim_modes = { @@ -195,10 +196,9 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): return file_name -@requires_segment_info -@memoize(2, cache_key=bufname, cache_reg_func=purgebuf_on_shell_and_write) -def file_size(segment_info, suffix='B', si_prefix=False): - '''Return file size. +@window_cached +def file_size(suffix='B', si_prefix=False): + '''Return file size in &encoding. :param str suffix: string appended to the file size @@ -206,13 +206,9 @@ def file_size(segment_info, suffix='B', si_prefix=False): use SI prefix, e.g. MB instead of MiB :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' - file_name = segment_info['buffer'].name - if not file_name: - return None - try: - file_size = os.stat(file_name).st_size - except: - return None + # Note: returns file size in &encoding, not in &fileencoding. But returned + # size is updated immediately; and it is valid for any buffer + file_size = vim_funcs['line2byte'](len(vim.current.buffer) + 1) - 1 return humanize_bytes(file_size, suffix, si_prefix) diff --git a/tests/test_segments.py b/tests/test_segments.py index 491e4a3a..ec6be894 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -229,7 +229,7 @@ class TestVim(TestCase): def test_file_size(self): segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_size(segment_info=segment_info), None) + self.assertEqual(vim.file_size(segment_info=segment_info), '0 B') with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info: self.assertEqual(vim.file_size(segment_info=segment_info), '0 B') diff --git a/tests/vim.py b/tests/vim.py index 1b9b820c..08e57276 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -149,6 +149,12 @@ def _emul_exists(varname): return varname[2:] in _g raise NotImplementedError +@_logged +def _emul_line2byte(line): + buflines = _buf_lines[_buffer()] + if line == len(buflines) + 1: + return sum((len(s) for s in buflines)) + 1 + raise NotImplementedError _window_ids = [None] _window_id = 0 @@ -242,6 +248,15 @@ class _Buffer(object): _buf_scopes.pop(bufnr) +class _Current(object): + @property + def buffer(self): + return buffers[_buffer()] + + +current = _Current() + + _dict = None From abe0b1a6477fa397997d7ae31d7cb881fc264d41 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 19:51:33 +0400 Subject: [PATCH 0480/1472] Add support for .startup and .shutdown methods --- powerline/__init__.py | 3 ++- powerline/bindings/vim/plugin/powerline.vim | 1 + powerline/renderers/vim.py | 6 ++++++ powerline/segment.py | 2 ++ powerline/shell.py | 2 +- powerline/theme.py | 14 +++++++++++++- 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index dc1f30ea..e684c44c 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -36,7 +36,7 @@ class Powerline(object): the package imported like this: ``powerline.renders.{render_module}``. ''' - def __init__(self, ext, renderer_module=None): + def __init__(self, ext, renderer_module=None, run_once=False): self.config_paths = self.get_config_paths() # Load main config file @@ -58,6 +58,7 @@ class Powerline(object): 'ext': ext, 'common_config': common_config, 'segment_info': self.get_segment_info(), + 'run_once': run_once, } local_themes = self.get_local_themes(ext_config.get('local_themes')) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index c692e294..4c014f4c 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -92,4 +92,5 @@ augroup Powerline autocmd! autocmd ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' autocmd VimEnter * :redrawstatus! + autocmd VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()' augroup END diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index bec4548a..08ebc0bc 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -31,6 +31,12 @@ class VimRenderer(Renderer): super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() + def add_local_theme(self, matcher, theme): if matcher in self.local_themes: raise KeyError('There is already a local theme with given matcher') diff --git a/powerline/segment.py b/powerline/segment.py index 998d9939..8f37702c 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -79,6 +79,8 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): 'include_modes': segment.get('include_modes', []), 'width': segment.get('width'), 'align': segment.get('align', 'l'), + 'shutdown': getattr(contents_func, 'shutdown', None), + 'startup': getattr(contents_func, 'startup', None), '_rendered_raw': '', '_rendered_hl': '', '_len': 0, diff --git a/powerline/shell.py b/powerline/shell.py index f84157ae..e811066f 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -17,7 +17,7 @@ class ShellPowerline(Powerline): def __init__(self, args): self.args = args self.theme_option = mergeargs(args.theme_option) or {} - super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module) + super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=True) def get_segment_info(self): return self.args diff --git a/powerline/theme.py b/powerline/theme.py index 5c2b0c4a..725248e5 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -24,7 +24,7 @@ def requires_segment_info(func): class Theme(object): - def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None): + def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None, run_once=False): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = { @@ -42,6 +42,18 @@ class Theme(object): get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module')) for side in ['left', 'right']: self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) + if not run_once: + for segment in self.segments[side]: + if segment['startup']: + segment['startup'](**segment['args']) + + def shutdown(self): + for segments in self.segments.values(): + for segment in segments: + try: + segment['shutdown']() + except TypeError: + pass def get_divider(self, side='left', type='soft'): '''Return segment divider.''' From f8db46b40629cfdb145a4a000d47277f72090c5b Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 17:08:22 +0400 Subject: [PATCH 0481/1472] Use proper clock if possible --- powerline/lib/memoize.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 194cf69e..fe783707 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,7 +1,12 @@ # vim:fileencoding=utf-8:noet from functools import wraps -import time +try: + # Python>=3.3, the only valid clock source for this job + from time import monotonic as time +except ImportError: + # System time, is affected by clock updates. + from time import time def default_cache_key(**kwargs): @@ -28,10 +33,13 @@ class memoize(object): cached = self.cache.get(key, None) except TypeError: return func(**kwargs) - if cached is None or time.time() - cached['time'] > self.timeout: + # Handle case when time() appears to be less then cached['time'] due + # to clock updates. Not applicable for monotonic clock, but this + # case is currently rare. + if cached is None or not (cached['time'] < time() < cached['time'] + self.timeout): cached = self.cache[key] = { 'result': func(**kwargs), - 'time': time.time(), + 'time': time(), } return cached['result'] return decorated_function From 93e41bf2c4f9483336b5eee2e241d2705010f862 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Mar 2013 22:37:26 +0400 Subject: [PATCH 0482/1472] Make psutil.BOOT_TIME fallback for /proc/uptime Method used with psutil.BOOT_TIME is subject to changes when clock_settime changes time, /proc/uptime is not. --- powerline/segments/common.py | 67 +++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 63142a8b..8a33dc13 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -202,33 +202,6 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): return [{'contents': _external_ip(query_url=query_url), 'divider_highlight_group': 'background:divider'}] -@add_divider_highlight_group('background:divider') -def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): - '''Return system uptime. - - Uses the ``psutil`` module if available for multi-platform compatibility, - falls back to reading :file:`/proc/uptime`. - - :param str format: - format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments - - Divider highlight group used: ``background:divider``. - ''' - try: - import psutil - seconds = int((datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).total_seconds()) - except ImportError: - try: - with open('/proc/uptime', 'r') as f: - seconds = int(float(f.readline().split()[0])) - except IOError: - return None - minutes, seconds = divmod(seconds, 60) - hours, minutes = divmod(minutes, 60) - days, hours = divmod(hours, 24) - return format.format(days=int(days), hours=hours, minutes=minutes) - - # Weather condition code descriptions available at # http://developer.yahoo.com/weather/#codes weather_conditions_codes = ( @@ -397,7 +370,11 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): Highlight groups used: ``system_load_good`` or ``system_load``, ``system_load_bad`` or ``system_load``, ``system_load_ugly`` or ``system_load``. It is recommended to define all highlight groups. ''' - cpu_num = cpu_count() + global cpu_count + try: + cpu_num = cpu_count() + except NotImplementedError: + return None ret = [] for avg in os.getloadavg(): normalized = avg / cpu_num @@ -435,6 +412,40 @@ def cpu_load_percent(measure_interval=.5): return '{0}%'.format(cpu_percent) +if os.path.exists('/proc/uptime'): + def _get_uptime(): + with open('/proc/uptime', 'r') as f: + return int(float(f.readline().split()[0])) +elif 'psutil' in globals(): + from time import time + def _get_uptime(): + # psutil.BOOT_TIME is not subject to clock adjustments, but time() is. + # Thus it is a fallback to /proc/uptime reading and not the reverse. + return int(time() - psutil.BOOT_TIME) +else: + def _get_uptime(): + raise NotImplementedError + + +@add_divider_highlight_group('background:divider') +def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): + '''Return system uptime. + + :param str format: + format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments + + Divider highlight group used: ``background:divider``. + ''' + try: + seconds = _get_uptime() + except IOError, NotImplementedError: + return None + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + return format.format(days=int(days), hours=hours, minutes=minutes) + + @add_divider_highlight_group('background:divider') def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=False): '''Return the network load. From 29eccf409bd535cc83e89f3c8ea7d5e2eb3ea767 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 13:00:01 +0400 Subject: [PATCH 0483/1472] Use psutil module for `user` segment --- powerline/segments/common.py | 167 +++++++++++++++++++++++------------ tests/install.sh | 1 + tests/test.sh | 2 +- tests/test_segments.py | 5 +- 4 files changed, 115 insertions(+), 60 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 8a33dc13..085f17c8 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -22,25 +22,6 @@ def hostname(only_if_ssh=False): return socket.gethostname() -def user(): - '''Return the current user. - - Highlights the user with the ``superuser`` if the effective user ID is 0. - - Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups. - ''' - user = os.environ.get('USER') - try: - euid = os.geteuid() - except AttributeError: - # os.geteuid is not available on windows - euid = 1 - return [{ - 'contents': user, - 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], - }] - - def branch(status_colors=True): '''Return the current VCS branch.@ @@ -396,20 +377,78 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): return ret -def cpu_load_percent(measure_interval=.5): - '''Return the average CPU load as a percentage. +try: + import psutil - Requires the ``psutil`` module. + def _get_bytes(interface): + io_counters = psutil.network_io_counters(pernic=True) + if_io = io_counters.get(interface) + if not if_io: + return None + return if_io.bytes_recv, if_io.bytes_sent - :param float measure_interval: - interval used to measure CPU load (in seconds) - ''' - try: - import psutil - except ImportError: + def _get_user(): + return psutil.Process(os.getpid()).username + + def cpu_load_percent(measure_interval=.5): + '''Return the average CPU load as a percentage. + + Requires the ``psutil`` module. + + :param float measure_interval: + interval used to measure CPU load (in seconds) + ''' + cpu_percent = int(psutil.cpu_percent(interval=measure_interval)) + return '{0}%'.format(cpu_percent) +except ImportError: + def _get_bytes(interface): # NOQA + try: + with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: + rx = int(file_obj.read()) + with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: + tx = int(file_obj.read()) + return (rx, tx) + except IOError: + return None + + def _get_user(): + return os.environ.get('USER', None) + + def cpu_load_percent(**kwargs): # NOQA + '''Return the average CPU load as a percentage. + + Requires the ``psutil`` module. + + :param float measure_interval: + interval used to measure CPU load (in seconds) + ''' return None - cpu_percent = int(psutil.cpu_percent(interval=measure_interval)) - return '{0}%'.format(cpu_percent) + + +username = False + + +def user(): + '''Return the current user. + + Highlights the user with the ``superuser`` if the effective user ID is 0. + + Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups. + ''' + global username + if username is False: + username = _get_user() + if username is None: + return None + try: + euid = os.geteuid() + except AttributeError: + # os.geteuid is not available on windows + euid = 1 + return [{ + 'contents': username, + 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], + }] if os.path.exists('/proc/uptime'): @@ -438,7 +477,7 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): ''' try: seconds = _get_uptime() - except IOError, NotImplementedError: + except (IOError, NotImplementedError): return None minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) @@ -466,33 +505,47 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F import time from powerline.lib import humanize_bytes - def get_bytes(): - try: - import psutil - io_counters = psutil.network_io_counters(pernic=True) - if_io = io_counters.get(interface) - if not if_io: - return None - return (if_io.bytes_recv, if_io.bytes_sent) - except ImportError: - try: - with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: - rx = int(file_obj.read()) - with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: - tx = int(file_obj.read()) - return (rx, tx) - except IOError: - return None + interfaces = {} - b1 = get_bytes() - if b1 is None: - return None - time.sleep(measure_interval) - b2 = get_bytes() - return '⬇ {rx_diff} ⬆ {tx_diff}'.format( - rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8), - tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8), - ) + @staticmethod + def key(interface='eth0', **kwargs): + return interface + + def compute_state(self, interface): + if interface in self.interfaces: + idata = self.interfaces[interface] + idata['prev'] = idata['last'] + else: + idata = {} + if self.run_once: + idata['prev'] = (monotonic(), _get_bytes(interface)) + self.sleep(0) + self.interfaces[interface] = idata + + idata['last'] = (monotonic(), _get_bytes(interface)) + return idata + + def render_one(self, idata, suffix='B/s', si_prefix=False, **kwargs): + if 'prev' not in idata: + return None + + t1, b1 = idata['prev'] + t2, b2 = idata['last'] + measure_interval = t2 - t1 + + if None in (b1, b2): + return None + + return [{ + 'contents': '⬇ {rx_diff} ⬆ {tx_diff}'.format( + rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8), + tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8), + ), + 'divider_highlight_group': 'background:divider', + }] + + +network_load = NetworkLoadSegment() def virtualenv(): diff --git a/tests/install.sh b/tests/install.sh index 30ecb922..eb81a522 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -1,5 +1,6 @@ #!/bin/sh pip install . +pip install psutil if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 pip install mercurial bzr diff --git a/tests/test.sh b/tests/test.sh index f4f69442..1e4c62ec 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -6,7 +6,7 @@ if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then export PYTHONPATH="${PYTHONPATH}:`realpath .`" for file in tests/test_*.py ; do if ! ${PYTHON} $file ; then - exit 1 + FAILED=1 fi done else diff --git a/tests/test_segments.py b/tests/test_segments.py index ec6be894..4db6c231 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -37,8 +37,9 @@ class TestCommon(TestCase): self.assertEqual(common.hostname(only_if_ssh=True), None) def test_user(self): - new_os = new_module('os', environ={'USER': 'def'}) - with replace_module_attr(common, 'os', new_os): + new_os = new_module('os', environ={'USER': 'def'}, getpid=lambda: 1) + new_psutil = new_module('psutil', Process=lambda pid: Args(username='def')) + with replace_module_attr(common, 'os', new_os), replace_module_attr(common, 'psutil', new_psutil): self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) new_os.geteuid = lambda: 1 self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) From a4adc9221570baccf2aa75dff358cfcbff566b50 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 20:51:28 +0400 Subject: [PATCH 0484/1472] Do not import memoize, humanize_bytes and urllib_* in powerline.lib --- powerline/lib/__init__.py | 4 ---- powerline/segments/common.py | 4 +++- powerline/segments/vim.py | 4 +++- tests/test_lib.py | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index fc97407d..a2e4be4a 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -2,10 +2,6 @@ from functools import wraps import json -from powerline.lib.memoize import memoize # NOQA -from powerline.lib.humanize_bytes import humanize_bytes # NOQA -from powerline.lib.url import urllib_read, urllib_urlencode # NOQA - def mergedicts(d1, d2): '''Recursively merge two dictionaries. First dictionary is modified in-place. diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 085f17c8..80909360 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -7,7 +7,9 @@ from datetime import datetime import socket from multiprocessing import cpu_count -from powerline.lib import memoize, urllib_read, urllib_urlencode, add_divider_highlight_group +from powerline.lib import add_divider_highlight_group +from powerline.lib.memoize import memoize +from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 09b4c636..dc8c2678 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -10,8 +10,10 @@ except ImportError: from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info -from powerline.lib import memoize, humanize_bytes, add_divider_highlight_group +from powerline.lib import add_divider_highlight_group +from powerline.lib.memoize import memoize from powerline.lib.vcs import guess +from powerline.lib.humanize_bytes import humanize_bytes from functools import wraps from collections import defaultdict diff --git a/tests/test_lib.py b/tests/test_lib.py index 94b43b94..6355e25f 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,5 +1,6 @@ # vim:fileencoding=utf-8:noet -from powerline.lib import mergedicts, add_divider_highlight_group, humanize_bytes +from powerline.lib import mergedicts, add_divider_highlight_group +from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess from subprocess import call, PIPE import os From 62e731314e404c3d107138a5a086159dfdf214f3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 22:38:56 +0400 Subject: [PATCH 0485/1472] Replace memoize with threading where applicable NOTE: Documentation now gets attached to *classes*, not actual segments. Hiding away classes (by changing their names to start with `_`) and/or doing self.__doc__ = self.__class__.__doc__ does not work (hiding classes only hides documentation completely). I am not familiar with sphinx enough to say how this should be fixed. Ref #168 --- powerline/lib/memoize.py | 11 +- powerline/lib/threaded.py | 125 ++++++++++++++++++ powerline/lib/time.py | 103 +++++++++++++++ powerline/lib/url.py | 2 +- powerline/lib/vcs/__init__.py | 2 - powerline/segments/common.py | 242 ++++++++++++++++++++++------------ powerline/segments/vim.py | 152 ++++++++++++++++----- tests/test_segments.py | 15 ++- 8 files changed, 517 insertions(+), 135 deletions(-) create mode 100644 powerline/lib/threaded.py create mode 100644 powerline/lib/time.py diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index fe783707..623f1d32 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,12 +1,7 @@ # vim:fileencoding=utf-8:noet from functools import wraps -try: - # Python>=3.3, the only valid clock source for this job - from time import monotonic as time -except ImportError: - # System time, is affected by clock updates. - from time import time +from powerline.lib.time import monotonic def default_cache_key(**kwargs): @@ -36,10 +31,10 @@ class memoize(object): # Handle case when time() appears to be less then cached['time'] due # to clock updates. Not applicable for monotonic clock, but this # case is currently rare. - if cached is None or not (cached['time'] < time() < cached['time'] + self.timeout): + if cached is None or not (cached['time'] < monotonic() < cached['time'] + self.timeout): cached = self.cache[key] = { 'result': func(**kwargs), - 'time': time(), + 'time': monotonic(), } return cached['result'] return decorated_function diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py new file mode 100644 index 00000000..5ce4b055 --- /dev/null +++ b/powerline/lib/threaded.py @@ -0,0 +1,125 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import + +from powerline.lib.time import monotonic + +from time import sleep +from threading import Thread, Lock + + +class ThreadedSegment(Thread): + daemon = True + min_sleep_time = 0.1 + + def __init__(self): + super(ThreadedSegment, self).__init__() + self.update_lock = Lock() + self.write_lock = Lock() + self.keep_going = True + self.run_once = True + self.did_set_interval = False + + def __call__(self, **kwargs): + if self.run_once: + self.set_state(**kwargs) + self.update() + elif not self.is_alive(): + self.startup(**kwargs) + + with self.write_lock: + return self.render(**kwargs) + + def sleep(self, adjust_time): + sleep(max(self.interval - adjust_time, self.min_sleep_time)) + + def run(self): + while self.keep_going: + start_time = monotonic() + + with self.update_lock: + self.update() + + self.sleep(monotonic() - start_time) + + def shutdown(self): + self.keep_going = False + + def set_interval(self, interval=None, **kwargs): + # Allowing “interval” keyword in configuration. + # Note: Here **kwargs is needed to support foreign data, in subclasses + # it can be seen in a number of places in order to support + # .set_interval(). + interval = interval or getattr(self, 'interval', 1) + self.interval = interval + self.has_set_interval = True + + def set_state(self, **kwargs): + if not self.did_set_interval: + self.set_interval(**kwargs) + + def startup(self, **kwargs): + # Normally .update() succeeds to run before value is requested, meaning + # that user is getting values he needs directly at vim startup. Without + # .startup() we will not have to wait long until receiving bug “I opened + # vim, but branch information is only shown after I move cursor”. + self.run_once = False + + self.set_state(**kwargs) + + if not self.is_alive(): + self.start() + + +def printed(func): + def f(*args, **kwargs): + print(func.__name__) + return func(*args, **kwargs) + return f + + +class KwThreadedSegment(ThreadedSegment): + drop_interval = 10 * 60 + + def __init__(self): + super(KwThreadedSegment, self).__init__() + self.queries = {} + self.update_missing = True + + @staticmethod + def key(**kwargs): + return frozenset(kwargs.items()) + + def render(self, **kwargs): + key = self.key(**kwargs) + try: + update_state = self.queries[key][1] + except KeyError: + update_state = self.compute_state(key) if self.update_missing else None + # No locks: render method is already running with write_lock acquired. + self.queries[key] = (monotonic(), update_state) + return self.render_one(update_state, **kwargs) + + def update(self): + updates = {} + removes = [] + for key, (last_query_time, state) in list(self.queries.items()): + if last_query_time < monotonic() < last_query_time + self.drop_interval: + updates[key] = (last_query_time, self.compute_state(key)) + else: + removes.append(key) + with self.write_lock: + self.queries.update(updates) + for key in removes: + self.queries.pop(key) + + def set_state(self, **kwargs): + if not self.did_set_interval or ('interval' in kwargs and self.interval > kwargs['interval']): + self.set_interval(**kwargs) + + key = self.key(**kwargs) + self.queries[key] = (monotonic(), None) + + @staticmethod + def render_one(update_state, **kwargs): + return update_state diff --git a/powerline/lib/time.py b/powerline/lib/time.py new file mode 100644 index 00000000..93f147f1 --- /dev/null +++ b/powerline/lib/time.py @@ -0,0 +1,103 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import division, absolute_import + +try: + try: + # >=python-3.3, Unix + from time import clock_gettime + try: + # >={kernel}-sources-2.6.28 + from time import CLOCK_MONOTONIC_RAW as CLOCK_ID + except ImportError: + from time import CLOCK_MONOTONIC as CLOCK_ID # NOQA + + monotonic = lambda: clock_gettime(CLOCK_ID) + + except ImportError: + # >=python-3.3 + from time import monotonic # NOQA + +except ImportError: + import ctypes + import sys + + try: + if sys.platform == 'win32': + # Windows only + GetTickCount64 = ctypes.windll.kernel32.GetTickCount64 + GetTickCount64.restype = ctypes.c_ulonglong + + def monotonic(): # NOQA + return GetTickCount64() / 1000 + + elif sys.platform == 'darwin': + # Mac OS X + from ctypes.util import find_library + + libc_name = find_library('c') + if not libc_name: + raise OSError + + libc = ctypes.CDLL(libc_name, use_errno=True) + + mach_absolute_time = libc.mach_absolute_time + mach_absolute_time.argtypes = () + mach_absolute_time.restype = ctypes.c_uint64 + + class mach_timebase_info_data_t(ctypes.Structure): + _fields_ = ( + ('numer', ctypes.c_uint32), + ('denom', ctypes.c_uint32), + ) + mach_timebase_info_data_p = ctypes.POINTER(mach_timebase_info_data_t) + + _mach_timebase_info = libc.mach_timebase_info + _mach_timebase_info.argtypes = (mach_timebase_info_data_p,) + _mach_timebase_info.restype = ctypes.c_int + + def mach_timebase_info(): + timebase = mach_timebase_info_data_t() + _mach_timebase_info(ctypes.byref(timebase)) + return (timebase.numer, timebase.denom) + + timebase = mach_timebase_info() + factor = timebase[0] / timebase[1] * 1e-9 + + def monotonic(): # NOQA + return mach_absolute_time() * factor + else: + # linux only (no librt on OS X) + import os + + # See + CLOCK_MONOTONIC = 1 + CLOCK_MONOTONIC_RAW = 4 + + class timespec(ctypes.Structure): + _fields_ = ( + ('tv_sec', ctypes.c_long), + ('tv_nsec', ctypes.c_long) + ) + tspec = timespec() + + librt = ctypes.CDLL('librt.so.1', use_errno=True) + clock_gettime = librt.clock_gettime + clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] + + if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(tspec)) == 0: + # >={kernel}-sources-2.6.28 + clock_id = CLOCK_MONOTONIC_RAW + elif clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) == 0: + clock_id = CLOCK_MONOTONIC + else: + raise OSError + + def monotonic(): # NOQA + if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) != 0: + errno_ = ctypes.get_errno() + raise OSError(errno_, os.strerror(errno_)) + return tspec.tv_sec + tspec.tv_nsec / 1e9 + + except: + from time import time as monotonic # NOQA diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 7387acc9..9d86da1f 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -11,6 +11,6 @@ except ImportError: def urllib_read(url): try: - return urlopen(url, timeout=100).read().decode('utf-8') + return urlopen(url, timeout=10).read().decode('utf-8') except HTTPError: return diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index aa53abf8..4b45293c 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,7 +1,6 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import import os -from powerline.lib.memoize import memoize vcs_props = ( @@ -21,7 +20,6 @@ def generate_directories(path): yield path -@memoize(100) def guess(path): for directory in generate_directories(path): for vcs, vcs_dir, check in vcs_props: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 80909360..61ea332c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -8,9 +8,12 @@ import socket from multiprocessing import cpu_count from powerline.lib import add_divider_highlight_group -from powerline.lib.memoize import memoize from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from powerline.lib.time import monotonic +from powerline.lib.humanize_bytes import humanize_bytes +from collections import namedtuple, defaultdict def hostname(only_if_ssh=False): @@ -163,12 +166,11 @@ def fuzzy_time(): return ' '.join([minute, hour]) -@memoize(600) def _external_ip(query_url='http://ipv4.icanhazip.com/'): return urllib_read(query_url).strip() -def external_ip(query_url='http://ipv4.icanhazip.com/'): +class ExternalIpSegment(ThreadedSegment): '''Return external IP address. Suggested URIs: @@ -182,7 +184,21 @@ def external_ip(query_url='http://ipv4.icanhazip.com/'): Divider highlight group used: ``background:divider``. ''' - return [{'contents': _external_ip(query_url=query_url), 'divider_highlight_group': 'background:divider'}] + + def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs): + super(ExternalIpSegment, self).set_state(**kwargs) + self.query_url = query_url + + def update(self): + ip = _external_ip(query_url=self.query_url) + with self.write_lock: + self.ip = ip + + def render(self): + return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}] + + +external_ip = ExternalIpSegment() # Weather condition code descriptions available at @@ -261,8 +277,7 @@ weather_conditions_icons = { } -@memoize(1800) -def weather(unit='c', location_query=None, icons=None): +class WeatherSegment(ThreadedSegment): '''Return weather from Yahoo! Weather. Uses GeoIP lookup from http://freegeoip.net/ to automatically determine @@ -284,55 +299,87 @@ def weather(unit='c', location_query=None, icons=None): Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``. Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. ''' - import json - if not location_query: + interval = 600 + + def set_state(self, location_query=None, unit='c', **kwargs): + super(WeatherSegment, self).set_state(**kwargs) + self.location = location_query + self.url = None + self.condition = {} + self.unit = unit + + def update(self): + import json + + if not self.url: + # Do not lock attribute assignments in this branch: they are used + # only in .update() + if not self.location: + try: + location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip())) + self.location = ','.join([location_data['city'], + location_data['region_name'], + location_data['country_name']]) + except (TypeError, ValueError): + return + query_data = { + 'q': + 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' + 'select * from we where location="{0}" and unit="{1}"'.format(self.location, self.unit).encode('utf-8'), + 'format': 'json', + } + self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data) + try: - location = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip())) - location_query = ','.join([location['city'], location['region_name'], location['country_name']]) - except (TypeError, ValueError): + raw_response = urllib_read(self.url) + response = json.loads(raw_response) + condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] + condition_code = int(condition['code']) + contents = '{0}°{1}'.format(condition['temp'], self.unit.upper()) + temp = int(condition['temp']) + except (KeyError, TypeError, ValueError): + return + + try: + icon_names = weather_conditions_codes[condition_code] + except IndexError: + icon_names = (('not_available' if condition_code == 3200 else 'unknown'),) + + with self.write_lock: + self.contents = contents + self.temp = temp + self.icon_names = icon_names + + def render(self, icons=None, **kwargs): + if not hasattr(self, 'icon_names'): return None - query_data = { - 'q': - 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' - 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit).encode('utf-8'), - 'format': 'json' - } - try: - url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data) - response = json.loads(urllib_read(url)) - condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] - condition_code = int(condition['code']) - except (KeyError, TypeError, ValueError): - return None - try: - icon_names = weather_conditions_codes[condition_code] - except IndexError: - icon_names = (('not_available' if condition_code == 3200 else 'unknown'),) + for icon_name in self.icon_names: + if icons: + if icon_name in icons: + icon = icons[icon_name] + break + else: + icon = weather_conditions_icons[self.icon_names[-1]] - for icon_name in icon_names: - if icons: - if icon_name in icons: - icon = icons[icon_name] - break - else: - icon = weather_conditions_icons[icon_names[-1]] + groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather'] + return [ + { + 'contents': icon + ' ', + 'highlight_group': groups, + 'divider_highlight_group': 'background:divider', + }, + { + 'contents': self.contents, + 'highlight_group': ['weather_temp_cold' if int(self.temp) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], + 'draw_divider': False, + 'divider_highlight_group': 'background:divider', + }, + ] - groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather'] - return [ - { - 'contents': icon + ' ', - 'highlight_group': groups, - 'divider_highlight_group': 'background:divider', - }, - { - 'contents': '{0}°{1}'.format(condition['temp'], unit.upper()), - 'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], - 'draw_divider': False, - 'divider_highlight_group': 'background:divider', - }, - ] + +weather = WeatherSegment() def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): @@ -487,8 +534,29 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): return format.format(days=int(days), hours=hours, minutes=minutes) -@add_divider_highlight_group('background:divider') -def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=False): +try: + import psutil + + + def get_bytes(interface): + io_counters = psutil.network_io_counters(pernic=True) + if_io = io_counters.get(interface) + if not if_io: + return None + return if_io.bytes_recv, if_io.bytes_sent +except ImportError: + def get_bytes(interface): + try: + with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: + rx = int(file_obj.read()) + with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: + tx = int(file_obj.read()) + return (rx, tx) + except IOError: + return None + + +class _NetworkLoadSegment(KwThreadedSegment): '''Return the network load. Uses the ``psutil`` module if available for multi-platform compatibility, @@ -497,15 +565,11 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F :param str interface: network interface to measure - :param float measure_interval: - interval used to measure the network load (in seconds) :param str suffix: string appended to each load string :param bool si_prefix: use SI prefix, e.g. MB instead of MiB ''' - import time - from powerline.lib import humanize_bytes interfaces = {} @@ -516,7 +580,10 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F def compute_state(self, interface): if interface in self.interfaces: idata = self.interfaces[interface] - idata['prev'] = idata['last'] + try: + idata['prev'] = idata['last'] + except KeyError: + pass else: idata = {} if self.run_once: @@ -527,8 +594,8 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F idata['last'] = (monotonic(), _get_bytes(interface)) return idata - def render_one(self, idata, suffix='B/s', si_prefix=False, **kwargs): - if 'prev' not in idata: + def render_one(self, idata, format='⬇ {recv:>8} ⬆ {sent:>8}', suffix='B/s', si_prefix=False, **kwargs): + if not idata or 'prev' not in idata: return None t1, b1 = idata['prev'] @@ -547,7 +614,7 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F }] -network_load = NetworkLoadSegment() +network_load = _NetworkLoadSegment() def virtualenv(): @@ -555,8 +622,10 @@ def virtualenv(): return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None -@memoize(60) -def email_imap_alert(username, password, server='imap.gmail.com', port=993, folder='INBOX'): +IMAPKey = namedtuple('Key', 'username password server port folder') + + +class EmailIMAPSegment(KwThreadedSegment): '''Return unread e-mail count for IMAP servers. :param str username: @@ -572,27 +641,38 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold Highlight groups used: ``email_alert``. ''' - import imaplib - import re - if not username or not password: - return None - try: - mail = imaplib.IMAP4_SSL(server, port) - mail.login(username, password) - rc, message = mail.status(folder, '(UNSEEN)') - unread_str = message[0].decode('utf-8') - unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) - except socket.gaierror: - return None - except imaplib.IMAP4.error as e: - unread_count = str(e) - if not unread_count: - return None - return [{ - 'highlight_group': 'email_alert', - 'contents': str(unread_count), - }] + interval = 60 + + @staticmethod + def key(username, password, server='imap.gmail.com', port=993, folder='INBOX'): + return IMAPKey(username, password, server, port, folder) + + @staticmethod + def compute_state(key): + if not key.username or not key.password: + return None + try: + import imaplib + import re + mail = imaplib.IMAP4_SSL(key.server, key.port) + mail.login(key.username, key.password) + rc, message = mail.status(key.folder, '(UNSEEN)') + unread_str = message[0].decode('utf-8') + unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) + except socket.gaierror: + return None + except imaplib.IMAP4.error as e: + unread_count = str(e) + if not unread_count: + return None + return [{ + 'highlight_group': 'email_alert', + 'contents': str(unread_count), + }] + + +email_imap_alert = EmailIMAPSegment() class NowPlayingSegment(object): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index dc8c2678..30a1c248 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -11,9 +11,9 @@ except ImportError: from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group -from powerline.lib.memoize import memoize from powerline.lib.vcs import guess from powerline.lib.humanize_bytes import humanize_bytes +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from functools import wraps from collections import defaultdict @@ -309,58 +309,138 @@ def modified_buffers(text='+ ', join_str=','): return None +class KwWindowThreadedSegment(KwThreadedSegment): + def set_state(self, **kwargs): + for window in vim.windows: + buffer = window.buffer + kwargs['segment_info'] = {'bufnr': buffer.number, 'buffer': buffer} + super(KwWindowThreadedSegment, self).set_state(**kwargs) + + +class RepositorySegment(KwWindowThreadedSegment): + def __init__(self): + super(RepositorySegment, self).__init__() + self.directories = {} + + @staticmethod + def key(segment_info, **kwargs): + # FIXME os.getcwd() is not a proper variant for non-current buffers + return segment_info['buffer'].name or os.getcwd() + + def update(self): + # .compute_state() is running only in this method, and only in one + # thread, thus operations with .directories do not need write locks + # (.render() method is not using .directories). If this is changed + # .directories needs redesigning + self.directories.clear() + super(RepositorySegment, self).update() + + def compute_state(self, path): + repo = guess(path=path) + if repo: + if repo.directory in self.directories: + return self.directories[repo.directory] + else: + r = self.process_repo(repo) + self.directories[repo.directory] = r + return r + + @requires_segment_info -@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell) -def branch(segment_info, status_colors=True): +class RepositoryStatusSegment(RepositorySegment): + '''Return the status for the current repo.''' + + interval = 2 + + @staticmethod + def process_repo(repo): + return repo.status() + + +repository_status = RepositoryStatusSegment() + + +@requires_segment_info +class BranchSegment(RepositorySegment): '''Return the current working branch. :param bool status_colors: - determines whether repository status will be used to determine highlighting. Default: True. + determines whether repository status will be used to determine highlighting. Default: False. Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. Divider highlight group used: ``branch:divider``. ''' - repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) - if repo: + + interval = 0.2 + started_repository_status = False + + @staticmethod + def process_repo(repo): + return repo.branch() + + def render_one(self, update_state, segment_info, status_colors=False, **kwargs): + if not update_state: + return None + + if status_colors: + self.started_repository_status = True + return [{ - 'contents': repo.branch(), - 'highlight_group': (['branch_dirty' if repo.status() else 'branch_clean'] if status_colors else []) + ['branch'], + 'contents': update_state, + 'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info) else 'branch_clean'] + if status_colors else []) + ['branch'], 'divider_highlight_group': 'branch:divider', - }] - return None + }] + + def startup(self, **kwargs): + super(BranchSegment, self).startup() + if kwargs.get('status_colors', False): + self.started_repository_status = True + repository_status.startup() + + def shutdown(self): + if self.started_repository_status: + repository_status.shutdown() + super(BranchSegment, self).shutdown() + + +branch = BranchSegment() @requires_segment_info -@memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write) -def file_vcs_status(segment_info): +class FileVCSStatusSegment(KwWindowThreadedSegment): '''Return the VCS status for this buffer. Highlight groups used: ``file_vcs_status``. ''' - name = segment_info['buffer'].name - if name and not getbufvar(segment_info['bufnr'], '&buftype'): - repo = guess(path=os.path.abspath(name)) - if repo: - status = repo.status(os.path.relpath(name, repo.directory)) - if not status: - return None - status = status.strip() - ret = [] - for status in status: - ret.append({ - 'contents': status, - 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], - }) - return ret - return None + + interval = 0.2 + + @staticmethod + def key(segment_info, **kwargs): + name = segment_info['buffer'].name + skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype'))) + return name, skip + + @staticmethod + def compute_state(key): + name, skip = key + if not skip: + repo = guess(path=name) + if repo: + status = repo.status(os.path.relpath(name, repo.directory)) + if not status: + return None + status = status.strip() + ret = [] + for status in status: + ret.append({ + 'contents': status, + 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], + }) + return ret + return None -@requires_segment_info -@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell) -def repository_status(segment_info): - '''Return the status for the current repo.''' - repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd())) - if repo: - return repo.status().strip() or None - return None +file_vcs_status = FileVCSStatusSegment() diff --git a/tests/test_segments.py b/tests/test_segments.py index 4db6c231..592ff3bd 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -39,12 +39,13 @@ class TestCommon(TestCase): def test_user(self): new_os = new_module('os', environ={'USER': 'def'}, getpid=lambda: 1) new_psutil = new_module('psutil', Process=lambda pid: Args(username='def')) - with replace_module_attr(common, 'os', new_os), replace_module_attr(common, 'psutil', new_psutil): - self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) - new_os.geteuid = lambda: 1 - self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) - new_os.geteuid = lambda: 0 - self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) + with replace_module_attr(common, 'os', new_os): + with replace_module_attr(common, 'psutil', new_psutil): + self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) + new_os.geteuid = lambda: 1 + self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) + new_os.geteuid = lambda: 0 + self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None)): @@ -149,7 +150,7 @@ class TestCommon(TestCase): {'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) def test_cpu_load_percent(self): - with replace_module('psutil', cpu_percent=lambda **kwargs: 52.3): + with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3): self.assertEqual(common.cpu_load_percent(), '52%') def test_network_load(self): From f547e8b85f13ff6448286796bfc3293312072576 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Mar 2013 22:38:40 +0400 Subject: [PATCH 0486/1472] Some fixes for flake8 --- powerline/__init__.py | 11 ++++++----- powerline/lib/url.py | 2 +- powerline/renderer.py | 12 ++++++------ powerline/segments/common.py | 34 ++++++---------------------------- powerline/segments/vim.py | 2 +- tests/test_segments.py | 2 +- tests/vim.py | 1 + 7 files changed, 22 insertions(+), 42 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index e684c44c..1a3ac823 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -70,11 +70,12 @@ class Powerline(object): except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) - options = {'term_truecolor': common_config.get('term_truecolor', False), - 'ambiwidth': common_config.get('ambiwidth', 1), - 'tmux_escape': common_config.get('additional_escapes') == 'tmux', - 'screen_escape': common_config.get('additional_escapes') == 'screen', - } + options = { + 'term_truecolor': common_config.get('term_truecolor', False), + 'ambiwidth': common_config.get('ambiwidth', 1), + 'tmux_escape': common_config.get('additional_escapes') == 'tmux', + 'screen_escape': common_config.get('additional_escapes') == 'screen', + } self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) @staticmethod diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 9d86da1f..6e599349 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -5,7 +5,7 @@ try: from urllib.request import urlopen from urllib.parse import urlencode as urllib_urlencode # NOQA except ImportError: - from urllib2 import urlopen, HTTPError + from urllib2 import urlopen, HTTPError # NOQA from urllib import urlencode as urllib_urlencode # NOQA diff --git a/powerline/renderer.py b/powerline/renderer.py index a9c3a175..6f517012 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -26,12 +26,12 @@ class Renderer(object): self.theme_kwargs = theme_kwargs self.colorscheme = colorscheme self.width_data = { - 'N': 1, # Neutral - 'Na': 1, # Narrow - 'A': getattr(self, 'ambiwidth', 1), # Ambigious - 'H': 1, # Half-width - 'W': 2, # Wide - 'F': 2, # Fullwidth + 'N': 1, # Neutral + 'Na': 1, # Narrow + 'A': getattr(self, 'ambiwidth', 1), # Ambigious + 'H': 1, # Half-width + 'W': 2, # Wide + 'F': 2, # Fullwidth } def strwidth(self, string): diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 61ea332c..72a3931e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -13,7 +13,7 @@ from powerline.lib.vcs import guess from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.time import monotonic from powerline.lib.humanize_bytes import humanize_bytes -from collections import namedtuple, defaultdict +from collections import namedtuple def hostname(only_if_ssh=False): @@ -460,7 +460,7 @@ except ImportError: except IOError: return None - def _get_user(): + def _get_user(): # NOQA return os.environ.get('USER', None) def cpu_load_percent(**kwargs): # NOQA @@ -506,12 +506,12 @@ if os.path.exists('/proc/uptime'): return int(float(f.readline().split()[0])) elif 'psutil' in globals(): from time import time - def _get_uptime(): + def _get_uptime(): # NOQA # psutil.BOOT_TIME is not subject to clock adjustments, but time() is. # Thus it is a fallback to /proc/uptime reading and not the reverse. return int(time() - psutil.BOOT_TIME) else: - def _get_uptime(): + def _get_uptime(): # NOQA raise NotImplementedError @@ -534,29 +534,7 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): return format.format(days=int(days), hours=hours, minutes=minutes) -try: - import psutil - - - def get_bytes(interface): - io_counters = psutil.network_io_counters(pernic=True) - if_io = io_counters.get(interface) - if not if_io: - return None - return if_io.bytes_recv, if_io.bytes_sent -except ImportError: - def get_bytes(interface): - try: - with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: - rx = int(file_obj.read()) - with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: - tx = int(file_obj.read()) - return (rx, tx) - except IOError: - return None - - -class _NetworkLoadSegment(KwThreadedSegment): +class NetworkLoadSegment(KwThreadedSegment): '''Return the network load. Uses the ``psutil`` module if available for multi-platform compatibility, @@ -614,7 +592,7 @@ class _NetworkLoadSegment(KwThreadedSegment): }] -network_load = _NetworkLoadSegment() +network_load = NetworkLoadSegment() def virtualenv(): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 30a1c248..fedee2f8 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -13,7 +13,7 @@ from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess from powerline.lib.humanize_bytes import humanize_bytes -from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from powerline.lib.threaded import KwThreadedSegment from functools import wraps from collections import defaultdict diff --git a/tests/test_segments.py b/tests/test_segments.py index 592ff3bd..8b407b78 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -4,7 +4,7 @@ from powerline.segments import shell, common import tests.vim as vim_module import sys import os -from tests.lib import Args, urllib_read, replace_module, replace_module_attr, new_module, replace_module_module, replace_env +from tests.lib import Args, urllib_read, replace_module_attr, new_module, replace_module_module, replace_env from tests import TestCase diff --git a/tests/vim.py b/tests/vim.py index 08e57276..6ba67e4a 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -149,6 +149,7 @@ def _emul_exists(varname): return varname[2:] in _g raise NotImplementedError + @_logged def _emul_line2byte(line): buflines = _buf_lines[_buffer()] From 84dfac6f8e06823255d33180be82c7fa08dea135 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 13:55:31 +0400 Subject: [PATCH 0487/1472] Avoid using python setup.py test Closes #295 --- tests/test.sh | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/test.sh b/tests/test.sh index 1e4c62ec..4c6704f5 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,19 +1,12 @@ #!/bin/sh : ${PYTHON:=python} FAILED=0 -if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then - # Python 2.6 - export PYTHONPATH="${PYTHONPATH}:`realpath .`" - for file in tests/test_*.py ; do - if ! ${PYTHON} $file ; then - FAILED=1 - fi - done -else - if ! ${PYTHON} setup.py test ; then +export PYTHONPATH="${PYTHONPATH}:`realpath .`" +for file in tests/test_*.py ; do + if ! ${PYTHON} $file ; then FAILED=1 fi -fi +done if ! ${PYTHON} scripts/powerline-lint ; then FAILED=1 fi From 050a34bceb87f3cc50e7bb5b02258526aa7bbe7d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 14:01:10 +0400 Subject: [PATCH 0488/1472] Avoid ResourceWarning messages: close stdout --- powerline/lib/vcs/git.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index f7ea2e83..033d893b 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -97,8 +97,9 @@ except ImportError: def readlines(cmd, cwd): p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) p.stderr.close() - for line in p.stdout: - yield line[:-1].decode('utf-8') + with p.stdout: + for line in p.stdout: + yield line[:-1].decode('utf-8') class Repository(object): __slots__ = ('directory',) From f9e9b2cd224d4b1fc474525663018633bde48795 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 14:52:02 +0400 Subject: [PATCH 0489/1472] Change uptime format, make it accept seconds if needed It is uncommon to use integers like 00 or 01 for days. --- powerline/segments/common.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 72a3931e..dae649a4 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import + import os import sys @@ -516,11 +518,12 @@ else: @add_divider_highlight_group('background:divider') -def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): +def uptime(format='{days}d {hours:02d}h {minutes:02d}m'): '''Return system uptime. :param str format: - format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments + format string, will be passed ``days``, ``hours``, ``minutes`` and + seconds as arguments Divider highlight group used: ``background:divider``. ''' @@ -531,7 +534,7 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) - return format.format(days=int(days), hours=hours, minutes=minutes) + return format.format(days=int(days), hours=hours, minutes=minutes, seconds=seconds) class NetworkLoadSegment(KwThreadedSegment): From a1b0b6239f3a5e1970e85fc6cb45f70ec8fbea21 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 15:11:26 +0400 Subject: [PATCH 0490/1472] Add format keyword argument to network_load segment --- powerline/segments/common.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index dae649a4..87124339 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -550,6 +550,8 @@ class NetworkLoadSegment(KwThreadedSegment): string appended to each load string :param bool si_prefix: use SI prefix, e.g. MB instead of MiB + :param str format: + format string, receives ``recv`` and ``sent`` as arguments ''' interfaces = {} @@ -587,9 +589,9 @@ class NetworkLoadSegment(KwThreadedSegment): return None return [{ - 'contents': '⬇ {rx_diff} ⬆ {tx_diff}'.format( - rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8), - tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8), + 'contents': format.format( + recv=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix), + sent=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix), ), 'divider_highlight_group': 'background:divider', }] From ceabd372d4661e21e0874cb46dd35df2ba84a998 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 15:30:47 +0400 Subject: [PATCH 0491/1472] Add temperature_format keyword argument to weather segment --- powerline/segments/common.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 87124339..57f188b0 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -278,6 +278,19 @@ weather_conditions_icons = { 'unknown': '⚠', } +temp_conversions = { + 'C': lambda temp: temp, + 'F': lambda temp: (temp * 9 / 5) + 32, + 'K': lambda temp: temp + 273.15, + } + +# Note: there are also unicode characters for units: ℃, ℉ and K +temp_units = { + 'C': '°C', + 'F': '°F', + 'K': 'K', + } + class WeatherSegment(ThreadedSegment): '''Return weather from Yahoo! Weather. @@ -295,6 +308,8 @@ class WeatherSegment(ThreadedSegment): location query for your current location, e.g. ``oslo, norway`` :param dict icons: dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` + :param str temperature_format: + format string, receives ``temp`` as an argument. Should also hold unit. Divider highlight group used: ``background:divider``. @@ -304,12 +319,11 @@ class WeatherSegment(ThreadedSegment): interval = 600 - def set_state(self, location_query=None, unit='c', **kwargs): + def set_state(self, location_query=None, **kwargs): super(WeatherSegment, self).set_state(**kwargs) self.location = location_query self.url = None self.condition = {} - self.unit = unit def update(self): import json @@ -328,7 +342,7 @@ class WeatherSegment(ThreadedSegment): query_data = { 'q': 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' - 'select * from we where location="{0}" and unit="{1}"'.format(self.location, self.unit).encode('utf-8'), + 'select * from we where location="{0}" and unit="c"'.format(self.location).encode('utf-8'), 'format': 'json', } self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data) @@ -338,8 +352,7 @@ class WeatherSegment(ThreadedSegment): response = json.loads(raw_response) condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) - contents = '{0}°{1}'.format(condition['temp'], self.unit.upper()) - temp = int(condition['temp']) + temp = float(condition['temp']) except (KeyError, TypeError, ValueError): return @@ -349,11 +362,10 @@ class WeatherSegment(ThreadedSegment): icon_names = (('not_available' if condition_code == 3200 else 'unknown'),) with self.write_lock: - self.contents = contents self.temp = temp self.icon_names = icon_names - def render(self, icons=None, **kwargs): + def render(self, icons=None, unit='C', temperature_format=None, **kwargs): if not hasattr(self, 'icon_names'): return None @@ -365,6 +377,8 @@ class WeatherSegment(ThreadedSegment): else: icon = weather_conditions_icons[self.icon_names[-1]] + temperature_format = temperature_format or ('{temp:.0f}' + temp_units[unit]) + temp = temp_conversions[unit](self.temp) groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather'] return [ { @@ -373,7 +387,7 @@ class WeatherSegment(ThreadedSegment): 'divider_highlight_group': 'background:divider', }, { - 'contents': self.contents, + 'contents': temperature_format.format(temp=temp), 'highlight_group': ['weather_temp_cold' if int(self.temp) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', From fb2e9b6cdb6d5cd215d0799251835276c61da02f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 18:46:59 +0400 Subject: [PATCH 0492/1472] Test all other segments Still uncovered: email_imap_alert and now_playing --- tests/lib/__init__.py | 2 +- tests/test_segments.py | 100 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 872be033..fab70e9b 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -24,7 +24,7 @@ def urllib_read(query_url): elif query_url.startswith('http://freegeoip.net/json/'): return '{"city": "Meppen", "region_code": "06", "region_name": "Niedersachsen", "areacode": "", "ip": "82.145.55.16", "zipcode": "49716", "longitude": 7.3167, "country_name": "Germany", "country_code": "DE", "metrocode": "", "latitude": 52.6833}' elif query_url.startswith('http://query.yahooapis.com/v1/public/'): - return '{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"
\nCurrent Conditions:
\nPartly Cloudy, -11 C
\n
Forecast:
\nSat - Partly Cloudy. High: -9 Low: -19
\nSun - Partly Cloudy. High: -12 Low: -18
\n
\nFull Forecast at Yahoo! Weather

\n(provided by The Weather Channel)
","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}' + return r'{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"
\nCurrent Conditions:
\nPartly Cloudy, -11 C
\n
Forecast:
\nSat - Partly Cloudy. High: -9 Low: -19
\nSun - Partly Cloudy. High: -12 Low: -18
\n
\nFull Forecast at Yahoo! Weather

\n(provided by The Weather Channel)
","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}' else: raise NotImplementedError diff --git a/tests/test_segments.py b/tests/test_segments.py index 8b407b78..6eedcbae 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -130,12 +130,41 @@ class TestCommon(TestCase): self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}]) def test_uptime(self): - # TODO - pass + with replace_module_attr(common, '_get_uptime', lambda: 65536): + self.assertEqual(common.uptime(), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}]) + + def _get_uptime(): + raise NotImplementedError + + with replace_module_attr(common, '_get_uptime', _get_uptime): + self.assertEqual(common.uptime(), None) def test_weather(self): - # TODO - pass + with replace_module_attr(common, 'urllib_read', urllib_read): + self.assertEqual(common.weather(), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'} + ]) + self.assertEqual(common.weather(icons={'cloudy': 'o'}), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'} + ]) + self.assertEqual(common.weather(icons={'partly_cloudy_day': 'x'}), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'} + ]) + self.assertEqual(common.weather(unit='F'), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '12°F'} + ]) + self.assertEqual(common.weather(unit='K'), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '262K'} + ]) + self.assertEqual(common.weather(temperature_format='{temp:.1e}C'), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-1.1e+01C'} + ]) def test_system_load(self): with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): @@ -154,8 +183,35 @@ class TestCommon(TestCase): self.assertEqual(common.cpu_load_percent(), '52%') def test_network_load(self): - # TODO - pass + def _get_bytes(interface): + return None + with replace_module_attr(common, '_get_bytes', _get_bytes): + self.assertEqual(common.network_load(), None) + l = [0, 0] + + def _get_bytes2(interface): + l[0] += 1200 + l[1] += 2400 + return tuple(l) + + from imp import reload + reload(common) + with replace_module_attr(common, '_get_bytes', _get_bytes2): + common.network_load.startup() + common.network_load.sleep(0) + common.network_load.sleep(0) + self.assertEqual(common.network_load(), [ + {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s ⬆ 2 KiB/s'} + ]) + self.assertEqual(common.network_load(format='r {recv} s {sent}'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s s 2 KiB/s'} + ]) + self.assertEqual(common.network_load(format='r {recv} s {sent}', suffix='bps'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps s 2 Kibps'} + ]) + self.assertEqual(common.network_load(format='r {recv} s {sent}', si_prefix=True), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s s 2 kB/s'} + ]) def test_virtualenv(self): with replace_env('VIRTUAL_ENV', '/abc/def/ghi'): @@ -269,16 +325,36 @@ class TestVim(TestCase): self.assertEqual(vim.modified_buffers(), None) def test_branch(self): - # TODO - pass + with vim_module._with('buffer', '/foo') as segment_info: + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): + self.assertEqual(vim.branch(segment_info=segment_info), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) + self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): + self.assertEqual(vim.branch(segment_info=segment_info), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) + self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) def test_file_vcs_status(self): - # TODO - pass + with vim_module._with('buffer', '/foo') as segment_info: + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): + self.assertEqual(vim.file_vcs_status(segment_info=segment_info), + [{'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'}]) + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)): + self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None) + with vim_module._with('buffer', '/bar') as segment_info: + with vim_module._with('bufoptions', buftype='nofile'): + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): + self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None) def test_repository_status(self): - # TODO - pass + segment_info = vim_module._get_segment_info() + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): + self.assertEqual(vim.repository_status(segment_info=segment_info), None) + with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): + self.assertEqual(vim.repository_status(segment_info=segment_info), 'DU') old_cwd = None From ce65470ac3822df21495da415d03d6ce9cf1dbbe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 18:59:44 +0400 Subject: [PATCH 0493/1472] Fix shorten_cwd handling --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index fedee2f8..6926d3c5 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -170,7 +170,7 @@ def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_ho if not name: return None file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') - + (':.' if shorten_home else '') + ':h') + + (':.' if shorten_cwd else '') + ':h') if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory + os.sep if file_directory else None From 19b45e609a3cd1cb2d3611e53aebcabd2a8c24c6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 20:20:21 +0400 Subject: [PATCH 0494/1472] Fix documentation for threaded segments --- docs/source/conf.py | 5 +- docs/source/powerline_autodoc.py | 56 ++++++++++++ powerline/lib/threaded.py | 5 ++ powerline/segments/common.py | 150 +++++++++++++++---------------- powerline/segments/vim.py | 53 +++++------ 5 files changed, 157 insertions(+), 112 deletions(-) create mode 100644 docs/source/powerline_autodoc.py diff --git a/docs/source/conf.py b/docs/source/conf.py index c2af72cb..645a493c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,9 +3,10 @@ import os import sys -sys.path.insert(0, os.path.abspath('../..')) +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(os.getcwd())))) +sys.path.insert(0, os.path.abspath(os.getcwd())) -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] +extensions = ['powerline_autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] source_suffix = '.rst' master_doc = 'index' project = u'Powerline' diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py new file mode 100644 index 00000000..69dfd08e --- /dev/null +++ b/docs/source/powerline_autodoc.py @@ -0,0 +1,56 @@ +from sphinx.ext import autodoc +from sphinx.util.inspect import getargspec +from inspect import ArgSpec, getargspec, formatargspec +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from itertools import count + + +class ThreadedDocumenter(autodoc.FunctionDocumenter): + '''Specialized documenter subclass for ThreadedSegment subclasses.''' + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + return (isinstance(member, ThreadedSegment) or + super(ThreadedDocumenter, cls).can_document_member(member, membername, isattr, parent)) + + def format_args(self): + if isinstance(self.object, ThreadedSegment): + args = ['interval'] + defaults = [getattr(self.object, 'interval', 1)] + methods = ['render', 'set_state'] + if isinstance(self.object, KwThreadedSegment): + methods += ['key', 'render_one'] + + for method in methods: + if hasattr(self.object, method): + # Note: on = -i: + default = argspec.defaults[i] + defaults.append(default) + args.append(arg) + else: + args.insert(0, arg) + argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) + else: + argspec = getargspec(self.object) + args = argspec.args + defaults = argspec.defaults + if args and args[0] == 'segment_info' and getattr(self.object, 'requires_powerline_segment_info', None): + args = args[1:] + if defaults and len(defaults) > len(args): + defaults = defaults[1:] + argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=defaults) + + return formatargspec(*argspec).replace('\\', '\\\\') + + +def setup(app): + autodoc.setup(app) + app.add_autodocumenter(ThreadedDocumenter) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 5ce4b055..0f2fb12a 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -123,3 +123,8 @@ class KwThreadedSegment(ThreadedSegment): @staticmethod def render_one(update_state, **kwargs): return update_state + + +def with_docstring(instance, doc): + instance.__doc__ = doc + return instance diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 57f188b0..f4342c27 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -12,7 +12,7 @@ from multiprocessing import cpu_count from powerline.lib import add_divider_highlight_group from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess -from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring from powerline.lib.time import monotonic from powerline.lib.humanize_bytes import humanize_bytes from collections import namedtuple @@ -173,20 +173,6 @@ def _external_ip(query_url='http://ipv4.icanhazip.com/'): class ExternalIpSegment(ThreadedSegment): - '''Return external IP address. - - Suggested URIs: - - * http://ipv4.icanhazip.com/ - * http://ipv6.icanhazip.com/ - * http://icanhazip.com/ (returns IPv6 address if available, else IPv4) - - :param str query_url: - URI to query for IP address, should return only the IP address as a text string - - Divider highlight group used: ``background:divider``. - ''' - def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs): super(ExternalIpSegment, self).set_state(**kwargs) self.query_url = query_url @@ -200,7 +186,20 @@ class ExternalIpSegment(ThreadedSegment): return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}] -external_ip = ExternalIpSegment() +external_ip = with_docstring(ExternalIpSegment(), +'''Return external IP address. + +Suggested URIs: + +* http://ipv4.icanhazip.com/ +* http://ipv6.icanhazip.com/ +* http://icanhazip.com/ (returns IPv6 address if available, else IPv4) + +:param str query_url: + URI to query for IP address, should return only the IP address as a text string + +Divider highlight group used: ``background:divider``. +''') # Weather condition code descriptions available at @@ -293,30 +292,6 @@ temp_units = { class WeatherSegment(ThreadedSegment): - '''Return weather from Yahoo! Weather. - - Uses GeoIP lookup from http://freegeoip.net/ to automatically determine - your current location. This should be changed if you're in a VPN or if your - IP address is registered at another location. - - Returns a list of colorized icon and temperature segments depending on - weather conditions. - - :param str unit: - temperature unit, can be one of ``F``, ``C`` or ``K`` - :param str location_query: - location query for your current location, e.g. ``oslo, norway`` - :param dict icons: - dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` - :param str temperature_format: - format string, receives ``temp`` as an argument. Should also hold unit. - - Divider highlight group used: ``background:divider``. - - Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``. - Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. - ''' - interval = 600 def set_state(self, location_query=None, **kwargs): @@ -395,7 +370,30 @@ class WeatherSegment(ThreadedSegment): ] -weather = WeatherSegment() +weather = with_docstring(WeatherSegment(), +'''Return weather from Yahoo! Weather. + +Uses GeoIP lookup from http://freegeoip.net/ to automatically determine +your current location. This should be changed if you're in a VPN or if your +IP address is registered at another location. + +Returns a list of colorized icon and temperature segments depending on +weather conditions. + +:param str unit: + temperature unit, can be one of ``F``, ``C`` or ``K`` +:param str location_query: + location query for your current location, e.g. ``oslo, norway`` +:param dict icons: + dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` +:param str temperature_format: + format string, receives ``temp`` as an argument. Should also hold unit. + +Divider highlight group used: ``background:divider``. + +Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``. +Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. +''') def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): @@ -552,22 +550,6 @@ def uptime(format='{days}d {hours:02d}h {minutes:02d}m'): class NetworkLoadSegment(KwThreadedSegment): - '''Return the network load. - - Uses the ``psutil`` module if available for multi-platform compatibility, - falls back to reading - :file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`. - - :param str interface: - network interface to measure - :param str suffix: - string appended to each load string - :param bool si_prefix: - use SI prefix, e.g. MB instead of MiB - :param str format: - format string, receives ``recv`` and ``sent`` as arguments - ''' - interfaces = {} @staticmethod @@ -611,7 +593,22 @@ class NetworkLoadSegment(KwThreadedSegment): }] -network_load = NetworkLoadSegment() +network_load = with_docstring(NetworkLoadSegment(), +'''Return the network load. + +Uses the ``psutil`` module if available for multi-platform compatibility, +falls back to reading +:file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`. + +:param str interface: + network interface to measure +:param str suffix: + string appended to each load string +:param bool si_prefix: + use SI prefix, e.g. MB instead of MiB +:param str format: + format string, receives ``recv`` and ``sent`` as arguments +''') def virtualenv(): @@ -619,31 +616,15 @@ def virtualenv(): return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None -IMAPKey = namedtuple('Key', 'username password server port folder') +_IMAPKey = namedtuple('Key', 'username password server port folder') class EmailIMAPSegment(KwThreadedSegment): - '''Return unread e-mail count for IMAP servers. - - :param str username: - login username - :param str password: - login password - :param str server: - e-mail server - :param int port: - e-mail server port - :param str folder: - folder to check for e-mails - - Highlight groups used: ``email_alert``. - ''' - interval = 60 @staticmethod def key(username, password, server='imap.gmail.com', port=993, folder='INBOX'): - return IMAPKey(username, password, server, port, folder) + return _IMAPKey(username, password, server, port, folder) @staticmethod def compute_state(key): @@ -669,7 +650,22 @@ class EmailIMAPSegment(KwThreadedSegment): }] -email_imap_alert = EmailIMAPSegment() +email_imap_alert = with_docstring(EmailIMAPSegment(), +'''Return unread e-mail count for IMAP servers. + +:param str username: + login username +:param str password: + login password +:param str server: + e-mail server +:param int port: + e-mail server port +:param str folder: + folder to check for e-mails + +Highlight groups used: ``email_alert``. +''') class NowPlayingSegment(object): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 6926d3c5..cb0f7124 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -13,7 +13,7 @@ from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess from powerline.lib.humanize_bytes import humanize_bytes -from powerline.lib.threaded import KwThreadedSegment +from powerline.lib.threaded import KwThreadedSegment, with_docstring from functools import wraps from collections import defaultdict @@ -77,28 +77,18 @@ def launchevent(event): pass -def bufnr(segment_info, **kwargs): - '''Used for cache key, returns current buffer number''' - return segment_info['bufnr'] - - -def bufname(segment_info, **kwargs): - '''Used for cache key, returns current buffer name''' - return segment_info['buffer'].name - - # TODO Remove cache when needed def window_cached(func): cache = {} @requires_segment_info @wraps(func) - def ret(segment_info, *args, **kwargs): + def ret(segment_info, **kwargs): window_id = segment_info['window_id'] if segment_info['mode'] == 'nc': return cache.get(window_id) else: - r = func(*args, **kwargs) + r = func(**kwargs) cache[window_id] = r return r @@ -348,8 +338,6 @@ class RepositorySegment(KwWindowThreadedSegment): @requires_segment_info class RepositoryStatusSegment(RepositorySegment): - '''Return the status for the current repo.''' - interval = 2 @staticmethod @@ -357,21 +345,12 @@ class RepositoryStatusSegment(RepositorySegment): return repo.status() -repository_status = RepositoryStatusSegment() +repository_status = with_docstring(RepositoryStatusSegment(), +'''Return the status for the current repo.''') @requires_segment_info class BranchSegment(RepositorySegment): - '''Return the current working branch. - - :param bool status_colors: - determines whether repository status will be used to determine highlighting. Default: False. - - Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. - - Divider highlight group used: ``branch:divider``. - ''' - interval = 0.2 started_repository_status = False @@ -405,16 +384,20 @@ class BranchSegment(RepositorySegment): super(BranchSegment, self).shutdown() -branch = BranchSegment() +branch = with_docstring(BranchSegment(), +'''Return the current working branch. + +:param bool status_colors: + determines whether repository status will be used to determine highlighting. Default: False. + +Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. + +Divider highlight group used: ``branch:divider``. +''') @requires_segment_info class FileVCSStatusSegment(KwWindowThreadedSegment): - '''Return the VCS status for this buffer. - - Highlight groups used: ``file_vcs_status``. - ''' - interval = 0.2 @staticmethod @@ -443,4 +426,8 @@ class FileVCSStatusSegment(KwWindowThreadedSegment): return None -file_vcs_status = FileVCSStatusSegment() +file_vcs_status = with_docstring(FileVCSStatusSegment(), +'''Return the VCS status for this buffer. + +Highlight groups used: ``file_vcs_status``. +''') From 34fba2286ace253d187628ff4e04d6db02da0673 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 21:06:56 +0400 Subject: [PATCH 0495/1472] Fix documentation for wrapped functions --- docs/source/powerline_autodoc.py | 28 +++++++++++++++++++--------- powerline/lib/__init__.py | 10 +++++++++- powerline/segments/common.py | 2 +- powerline/segments/vim.py | 2 +- powerline/theme.py | 6 +++--- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 69dfd08e..93dbc312 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -28,7 +28,7 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): for i, arg in zip(count(-1, -1), reversed(argspec.args)): if (arg == 'self' or (arg == 'segment_info' and - getattr(self.object, 'requires_powerline_segment_info', None)) or + getattr(self.object, 'powerline_requires_segment_info', None)) or (method == 'render_one' and -i == len(argspec.args))): continue if argspec.defaults and len(argspec.defaults) >= -i: @@ -39,14 +39,24 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): args.insert(0, arg) argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) else: - argspec = getargspec(self.object) - args = argspec.args - defaults = argspec.defaults - if args and args[0] == 'segment_info' and getattr(self.object, 'requires_powerline_segment_info', None): - args = args[1:] - if defaults and len(defaults) > len(args): - defaults = defaults[1:] - argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=defaults) + if hasattr(self.object, 'powerline_origin'): + obj = self.object.powerline_origin + else: + obj = self.object + + argspec = getargspec(obj) + args = [] + defaults = [] + for i, arg in zip(count(-1, -1), reversed(argspec.args)): + if (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)): + continue + if argspec.defaults and len(argspec.defaults) >= -i: + default = argspec.defaults[i] + defaults.append(default) + args.append(arg) + else: + args.insert(0, arg) + argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults)) return formatargspec(*argspec).replace('\\', '\\\\') diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index a2e4be4a..8f6a7ba2 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -3,6 +3,14 @@ from functools import wraps import json +def wraps_saveargs(wrapped): + def dec(wrapper): + r = wraps(wrapped)(wrapper) + r.powerline_origin = getattr(wrapped, 'powerline_origin', wrapped) + return r + return dec + + def mergedicts(d1, d2): '''Recursively merge two dictionaries. First dictionary is modified in-place. ''' @@ -15,7 +23,7 @@ def mergedicts(d1, d2): def add_divider_highlight_group(highlight_group): def dec(func): - @wraps(func) + @wraps_saveargs(func) def f(**kwargs): r = func(**kwargs) if r: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f4342c27..744fca7d 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -477,7 +477,7 @@ except ImportError: def _get_user(): # NOQA return os.environ.get('USER', None) - def cpu_load_percent(**kwargs): # NOQA + def cpu_load_percent(measure_interval=.5): # NOQA '''Return the average CPU load as a percentage. Requires the ``psutil`` module. diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index cb0f7124..e2df1896 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -14,7 +14,7 @@ from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.threaded import KwThreadedSegment, with_docstring -from functools import wraps +from powerline.lib import wraps_saveargs as wraps from collections import defaultdict vim_funcs = { diff --git a/powerline/theme.py b/powerline/theme.py index 725248e5..02647c83 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -19,7 +19,7 @@ def u(s): def requires_segment_info(func): - func.requires_powerline_segment_info = True + func.powerline_requires_segment_info = True return func @@ -72,8 +72,8 @@ class Theme(object): parsed_segments = [] for segment in self.segments[side]: if segment['type'] == 'function': - if (hasattr(segment['contents_func'], 'requires_powerline_segment_info') - and segment['contents_func'].requires_powerline_segment_info): + if (hasattr(segment['contents_func'], 'powerline_requires_segment_info') + and segment['contents_func'].powerline_requires_segment_info): contents = segment['contents_func'](segment_info=self.segment_info, **segment['args']) else: contents = segment['contents_func'](**segment['args']) From 60cc0196e8fbbebd8943590928c9bd3cf4ac1e78 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 21:15:42 +0400 Subject: [PATCH 0496/1472] Remove @ from documentation which was present for unknown reason --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 744fca7d..13ad6b8c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -30,7 +30,7 @@ def hostname(only_if_ssh=False): def branch(status_colors=True): - '''Return the current VCS branch.@ + '''Return the current VCS branch. :param bool status_colors: determines whether repository status will be used to determine highlighting. Default: True. From 674a211cdaa0dcd6e9a4827ca8f794430ceb0a90 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Mar 2013 22:26:57 +0400 Subject: [PATCH 0497/1472] Remove escape sequences from default strings in documentation Note: to properly view some values in browser you need the browser to use patched font or fontconfig. Though there is not much difference between some cryptic escape sequence and singe wrongly displayed character. --- docs/source/powerline_autodoc.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 93dbc312..a3a3d54b 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -4,6 +4,18 @@ from inspect import ArgSpec, getargspec, formatargspec from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from itertools import count +try: + from __builtin__ import unicode +except ImportError: + unicode = lambda s, enc: s + + +def formatvalue(val): + if type(val) is str: + return '="' + unicode(val, 'utf-8').replace('"', '\\"').replace('\\', '\\\\') + '"' + else: + return '=' + repr(val) + class ThreadedDocumenter(autodoc.FunctionDocumenter): '''Specialized documenter subclass for ThreadedSegment subclasses.''' @@ -58,7 +70,7 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): args.insert(0, arg) argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults)) - return formatargspec(*argspec).replace('\\', '\\\\') + return formatargspec(*argspec, formatvalue=formatvalue).replace('\\', '\\\\') def setup(app): From b0495d028f14d9ad38f783737138ba9cce191432 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Mar 2013 07:56:25 +0400 Subject: [PATCH 0498/1472] Do not let daemon threads be stopped while being updated --- powerline/lib/threaded.py | 1 + powerline/renderer.py | 3 +++ powerline/shell.py | 4 ++-- scripts/powerline | 2 +- tests/test_configuration.py | 13 ++++++++----- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 0f2fb12a..20a9954b 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -44,6 +44,7 @@ class ThreadedSegment(Thread): def shutdown(self): self.keep_going = False + self.update_lock.acquire() def set_interval(self, interval=None, **kwargs): # Allowing “interval” keyword in configuration. diff --git a/powerline/renderer.py b/powerline/renderer.py index 6f517012..074c3380 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -40,6 +40,9 @@ class Renderer(object): def get_theme(self, matcher_info): return self.theme + def shutdown(self): + self.theme.shutdown() + def get_highlighting(self, segment, mode): segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) if segment['divider_highlight_group']: diff --git a/powerline/shell.py b/powerline/shell.py index e811066f..388a5e7b 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -14,10 +14,10 @@ def mergeargs(argvalue): class ShellPowerline(Powerline): - def __init__(self, args): + def __init__(self, args, run_once=False): self.args = args self.theme_option = mergeargs(args.theme_option) or {} - super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=True) + super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once) def get_segment_info(self): return self.args diff --git a/scripts/powerline b/scripts/powerline index 01a74892..f36a805c 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -12,7 +12,7 @@ except ImportError: if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() - powerline = ShellPowerline(args) + powerline = ShellPowerline(args, run_once=True) rendered = powerline.renderer.render(width=args.width, side=args.side) try: sys.stdout.write(rendered) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index bd5f9e89..c3739493 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -49,6 +49,7 @@ class TestConfig(TestCase): check_output(1, 0) finally: vim_module._start_mode('n') + powerline.renderer.shutdown() def test_tmux(self): from powerline.segments import common @@ -56,16 +57,16 @@ class TestConfig(TestCase): reload(common) from powerline.shell import ShellPowerline with replace_module_attr(common, 'urllib_read', urllib_read): - ShellPowerline(Args(ext=['tmux'])).renderer.render() + ShellPowerline(Args(ext=['tmux']), run_once=True).renderer.render() reload(common) def test_zsh(self): from powerline.shell import ShellPowerline - ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt')).renderer.render() + ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=True).renderer.render() def test_bash(self): from powerline.shell import ShellPowerline - ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})])).renderer.render() + ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=True).renderer.render() def test_ipython(self): from powerline.ipython import IpythonPowerline @@ -75,7 +76,9 @@ class TestConfig(TestCase): config_overrides = None theme_overrides = {} - IpyPowerline().renderer.render() + powerline = IpyPowerline() + powerline.renderer.render() + powerline.renderer.shutdown() def test_wm(self): from powerline.segments import common @@ -83,7 +86,7 @@ class TestConfig(TestCase): reload(common) from powerline import Powerline with replace_module_attr(common, 'urllib_read', urllib_read): - Powerline(ext='wm', renderer_module='pango_markup').renderer.render() + Powerline(ext='wm', renderer_module='pango_markup', run_once=True).renderer.render() reload(common) From bb63722fb16cbe8d1b39b796c0839e6c911436be Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Mar 2013 08:15:02 +0400 Subject: [PATCH 0499/1472] Also call .shutdown() in ipython --- powerline/bindings/ipython/post_0_11.py | 7 +++++++ powerline/bindings/ipython/pre_0_11.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index c0c40ce1..e8a685a6 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -2,6 +2,7 @@ from powerline.ipython import IpythonPowerline from IPython.core.prompts import PromptManager +from IPython.core.hooks import TryNext class PowerlinePromptManager(PromptManager): @@ -41,6 +42,12 @@ def load_ipython_extension(ip): ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell, config=ip.prompt_manager.config) + def shutdown_hook(): + powerline.renderer.shutdown() + raise TryNext() + + ip.hooks.shutdown_hook.add(shutdown_hook) + def unload_ipython_extension(ip): ip.prompt_manager = old_prompt_manager diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 098ddb6b..b83780d4 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -2,6 +2,7 @@ from powerline.ipython import IpythonPowerline from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython +from IPython.ipapi import TryNext class PowerlinePrompt(BasePrompt): @@ -38,4 +39,11 @@ def setup(prompt='1', **kwargs): old_prompt = getattr(ip.IP.outputcache, attr) setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline, old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left)) + raise TryNext() + + def shutdown_hook(): + powerline.renderer.shutdown() + raise TryNext() + ip.IP.hooks.late_startup_hook.add(late_startup_hook) + ip.IP.hooks.shutdown_hook.add(shutdown_hook) From c92aaef0e50fd3203ae188284744924ce3517ab3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Mar 2013 00:42:46 +0530 Subject: [PATCH 0500/1472] bzr segment: Handle repos with shelves correctly Return None instead of a string with spaces for a clean repo with shelves. --- powerline/lib/vcs/bzr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index 89c22823..c243836f 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -53,7 +53,8 @@ class Repository(object): dirtied = 'D' elif line and line[0] == '?': untracked = 'U' - return dirtied + untracked + ans = dirtied + untracked + return ans if ans.strip() else None def branch(self): try: From 137ae5f990bc9500e750a985e49b17cd56e69ed4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Mar 2013 23:55:14 +0400 Subject: [PATCH 0501/1472] Change hook type It makes sense to run _powerline_tmux_set_pwd only when working directory is changed. --- powerline/bindings/zsh/powerline.zsh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 4499bf9c..04360269 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,4 +1,5 @@ _powerline_tmux_setenv() { + emulate -L zsh if [[ -n "$TMUX" ]]; then tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" fi @@ -13,12 +14,13 @@ _powerline_tmux_set_columns() { } _powerline_install_precmd() { + emulate -L zsh for f in "${precmd_functions[@]}"; do if [[ "$f" = "_powerline_precmd" ]]; then return fi done - precmd_functions+=(_powerline_tmux_set_pwd) + chpwd_functions+=( _powerline_tmux_set_pwd ) setopt promptpercent setopt promptsubst if zmodload zsh/zpython &>/dev/null ; then @@ -32,6 +34,6 @@ _powerline_install_precmd() { } trap "_powerline_tmux_set_columns" SIGWINCH -kill -SIGWINCH $$ +_powerline_tmux_set_columns _powerline_install_precmd From ca723aafaa7038372eb80ef75d8b80d962260e02 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Mar 2013 23:58:06 +0400 Subject: [PATCH 0502/1472] Skip external_ip segment if self.ip is not defined Ref #335 --- powerline/segments/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 13ad6b8c..2cc3254c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -183,6 +183,8 @@ class ExternalIpSegment(ThreadedSegment): self.ip = ip def render(self): + if not hasattr(self, 'ip'): + return None return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}] From 4551bd2887f560f7522809320bcd3dc1b282132d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 00:18:51 +0400 Subject: [PATCH 0503/1472] =?UTF-8?q?threaded:=20Add=20.update=5Ffirst,=20?= =?UTF-8?q?leave=20.queries=20as-is=20unless=20key=E2=80=99s=20missing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #335 Closes #336 --- powerline/lib/threaded.py | 35 +++++++++++++++++++++-------------- powerline/segments/common.py | 4 ++-- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 20a9954b..b9841cec 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -11,6 +11,8 @@ from threading import Thread, Lock class ThreadedSegment(Thread): daemon = True min_sleep_time = 0.1 + update_first = True + interval = 1 def __init__(self): super(ThreadedSegment, self).__init__() @@ -46,24 +48,25 @@ class ThreadedSegment(Thread): self.keep_going = False self.update_lock.acquire() - def set_interval(self, interval=None, **kwargs): + def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. # Note: Here **kwargs is needed to support foreign data, in subclasses # it can be seen in a number of places in order to support # .set_interval(). - interval = interval or getattr(self, 'interval', 1) + interval = interval or getattr(self, 'interval') self.interval = interval self.has_set_interval = True - def set_state(self, **kwargs): - if not self.did_set_interval: - self.set_interval(**kwargs) + def set_state(self, interval=None, **kwargs): + if not self.did_set_interval or interval: + self.set_interval(interval) + # Without this we will not have to wait long until receiving bug “I + # opened vim, but branch information is only shown after I move cursor”. + if self.update_first: + self.update_first = False + self.update() def startup(self, **kwargs): - # Normally .update() succeeds to run before value is requested, meaning - # that user is getting values he needs directly at vim startup. Without - # .startup() we will not have to wait long until receiving bug “I opened - # vim, but branch information is only shown after I move cursor”. self.run_once = False self.set_state(**kwargs) @@ -81,11 +84,12 @@ def printed(func): class KwThreadedSegment(ThreadedSegment): drop_interval = 10 * 60 + update_missing = True + update_first = False def __init__(self): super(KwThreadedSegment, self).__init__() self.queries = {} - self.update_missing = True @staticmethod def key(**kwargs): @@ -96,6 +100,8 @@ class KwThreadedSegment(ThreadedSegment): try: update_state = self.queries[key][1] except KeyError: + # self.update_missing has the same reasoning as self.update_first in + # parent class update_state = self.compute_state(key) if self.update_missing else None # No locks: render method is already running with write_lock acquired. self.queries[key] = (monotonic(), update_state) @@ -114,12 +120,13 @@ class KwThreadedSegment(ThreadedSegment): for key in removes: self.queries.pop(key) - def set_state(self, **kwargs): - if not self.did_set_interval or ('interval' in kwargs and self.interval > kwargs['interval']): - self.set_interval(**kwargs) + def set_state(self, interval=None, **kwargs): + if not self.did_set_interval or (interval < self.interval): + self.set_interval(interval) key = self.key(**kwargs) - self.queries[key] = (monotonic(), None) + if key not in self.queries: + self.queries[key] = (monotonic(), self.compute_state(key) if self.update_missing else None) @staticmethod def render_one(update_state, **kwargs): diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 2cc3254c..11334837 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -174,8 +174,8 @@ def _external_ip(query_url='http://ipv4.icanhazip.com/'): class ExternalIpSegment(ThreadedSegment): def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs): - super(ExternalIpSegment, self).set_state(**kwargs) self.query_url = query_url + super(ExternalIpSegment, self).set_state(**kwargs) def update(self): ip = _external_ip(query_url=self.query_url) @@ -297,10 +297,10 @@ class WeatherSegment(ThreadedSegment): interval = 600 def set_state(self, location_query=None, **kwargs): - super(WeatherSegment, self).set_state(**kwargs) self.location = location_query self.url = None self.condition = {} + super(WeatherSegment, self).set_state(**kwargs) def update(self): import json From fd6c1f1e13fa0814bb44ee299358fa3e12ce694e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 18 Mar 2013 22:13:20 +0530 Subject: [PATCH 0504/1472] Avoid divisin by zero exception in the network load segment --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 11334837..dfa1a3ae 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -583,7 +583,7 @@ class NetworkLoadSegment(KwThreadedSegment): t2, b2 = idata['last'] measure_interval = t2 - t1 - if None in (b1, b2): + if None in (b1, b2) or measure_interval == 0: return None return [{ From 1dc039a3b0b4b4efeaebbdd55cdfeb0cf8e8f06a Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 00:29:41 +0400 Subject: [PATCH 0505/1472] Some small changes in .segments.vim --- powerline/segments/vim.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index e2df1896..f0ae931f 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -301,6 +301,7 @@ def modified_buffers(text='+ ', join_str=','): class KwWindowThreadedSegment(KwThreadedSegment): def set_state(self, **kwargs): + kwargs = kwargs.copy() for window in vim.windows: buffer = window.buffer kwargs['segment_info'] = {'bufnr': buffer.number, 'buffer': buffer} @@ -372,9 +373,9 @@ class BranchSegment(RepositorySegment): 'divider_highlight_group': 'branch:divider', }] - def startup(self, **kwargs): + def startup(self, status_colors=False, **kwargs): super(BranchSegment, self).startup() - if kwargs.get('status_colors', False): + if status_colors: self.started_repository_status = True repository_status.startup() From 260a0dafcb640dc4c337ef35a06c04a0ac64b1f1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 00:42:32 +0400 Subject: [PATCH 0506/1472] Add tests for the constantly recreated *Powerline instances --- tests/test_configuration.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index c3739493..c1df253a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -15,6 +15,17 @@ VBLOCK = chr(ord('V') - 0x40) SBLOCK = chr(ord('S') - 0x40) +def shutdown(powerline): + from powerline.segments import common, vim + try: + powerline.renderer.shutdown() + finally: + # After shutdown threads are useless, it is needed to recreate them. + from imp import reload + reload(common) + reload(vim) + + class TestConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline @@ -49,7 +60,7 @@ class TestConfig(TestCase): check_output(1, 0) finally: vim_module._start_mode('n') - powerline.renderer.shutdown() + shutdown(powerline) def test_tmux(self): from powerline.segments import common @@ -57,16 +68,27 @@ class TestConfig(TestCase): reload(common) from powerline.shell import ShellPowerline with replace_module_attr(common, 'urllib_read', urllib_read): - ShellPowerline(Args(ext=['tmux']), run_once=True).renderer.render() - reload(common) + powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) + powerline.renderer.render() + powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) + powerline.renderer.render() + shutdown(powerline) def test_zsh(self): from powerline.shell import ShellPowerline - ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=True).renderer.render() + powerline = ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=False) + powerline.renderer.render() + powerline = ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=False) + powerline.renderer.render() + shutdown(powerline) def test_bash(self): from powerline.shell import ShellPowerline - ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=True).renderer.render() + powerline = ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=False) + powerline.renderer.render() + powerline = ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=False) + powerline.renderer.render() + shutdown(powerline) def test_ipython(self): from powerline.ipython import IpythonPowerline @@ -78,7 +100,8 @@ class TestConfig(TestCase): powerline = IpyPowerline() powerline.renderer.render() - powerline.renderer.shutdown() + powerline.renderer.render() + shutdown(powerline) def test_wm(self): from powerline.segments import common From 1a4eeadbeedd5aa1f5fd2aad0ad6ddf34812fdf8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Mar 2013 00:31:00 +0400 Subject: [PATCH 0507/1472] Added green_yellow_orange_red gradient for line % in solarized Ref #301 --- powerline/config_files/colors.json | 4 ++++ powerline/config_files/colorschemes/vim/solarized.json | 1 + powerline/segments/vim.py | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 5975572c..d979ffac 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -77,6 +77,10 @@ "green_yellow_red": [ [190, 184, 178, 172, 166, 160], ["8ae71c", "8ce71c", "8fe71c", "92e71c", "95e71d", "98e71d", "9ae71d", "9de71d", "a0e71e", "a3e71e", "a6e71e", "a8e71e", "abe71f", "aee71f", "b1e71f", "b4e71f", "b6e720", "b9e720", "bce720", "bfe720", "c2e821", "c3e721", "c5e621", "c7e521", "c9e522", "cbe422", "cde322", "cfe222", "d1e223", "d3e123", "d5e023", "d7df23", "d9df24", "dbde24", "dddd24", "dfdc24", "e1dc25", "e3db25", "e5da25", "e7d925", "e9d926", "e9d626", "e9d426", "e9d126", "e9cf27", "e9cc27", "e9ca27", "e9c727", "e9c528", "e9c228", "e9c028", "e9bd28", "e9bb29", "e9b829", "e9b629", "e9b329", "e9b12a", "e9ae2a", "e9ac2a", "e9a92a", "eaa72b", "eaa42b", "eaa22b", "ea9f2b", "ea9d2c", "ea9b2c", "ea982c", "ea962c", "ea942d", "ea912d", "ea8f2d", "ea8d2d", "ea8a2e", "ea882e", "ea862e", "ea832e", "ea812f", "ea7f2f", "ea7c2f", "ea7a2f", "eb7830", "eb7530", "eb7330", "eb7130", "eb6f31", "eb6c31", "eb6a31", "eb6831", "eb6632", "eb6332", "eb6132", "eb5f32", "eb5d33", "eb5a33", "eb5833", "eb5633", "eb5434", "eb5134", "eb4f34", "eb4d34", "ec4b35"] + ], + "green_yellow_orange_red": [ + [70, 106, 100, 136, 130, 166], + ["719e07", "739d06", "759c06", "779c06", "799b06", "7b9a05", "7d9a05", "7f9905", "819805", "839805", "859704", "879704", "899604", "8b9504", "8d9504", "8f9403", "919303", "949303", "969203", "989102", "9a9102", "9c9002", "9e9002", "a08f02", "a28e01", "a48e01", "a68d01", "a88c01", "aa8c01", "ac8b00", "ae8a00", "b08a00", "b28900", "b58900", "b58700", "b68501", "b78302", "b78102", "b87f03", "b97d04", "b97b04", "ba7905", "bb7806", "bb7606", "bc7407", "bd7208", "bd7008", "be6e09", "bf6c0a", "bf6a0a", "c0690b", "c1670c", "c1650c", "c2630d", "c3610e", "c35f0e", "c45d0f", "c55b10", "c55a10", "c65811", "c75612", "c75412", "c85213", "c95014", "c94e14", "ca4c15", "cb4b16", "cb4a16", "cc4917", "cc4818", "cd4719", "cd4719", "ce461a", "ce451b", "cf441c", "cf441c", "d0431d", "d0421e", "d1411f", "d1411f", "d24020", "d23f21", "d33e22", "d33e22", "d43d23", "d43c24", "d53b25", "d53b25", "d63a26", "d63927", "d73828", "d73828", "d83729", "d8362a", "d9352b", "d9352b", "da342c", "da332d", "db322e", "dc322f"] ] } } diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 9a55a237..5fed1c91 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -23,6 +23,7 @@ "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper" }, "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper" }, "line_percent": { "fg": "oldlace", "bg": "lightskyblue4" }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, "col_current": { "fg": "azure4", "bg": "lightyellow" } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f0ae931f..f632c798 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -247,7 +247,7 @@ def line_percent(segment_info, gradient=False): :param bool gradient: highlight the percentage with a color gradient (by default a green to red gradient) - Highlight groups used: ``line_percent_gradient`` (gradient) or ``line_percent``. + Highlight groups used: ``line_percent_gradient`` (gradient), ``line_percent``. ''' line_current = segment_info['window'].cursor[0] line_last = len(segment_info['buffer']) From 3e4a98b174aa689293b208779b34988038faeccf Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Mar 2013 07:40:39 +0400 Subject: [PATCH 0508/1472] Make green_yellow_orange_red grad use <16 colors, added 2 more grads They are currently unused --- powerline/config_files/colors.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index d979ffac..ca0b7a71 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -79,8 +79,16 @@ ["8ae71c", "8ce71c", "8fe71c", "92e71c", "95e71d", "98e71d", "9ae71d", "9de71d", "a0e71e", "a3e71e", "a6e71e", "a8e71e", "abe71f", "aee71f", "b1e71f", "b4e71f", "b6e720", "b9e720", "bce720", "bfe720", "c2e821", "c3e721", "c5e621", "c7e521", "c9e522", "cbe422", "cde322", "cfe222", "d1e223", "d3e123", "d5e023", "d7df23", "d9df24", "dbde24", "dddd24", "dfdc24", "e1dc25", "e3db25", "e5da25", "e7d925", "e9d926", "e9d626", "e9d426", "e9d126", "e9cf27", "e9cc27", "e9ca27", "e9c727", "e9c528", "e9c228", "e9c028", "e9bd28", "e9bb29", "e9b829", "e9b629", "e9b329", "e9b12a", "e9ae2a", "e9ac2a", "e9a92a", "eaa72b", "eaa42b", "eaa22b", "ea9f2b", "ea9d2c", "ea9b2c", "ea982c", "ea962c", "ea942d", "ea912d", "ea8f2d", "ea8d2d", "ea8a2e", "ea882e", "ea862e", "ea832e", "ea812f", "ea7f2f", "ea7c2f", "ea7a2f", "eb7830", "eb7530", "eb7330", "eb7130", "eb6f31", "eb6c31", "eb6a31", "eb6831", "eb6632", "eb6332", "eb6132", "eb5f32", "eb5d33", "eb5a33", "eb5833", "eb5633", "eb5434", "eb5134", "eb4f34", "eb4d34", "ec4b35"] ], "green_yellow_orange_red": [ - [70, 106, 100, 136, 130, 166], + [2, 3, 9, 1], ["719e07", "739d06", "759c06", "779c06", "799b06", "7b9a05", "7d9a05", "7f9905", "819805", "839805", "859704", "879704", "899604", "8b9504", "8d9504", "8f9403", "919303", "949303", "969203", "989102", "9a9102", "9c9002", "9e9002", "a08f02", "a28e01", "a48e01", "a68d01", "a88c01", "aa8c01", "ac8b00", "ae8a00", "b08a00", "b28900", "b58900", "b58700", "b68501", "b78302", "b78102", "b87f03", "b97d04", "b97b04", "ba7905", "bb7806", "bb7606", "bc7407", "bd7208", "bd7008", "be6e09", "bf6c0a", "bf6a0a", "c0690b", "c1670c", "c1650c", "c2630d", "c3610e", "c35f0e", "c45d0f", "c55b10", "c55a10", "c65811", "c75612", "c75412", "c85213", "c95014", "c94e14", "ca4c15", "cb4b16", "cb4a16", "cc4917", "cc4818", "cd4719", "cd4719", "ce461a", "ce451b", "cf441c", "cf441c", "d0431d", "d0421e", "d1411f", "d1411f", "d24020", "d23f21", "d33e22", "d33e22", "d43d23", "d43c24", "d53b25", "d53b25", "d63a26", "d63927", "d73828", "d73828", "d83729", "d8362a", "d9352b", "d9352b", "da342c", "da332d", "db322e", "dc322f"] + ], + "yellow_red": [ + [220, 178, 172, 166, 160], + ["ffd700", "fdd500", "fbd300", "fad200", "f8d000", "f7cf00", "f5cd00", "f3cb00", "f2ca00", "f0c800", "efc700", "edc500", "ebc300", "eac200", "e8c000", "e7bf00", "e5bd00", "e3bb00", "e2ba00", "e0b800", "dfb700", "ddb500", "dbb300", "dab200", "d8b000", "d7af00", "d7ad00", "d7ab00", "d7aa00", "d7a800", "d7a700", "d7a500", "d7a300", "d7a200", "d7a000", "d79f00", "d79d00", "d79b00", "d79a00", "d79800", "d79700", "d79500", "d79300", "d79200", "d79000", "d78f00", "d78d00", "d78b00", "d78a00", "d78800", "d78700", "d78500", "d78300", "d78200", "d78000", "d77f00", "d77d00", "d77b00", "d77a00", "d77800", "d77700", "d77500", "d77300", "d77200", "d77000", "d76f00", "d76d00", "d76b00", "d76a00", "d76800", "d76700", "d76500", "d76300", "d76200", "d76000", "d75f00", "d75b00", "d75700", "d75300", "d74f00", "d74c00", "d74800", "d74400", "d74000", "d73c00", "d73900", "d73500", "d73100", "d72d00", "d72900", "d72600", "d72200", "d71e00", "d71a00", "d71600", "d71300", "d70f00", "d70b00", "d70700"] + ], + "yellow_orange_red": [ + [3, 9, 1], + ["b58900", "b58700", "b58600", "b68501", "b68401", "b78202", "b78102", "b88003", "b87f03", "b87d03", "b97c04", "b97b04", "ba7a05", "ba7805", "bb7706", "bb7606", "bc7507", "bc7307", "bc7207", "bd7108", "bd7008", "be6e09", "be6d09", "bf6c0a", "bf6b0a", "c06a0b", "c0680b", "c0670b", "c1660c", "c1650c", "c2630d", "c2620d", "c3610e", "c3600e", "c35e0e", "c45d0f", "c45c0f", "c55b10", "c55910", "c65811", "c65711", "c75612", "c75412", "c75312", "c85213", "c85113", "c94f14", "c94e14", "ca4d15", "ca4c15", "cb4b16", "cb4a16", "cb4a17", "cc4917", "cc4918", "cc4818", "cd4819", "cd4719", "cd471a", "ce461a", "ce461b", "ce451b", "cf451c", "cf441c", "cf441d", "d0431d", "d0431e", "d0421e", "d1421f", "d1411f", "d14120", "d24020", "d24021", "d23f21", "d33f22", "d33e22", "d33e23", "d43d23", "d43d24", "d43c24", "d53c25", "d53b25", "d53b26", "d63a26", "d63a27", "d63927", "d73928", "d73828", "d73829", "d83729", "d8372a", "d8362a", "d9362b", "d9352b", "d9352c", "da342c", "da342d", "da332d", "db332e"] ] } } From 11aa74c634bfece1ccabdd5856a8cc8c36a7a6f2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 08:11:53 +0400 Subject: [PATCH 0509/1472] Make it use gradient for weather temperature Ref #301 --- powerline/config_files/colors.json | 4 ++++ .../config_files/colorschemes/tmux/default.json | 3 +-- powerline/config_files/colorschemes/wm/default.json | 3 +-- powerline/segments/common.py | 11 +++++++++-- tests/test_segments.py | 12 ++++++------ 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index ca0b7a71..3bbb90f2 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -89,6 +89,10 @@ "yellow_orange_red": [ [3, 9, 1], ["b58900", "b58700", "b58600", "b68501", "b68401", "b78202", "b78102", "b88003", "b87f03", "b87d03", "b97c04", "b97b04", "ba7a05", "ba7805", "bb7706", "bb7606", "bc7507", "bc7307", "bc7207", "bd7108", "bd7008", "be6e09", "be6d09", "bf6c0a", "bf6b0a", "c06a0b", "c0680b", "c0670b", "c1660c", "c1650c", "c2630d", "c2620d", "c3610e", "c3600e", "c35e0e", "c45d0f", "c45c0f", "c55b10", "c55910", "c65811", "c65711", "c75612", "c75412", "c75312", "c85213", "c85113", "c94f14", "c94e14", "ca4d15", "ca4c15", "cb4b16", "cb4a16", "cb4a17", "cc4917", "cc4918", "cc4818", "cd4819", "cd4719", "cd471a", "ce461a", "ce461b", "ce451b", "cf451c", "cf441c", "cf441d", "d0431d", "d0431e", "d0421e", "d1421f", "d1411f", "d14120", "d24020", "d24021", "d23f21", "d33f22", "d33e22", "d33e23", "d43d23", "d43d24", "d43c24", "d53c25", "d53b25", "d53b26", "d63a26", "d63a27", "d63927", "d73928", "d73828", "d73829", "d83729", "d8372a", "d8362a", "d9362b", "d9352b", "d9352c", "da342c", "da342d", "da332d", "db332e"] + ], + "blue_red": [ + [39, 74, 68, 67, 103, 97, 96, 132, 131, 167, 203, 197], + ["19b4fe", "1bb2fc", "1db1fa", "1faff8", "22aef6", "24adf4", "26abf2", "29aaf0", "2ba9ee", "2da7ec", "30a6ea", "32a5e8", "34a3e6", "36a2e4", "39a0e2", "3b9fe1", "3d9edf", "409cdd", "429bdb", "449ad9", "4798d7", "4997d5", "4b96d3", "4d94d1", "5093cf", "5292cd", "5490cb", "578fc9", "598dc7", "5b8cc6", "5e8bc4", "6089c2", "6288c0", "6487be", "6785bc", "6984ba", "6b83b8", "6e81b6", "7080b4", "727eb2", "757db0", "777cae", "797aac", "7b79ab", "7e78a9", "8076a7", "8275a5", "8574a3", "8772a1", "89719f", "8c709d", "8e6e9b", "906d99", "926b97", "956a95", "976993", "996791", "9c668f", "9e658e", "a0638c", "a3628a", "a56188", "a75f86", "a95e84", "ac5c82", "ae5b80", "b05a7e", "b3587c", "b5577a", "b75678", "ba5476", "bc5374", "be5273", "c05071", "c34f6f", "c54e6d", "c74c6b", "ca4b69", "cc4967", "ce4865", "d14763", "d34561", "d5445f", "d7435d", "da415b", "dc4059", "de3f58", "e13d56", "e33c54", "e53a52", "e83950", "ea384e", "ec364c", "ee354a", "f13448", "f33246", "f53144", "f83042", "fa2e40"] ] } } diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 67be9993..8b7ba787 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -9,8 +9,7 @@ "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_cold": { "fg": "steelblue", "bg": "gray0" }, - "weather_temp_hot": { "fg": "darkorange3", "bg": "gray0" }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json index 90bd6cc4..ab1ee74b 100644 --- a/powerline/config_files/colorschemes/wm/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -9,8 +9,7 @@ "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_cold": { "fg": "steelblue", "bg": "gray0" }, - "weather_temp_hot": { "fg": "darkorange3", "bg": "gray0" }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index dfa1a3ae..c1d89f6e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -356,6 +356,12 @@ class WeatherSegment(ThreadedSegment): temperature_format = temperature_format or ('{temp:.0f}' + temp_units[unit]) temp = temp_conversions[unit](self.temp) + if self.temp < -30: + gradient_level = 0 + elif self.temp > 40: + gradient_level = 100 + else: + gradient_level = int((self.temp + 30) * 100 // 70) groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather'] return [ { @@ -365,9 +371,10 @@ class WeatherSegment(ThreadedSegment): }, { 'contents': temperature_format.format(temp=temp), - 'highlight_group': ['weather_temp_cold' if int(self.temp) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'], + 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', + 'gradient_level': gradient_level, }, ] @@ -393,7 +400,7 @@ weather conditions. Divider highlight group used: ``background:divider``. -Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``. +Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_gradient`` (gradient) or ``weather``. Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. ''') diff --git a/tests/test_segments.py b/tests/test_segments.py index 6eedcbae..c5f3e1c0 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -143,27 +143,27 @@ class TestCommon(TestCase): with replace_module_attr(common, 'urllib_read', urllib_read): self.assertEqual(common.weather(), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-11°C'} ]) self.assertEqual(common.weather(icons={'cloudy': 'o'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-11°C'} ]) self.assertEqual(common.weather(icons={'partly_cloudy_day': 'x'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-11°C'} ]) self.assertEqual(common.weather(unit='F'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '12°F'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '12°F'} ]) self.assertEqual(common.weather(unit='K'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '262K'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '262K'} ]) self.assertEqual(common.weather(temperature_format='{temp:.1e}C'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-1.1e+01C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-1.1e+01C'} ]) def test_system_load(self): From da8647895a26d93ba34c2bcfbd74330b466f08a8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 08:17:11 +0400 Subject: [PATCH 0510/1472] Make generate_gradients tool be able to show custom scale --- tools/generate_gradients.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 46f418ca..966863a5 100644 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -6,7 +6,7 @@ from itertools import groupby if len(sys.argv) == 1: sys.stderr.write(''' - Usage: generate_gradients.py colors itemnum[ "show"] + Usage: generate_gradients.py colors itemnum[ "show" [ min max num]] colors: JSON list with either cterm ([200, 42, 6]) or RGB (["abcdef", "feffef"]) colors. @@ -15,6 +15,14 @@ if len(sys.argv) == 1: "show": static string, determines whether gradient sample should be printed to stdout as well. + + min, max: floating point values. + num: integer + + all of the above are used to generate sample gradient for given + range (just like the above gradients shown with "show", but with + different scale (controlled by min and max) and, possibly, + different length (controlled by num)). ''') @@ -73,9 +81,9 @@ def print_color(color): sys.stdout.write('\033[48;' + colstr + 'm ') -def print_colors(colors): - for i in range(101): - color = colors[int(round(i * (len(colors) - 1) / 100))] +def print_colors(colors, num=100): + for i in range(num + 1): + color = colors[int(round(i * (len(colors) - 1) / num))] print_color(color) sys.stdout.write('\033[0m\n') @@ -90,9 +98,9 @@ gradient = [gr_func(y) for y in range(0, m - 1)] r = [get_rgb(*color) for color in gradient] r2 = [find_color(color, cterm_to_hex)[0] for color in gradient] r3 = [i[0] for i in groupby(r2)] -print json.dumps(r) -print json.dumps(r2) -print json.dumps(r3) +print(json.dumps(r)) +print(json.dumps(r2)) +print(json.dumps(r3)) if len(sys.argv) > 3 and sys.argv[3] == 'show': print_colors(gradient) print_colors(r2) @@ -103,3 +111,14 @@ if len(sys.argv) > 3 and sys.argv[3] == 'show': nums = (''.join((str(i) for i in range(10)))) sys.stdout.write(''.join(((('\033[1m' if j % 2 else '\033[0m') + nums) for j in range(10)))) sys.stdout.write('\033[0m0\n') + if len(sys.argv) > 6: + vmin = float(sys.argv[4]) + vmax = float(sys.argv[5]) + num = int(sys.argv[6]) + print_colors(gradient, num) + s = '' + while len(s) < num: + curpc = len(s) + 1 if s else 0 + curval = vmin + curpc * (vmax - vmin) / 100.0 + s += str(curval) + ' ' + print(s) From 910dc3e69df4ffae41db2807893edcf5b30bf8df Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 08:20:04 +0400 Subject: [PATCH 0511/1472] Made tools/generate_gradients.py work in python3 --- tools/generate_gradients.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 966863a5..8e9a78a7 100644 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -3,6 +3,11 @@ import json from powerline.colorscheme import cterm_to_hex from itertools import groupby +try: + from __builtin__ import unicode +except ImportError: + unicode = str + if len(sys.argv) == 1: sys.stderr.write(''' @@ -77,7 +82,7 @@ def print_color(color): if type(color) is int: colstr = '5;' + str(color) else: - colstr = '2;' + ';'.join((str(i) for i in color)) + colstr = '2;' + ';'.join((str(int(round(i))) for i in color)) sys.stdout.write('\033[48;' + colstr + 'm ') From c92dfae5dc4b964219bfef8a49f61501add1f022 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Mar 2013 08:20:40 +0400 Subject: [PATCH 0512/1472] Made tools/*.py executable --- tools/colors_find.py | 1 + tools/generate_gradients.py | 1 + 2 files changed, 2 insertions(+) mode change 100644 => 100755 tools/colors_find.py mode change 100644 => 100755 tools/generate_gradients.py diff --git a/tools/colors_find.py b/tools/colors_find.py old mode 100644 new mode 100755 index d93bf455..6ba1489d --- a/tools/colors_find.py +++ b/tools/colors_find.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python import sys import os diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py old mode 100644 new mode 100755 index 8e9a78a7..b2915afc --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python import sys import json from powerline.colorscheme import cterm_to_hex From 0610316e2551c3fc9e2f11fee73e85c69f7e75c0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Mar 2013 07:52:54 +0400 Subject: [PATCH 0513/1472] Make prompt* sets non-local Fixes #342 --- powerline/bindings/zsh/powerline.zsh | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 04360269..0b62aae6 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -21,6 +21,7 @@ _powerline_install_precmd() { fi done chpwd_functions+=( _powerline_tmux_set_pwd ) + setopt nolocaloptions setopt promptpercent setopt promptsubst if zmodload zsh/zpython &>/dev/null ; then From b951907912df463bf727de826dc87365c96cfe6d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 21 Mar 2013 17:24:09 +0100 Subject: [PATCH 0514/1472] Autodetect network interfaces to monitor --- powerline/lib/threaded.py | 7 ++++-- powerline/segments/common.py | 45 ++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index b9841cec..8d8ff5d4 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -67,11 +67,14 @@ class ThreadedSegment(Thread): self.update() def startup(self, **kwargs): + # Normally .update() succeeds to run before value is requested, meaning + # that user is getting values he needs directly at vim startup. Without + # .startup() we will not have to wait long until receiving bug “I opened + # vim, but branch information is only shown after I move cursor”. self.run_once = False - self.set_state(**kwargs) - if not self.is_alive(): + self.set_state(**kwargs) self.start() diff --git a/powerline/segments/common.py b/powerline/segments/common.py index dfa1a3ae..551a1dd4 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -452,6 +452,12 @@ try: return None return if_io.bytes_recv, if_io.bytes_sent + def _get_interfaces(): + io_counters = psutil.network_io_counters(pernic=True) + for interface, data in io_counters.items(): + if data: + yield interface, data.bytes_recv, data.bytes_sent + def _get_user(): return psutil.Process(os.getpid()).username @@ -476,6 +482,12 @@ except ImportError: except IOError: return None + def _get_interfaces(): + for interface in os.listdir('/sys/class/net'): + x = _get_bytes(interface) + if x is not None: + yield interface, x[0], x[1] + def _get_user(): # NOQA return os.environ.get('USER', None) @@ -552,13 +564,42 @@ def uptime(format='{days}d {hours:02d}h {minutes:02d}m'): class NetworkLoadSegment(KwThreadedSegment): + import re interfaces = {} + replace_num_pat = re.compile(r'[a-zA-Z]+') @staticmethod - def key(interface='eth0', **kwargs): + def key(interface='detect', **kwargs): return interface def compute_state(self, interface): + if interface == 'detect': + proc_exists = getattr(self, 'proc_exists', None) + if proc_exists is None: + proc_exists = self.proc_exists = os.path.exists('/proc/net/route') + if proc_exists: + # Look for default interface in routing table + with open('/proc/net/route', 'rb') as f: + for line in f.readlines(): + parts = line.split() + if len(parts) > 1: + iface, destination = parts[:2] + if not destination.replace(b'0', b''): + interface = iface.decode('utf-8') + break + if interface == 'detect': + # Choose interface with most total activity, excluding some + # well known interface names + interface, total = 'eth0', -1 + for name, rx, tx in _get_interfaces(): + base = self.replace_num_pat.match(name) + if None in (base, rx, tx) or base.group() in ('lo', 'vmnet', 'sit'): + continue + activity = rx + tx + if activity > total: + total = activity + interface = name + if interface in self.interfaces: idata = self.interfaces[interface] try: @@ -603,7 +644,7 @@ falls back to reading :file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`. :param str interface: - network interface to measure + network interface to measure (use the special value "detect" to have powerline try to auto-detect the network interface) :param str suffix: string appended to each load string :param bool si_prefix: From 157b849d33829fb878ec09c468557192779782fe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 00:55:54 +0400 Subject: [PATCH 0515/1472] Make weather gradient configurable, add system_load gradient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also make gradients floating-point and fix #332 by removing “normalized” from system_load segment documentation. --- .../colorschemes/tmux/default.json | 4 +- .../colorschemes/vim/solarized.json | 1 + powerline/segments/common.py | 45 ++++++++++++------- powerline/segments/vim.py | 6 +-- tests/lib/__init__.py | 2 +- tests/test_segments.py | 40 ++++++++++------- tools/generate_gradients.py | 3 +- 7 files changed, 62 insertions(+), 39 deletions(-) diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 8b7ba787..a23ccdb9 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -17,8 +17,6 @@ "external_ip": { "fg": "gray8", "bg": "gray0" }, "network_load": { "fg": "gray8", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_good": { "fg": "lightyellowgreen", "bg": "gray0" }, - "system_load_bad": { "fg": "gold3", "bg": "gray0" }, - "system_load_ugly": { "fg": "orangered", "bg": "gray0" } + "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" } } } diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 5fed1c91..2eb4c36d 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -61,6 +61,7 @@ "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow" }, "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, "line_percent": { "fg": "oldlace", "bg": "gray61" }, + "line_percent_gradient": { "fg": "oldlace", "bg": "gray61" }, "line_current": { "fg": "gray13", "bg": "oldlace", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray13", "bg": "oldlace" }, "col_current": { "fg": "azure4", "bg": "oldlace" } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c1d89f6e..f0dc37e7 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -342,7 +342,7 @@ class WeatherSegment(ThreadedSegment): self.temp = temp self.icon_names = icon_names - def render(self, icons=None, unit='C', temperature_format=None, **kwargs): + def render(self, icons=None, unit='C', temp_format=None, temp_coldest=-30, temp_hottest=40, **kwargs): if not hasattr(self, 'icon_names'): return None @@ -354,14 +354,14 @@ class WeatherSegment(ThreadedSegment): else: icon = weather_conditions_icons[self.icon_names[-1]] - temperature_format = temperature_format or ('{temp:.0f}' + temp_units[unit]) + temp_format = temp_format or ('{temp:.0f}' + temp_units[unit]) temp = temp_conversions[unit](self.temp) - if self.temp < -30: + if self.temp <= temp_coldest: gradient_level = 0 - elif self.temp > 40: + elif self.temp >= temp_hottest: gradient_level = 100 else: - gradient_level = int((self.temp + 30) * 100 // 70) + gradient_level = (self.temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest) groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather'] return [ { @@ -370,7 +370,7 @@ class WeatherSegment(ThreadedSegment): 'divider_highlight_group': 'background:divider', }, { - 'contents': temperature_format.format(temp=temp), + 'contents': temp_format.format(temp=temp), 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', @@ -395,8 +395,16 @@ weather conditions. location query for your current location, e.g. ``oslo, norway`` :param dict icons: dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` -:param str temperature_format: +:param str temp_format: format string, receives ``temp`` as an argument. Should also hold unit. +:param float temp_coldest: + coldest temperature. Any temperature below it will have gradient level equal + to zero. +:param float temp_hottest: + hottest temperature. Any temperature above it will have gradient level equal + to 100. Temperatures between ``temp_coldest`` and ``temp_hottest`` receive + gradient level that indicates relative position in this interval + (``100 * (cur-coldest) / (hottest-coldest)``). Divider highlight group used: ``background:divider``. @@ -406,7 +414,7 @@ Also uses ``weather_conditions_{condition}`` for all weather conditions supporte def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): - '''Return normalized system load average. + '''Return system load average. Highlights using ``system_load_good``, ``system_load_bad`` and ``system_load_ugly`` highlighting groups, depending on the thresholds @@ -415,13 +423,19 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): :param str format: format string, receives ``avg`` as an argument :param float threshold_good: - threshold for "good load" highlighting + threshold for gradient level 0: any normalized load average below this + value will have this gradient level. :param float threshold_bad: - threshold for "bad load" highlighting + threshold for gradient level 100: any normalized load average above this + value will have this gradient level. Load averages between + ``threshold_good`` and ``threshold_bad`` receive gradient level that + indicates relative position in this interval: + (``100 * (cur-good) / (bad-good)``). + Note: both parameters are checked against normalized load averages. Divider highlight group used: ``background:divider``. - Highlight groups used: ``system_load_good`` or ``system_load``, ``system_load_bad`` or ``system_load``, ``system_load_ugly`` or ``system_load``. It is recommended to define all highlight groups. + Highlight groups used: ``system_load_gradient`` (gradient) or ``system_load``. ''' global cpu_count try: @@ -432,16 +446,17 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): for avg in os.getloadavg(): normalized = avg / cpu_num if normalized < threshold_good: - hl = 'system_load_good' + gradient_level = 0 elif normalized < threshold_bad: - hl = 'system_load_bad' + gradient_level = (normalized - threshold_good) * 100.0 / (threshold_bad - threshold_good) else: - hl = 'system_load_ugly' + gradient_level = 100 ret.append({ 'contents': format.format(avg=avg), - 'highlight_group': [hl, 'system_load'], + 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', + 'gradient_level': gradient_level, }) ret[0]['draw_divider'] = True ret[0]['contents'] += ' ' diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f632c798..1ceca510 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -251,11 +251,11 @@ def line_percent(segment_info, gradient=False): ''' line_current = segment_info['window'].cursor[0] line_last = len(segment_info['buffer']) - percentage = int(line_current * 100 // line_last) + percentage = line_current * 100.0 / line_last if not gradient: - return str(percentage) + return str(int(round(percentage))) return [{ - 'contents': str(percentage), + 'contents': str(int(round(percentage))), 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': percentage, }] diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index fab70e9b..8d72fb4e 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -24,7 +24,7 @@ def urllib_read(query_url): elif query_url.startswith('http://freegeoip.net/json/'): return '{"city": "Meppen", "region_code": "06", "region_name": "Niedersachsen", "areacode": "", "ip": "82.145.55.16", "zipcode": "49716", "longitude": 7.3167, "country_name": "Germany", "country_code": "DE", "metrocode": "", "latitude": 52.6833}' elif query_url.startswith('http://query.yahooapis.com/v1/public/'): - return r'{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"
\nCurrent Conditions:
\nPartly Cloudy, -11 C
\n
Forecast:
\nSat - Partly Cloudy. High: -9 Low: -19
\nSun - Partly Cloudy. High: -12 Low: -18
\n
\nFull Forecast at Yahoo! Weather

\n(provided by The Weather Channel)
","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}' + return r'{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-9","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-9","text":"Partly Cloudy"},"description":"
\nCurrent Conditions:
\nPartly Cloudy, -9 C
\n
Forecast:
\nSat - Partly Cloudy. High: -9 Low: -19
\nSun - Partly Cloudy. High: -12 Low: -18
\n
\nFull Forecast at Yahoo! Weather

\n(provided by The Weather Channel)
","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}' else: raise NotImplementedError diff --git a/tests/test_segments.py b/tests/test_segments.py index c5f3e1c0..ea739aac 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -143,40 +143,48 @@ class TestCommon(TestCase): with replace_module_attr(common, 'urllib_read', urllib_read): self.assertEqual(common.weather(), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-11°C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + ]) + self.assertEqual(common.weather(temp_coldest=0, temp_hottest=100), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} + ]) + self.assertEqual(common.weather(temp_coldest=-100, temp_hottest=-50), [ + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} ]) self.assertEqual(common.weather(icons={'cloudy': 'o'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-11°C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(icons={'partly_cloudy_day': 'x'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-11°C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(unit='F'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '12°F'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(unit='K'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '262K'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} ]) - self.assertEqual(common.weather(temperature_format='{temp:.1e}C'), [ + self.assertEqual(common.weather(temp_format='{temp:.1e}C'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-1.1e+01C'} + {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} ]) def test_system_load(self): with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): with replace_module_attr(common, 'cpu_count', lambda: 2): self.assertEqual(common.system_load(), - [{'contents': '7.5 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, - {'contents': '3.5 ', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, - {'contents': '1.5', 'highlight_group': ['system_load_good', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) + [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, + {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) self.assertEqual(common.system_load(format='{avg:.0f}', threshold_good=0, threshold_bad=1), - [{'contents': '8 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider'}, - {'contents': '4 ', 'highlight_group': ['system_load_ugly', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}, - {'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}]) + [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) def test_cpu_load_percent(self): with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3): @@ -306,11 +314,11 @@ class TestVim(TestCase): segment_info = vim_module._get_segment_info() segment_info['buffer'][0:-1] = [str(i) for i in range(100)] try: - self.assertEqual(vim.line_percent(segment_info=segment_info), '0') + self.assertEqual(vim.line_percent(segment_info=segment_info), '1') vim_module._set_cursor(50, 0) - self.assertEqual(vim.line_percent(segment_info=segment_info), '49') + self.assertEqual(vim.line_percent(segment_info=segment_info), '50') self.assertEqual(vim.line_percent(segment_info=segment_info, gradient=True), - [{'contents': '49', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 49}]) + [{'contents': '50', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 50 * 100.0 / 101}]) finally: vim_module._bw(segment_info['bufnr']) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index b2915afc..1fd4a137 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -10,7 +10,7 @@ except ImportError: unicode = str -if len(sys.argv) == 1: +if len(sys.argv) == 1 or sys.argv[1] == '--help': sys.stderr.write(''' Usage: generate_gradients.py colors itemnum[ "show" [ min max num]] @@ -30,6 +30,7 @@ if len(sys.argv) == 1: different scale (controlled by min and max) and, possibly, different length (controlled by num)). ''') + sys.exit(1) def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset): From 37b684dcd32ee1d5cf93e85653d3b987d0e4a37f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 01:19:58 +0400 Subject: [PATCH 0516/1472] Fix checks for gradient groups (it was not forcing gradient colors) --- powerline/lint/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6267e2f9..f7f3c451 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -687,7 +687,7 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): for colorscheme, cconfig in data['colorscheme_configs'][ext].items(): if hl_group not in cconfig.get('groups', {}): r.append(colorscheme) - elif not allow_gradients: + elif not allow_gradients or allow_gradients == 'force': group_config = cconfig['groups'][hl_group] hadgradient = False for ckey in ('fg', 'bg'): @@ -704,14 +704,14 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): hadgradient = True if allow_gradients is False and not hascolor and hasgradient: echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)), - context_mark=hl_group.mark, + context_mark=getattr(hl_group, 'mark', None), problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), problem_mark=color.mark) r.append(colorscheme) continue if allow_gradients == 'force' and not hadgradient: echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)), - context_mark=hl_group.mark, + context_mark=getattr(hl_group, 'mark', None), problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), problem_mark=group_config.mark) r.append(colorscheme) From 25806fa075cb242ad49f20ab161c8eaecda88ba2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 01:33:56 +0400 Subject: [PATCH 0517/1472] Added gradient for network_load segment --- .../colorschemes/tmux/default.json | 1 + powerline/segments/common.py | 45 +++++++++++++++---- tests/test_segments.py | 29 +++++++++--- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index a23ccdb9..81386ae9 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -16,6 +16,7 @@ "uptime": { "fg": "gray8", "bg": "gray0" }, "external_ip": { "fg": "gray8", "bg": "gray0" }, "network_load": { "fg": "gray8", "bg": "gray0" }, + "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" } } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f0dc37e7..34917af8 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -597,7 +597,7 @@ class NetworkLoadSegment(KwThreadedSegment): idata['last'] = (monotonic(), _get_bytes(interface)) return idata - def render_one(self, idata, format='⬇ {recv:>8} ⬆ {sent:>8}', suffix='B/s', si_prefix=False, **kwargs): + def render_one(self, idata, recv_format='⬇ {value:>8}', sent_format='⬆ {value:>8}', suffix='B/s', si_prefix=False, **kwargs): if not idata or 'prev' not in idata: return None @@ -608,13 +608,28 @@ class NetworkLoadSegment(KwThreadedSegment): if None in (b1, b2) or measure_interval == 0: return None - return [{ - 'contents': format.format( - recv=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix), - sent=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix), - ), + r = [] + for i, key in zip((0, 1), ('recv', 'sent')): + format = locals()[key + '_format'] + value = (b2[i] - b1[i]) / measure_interval + max_key = key + '_max' + is_gradient = max_key in kwargs + hl_groups = ['network_load_' + key, 'network_load'] + if is_gradient: + hl_groups[:0] = (group + '_gradient' for group in hl_groups) + r.append({ + 'contents': format.format(value=humanize_bytes(value, suffix, si_prefix)), 'divider_highlight_group': 'background:divider', - }] + 'highlight_group': hl_groups, + }) + if is_gradient: + max = kwargs[max_key] + if value >= max: + r[-1]['gradient_level'] = 100 + else: + r[-1]['gradient_level'] = value * 100.0 / max + + return r network_load = with_docstring(NetworkLoadSegment(), @@ -630,8 +645,20 @@ falls back to reading string appended to each load string :param bool si_prefix: use SI prefix, e.g. MB instead of MiB -:param str format: - format string, receives ``recv`` and ``sent`` as arguments +:param str recv_format: + format string, receives ``value`` as argument +:param str sent_format: + format string, receives ``value`` as argument +:param float recv_max: + maximum number of received bytes per second. Is only used to compute + gradient level +:param float sent_max: + maximum number of sent bytes per second. Is only used to compute gradient + level + +Divider highlight group used: ``background:divider``. + +Highlight groups used: ``network_load_sent_gradient`` (gradient) or ``network_load_recv_gradient`` (gradient) or ``network_load_gradient`` (gradient), ``network_load_sent`` or ``network_load_recv`` or ``network_load``. ''') diff --git a/tests/test_segments.py b/tests/test_segments.py index ea739aac..b085bfdd 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -209,16 +209,31 @@ class TestCommon(TestCase): common.network_load.sleep(0) common.network_load.sleep(0) self.assertEqual(common.network_load(), [ - {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s ⬆ 2 KiB/s'} + {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(format='r {recv} s {sent}'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s s 2 KiB/s'} + self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(format='r {recv} s {sent}', suffix='bps'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps s 2 Kibps'} + self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', suffix='bps'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(format='r {recv} s {sent}', si_prefix=True), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s s 2 kB/s'} + self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', si_prefix=True), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) + self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', recv_max=0), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) + class ApproxEqual(object): + def __eq__(self, i): + return abs(i - 50.0) < 1 + self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', sent_max=4800), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, ]) def test_virtualenv(self): From 83517f65b98f0c8cfa943e42a08c181ca81a1c9b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 01:42:50 +0400 Subject: [PATCH 0518/1472] Make test.sh always check only powerline/config_files directory --- tests/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.sh b/tests/test.sh index 4c6704f5..a8439ac7 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -7,7 +7,7 @@ for file in tests/test_*.py ; do FAILED=1 fi done -if ! ${PYTHON} scripts/powerline-lint ; then +if ! ${PYTHON} scripts/powerline-lint -p powerline/config_files ; then FAILED=1 fi exit $FAILED From 004e6a8b4646fd6e1ac4070f68a072d49b8be155 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 01:43:37 +0400 Subject: [PATCH 0519/1472] Add gradient to email_imap_alert segment Colors probably need to be revised Fixes #301 --- .../colorschemes/tmux/default.json | 1 + .../config_files/colorschemes/wm/default.json | 1 + powerline/segments/common.py | 25 +++++++++++++++---- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 81386ae9..35686d32 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -7,6 +7,7 @@ "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, "time:divider": { "fg": "gray5", "bg": "gray2" }, "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, + "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json index ab1ee74b..d71d4e3c 100644 --- a/powerline/config_files/colorschemes/wm/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -7,6 +7,7 @@ "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, "time:divider": { "fg": "gray5", "bg": "gray2" }, "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, + "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0" }, "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 34917af8..21a11600 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -693,12 +693,23 @@ class EmailIMAPSegment(KwThreadedSegment): return None except imaplib.IMAP4.error as e: unread_count = str(e) + return unread_count + + @staticmethod + def render_one(unread_count, max_msgs=None, **kwargs): if not unread_count: return None - return [{ - 'highlight_group': 'email_alert', - 'contents': str(unread_count), - }] + elif type(unread_count) != int or not max_msgs: + return [{ + 'contents': str(unread_count), + 'highlight_group': 'email_alert', + }] + else: + return [{ + 'contents': str(unread_count), + 'highlight_group': ['email_alert_gradient', 'email_alert'], + 'gradient_level': unread_count * 100.0 / max_msgs, + }] email_imap_alert = with_docstring(EmailIMAPSegment(), @@ -714,8 +725,12 @@ email_imap_alert = with_docstring(EmailIMAPSegment(), e-mail server port :param str folder: folder to check for e-mails +:param int max_msgs: + Maximum number of messages. If there are more messages then max_msgs then it + will use gradient level equal to 100, otherwise gradient level is equal to + ``100 * msgs_num / max_msgs``. If not present gradient is not computed. -Highlight groups used: ``email_alert``. +Highlight groups used: ``email_alert_gradient`` (gradient), ``email_alert``. ''') From b1f9edab4c9b95ac5002a289138379c470f67987 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 04:09:39 +0400 Subject: [PATCH 0520/1472] Remove get_segment_info and passing segment_info through globals --- powerline/__init__.py | 12 ------------ powerline/renderer.py | 5 +---- powerline/theme.py | 7 +++---- scripts/powerline | 3 ++- tests/test_configuration.py | 18 ++++++++++-------- 5 files changed, 16 insertions(+), 29 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 1a3ac823..2fab620e 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -57,7 +57,6 @@ class Powerline(object): theme_kwargs = { 'ext': ext, 'common_config': common_config, - 'segment_info': self.get_segment_info(), 'run_once': run_once, } local_themes = self.get_local_themes(ext_config.get('local_themes')) @@ -143,14 +142,3 @@ class Powerline(object): ``__init__`` arguments, refer to its documentation. ''' return None - - @staticmethod - def get_segment_info(): - '''Get information for segments that require ``segment_info`` argument. - To be overridden in subclasses. - - :return: - anything accepted by segments as ``segment_info`` argument. Depends - on the segments used, refer to Powerline subclass documentation. - ''' - return None diff --git a/powerline/renderer.py b/powerline/renderer.py index 074c3380..4fdcff72 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -61,10 +61,7 @@ class Renderer(object): reached. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side) - - if segment_info: - theme.segment_info.update(segment_info) + segments = theme.get_segments(side, segment_info) # Handle excluded/included segments for the current mode segments = [self.get_highlighting(segment, mode) for segment in segments diff --git a/powerline/theme.py b/powerline/theme.py index 02647c83..9b84ba5f 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -24,7 +24,7 @@ def requires_segment_info(func): class Theme(object): - def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None, run_once=False): + def __init__(self, ext, theme_config, common_config, top_theme_config=None, run_once=False): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = { @@ -35,7 +35,6 @@ class Theme(object): 'contents': None, 'highlight': {'fg': False, 'bg': False, 'attr': 0} } - self.segment_info = segment_info theme_configs = [theme_config] if top_theme_config: theme_configs.append(top_theme_config) @@ -62,7 +61,7 @@ class Theme(object): def get_spaces(self): return self.spaces - def get_segments(self, side=None): + def get_segments(self, side=None, segment_info=None): '''Return all segments. Function segments are called, and all segments get their before/after @@ -74,7 +73,7 @@ class Theme(object): if segment['type'] == 'function': if (hasattr(segment['contents_func'], 'powerline_requires_segment_info') and segment['contents_func'].powerline_requires_segment_info): - contents = segment['contents_func'](segment_info=self.segment_info, **segment['args']) + contents = segment['contents_func'](segment_info=segment_info, **segment['args']) else: contents = segment['contents_func'](**segment['args']) if contents is None: diff --git a/scripts/powerline b/scripts/powerline index f36a805c..ffae9863 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- '''Powerline prompt and statusline script.''' import sys +import os try: from powerline.shell import ShellPowerline, get_argparser @@ -13,7 +14,7 @@ except ImportError: if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() powerline = ShellPowerline(args, run_once=True) - rendered = powerline.renderer.render(width=args.width, side=args.side) + rendered = powerline.renderer.render(width=args.width, side=args.side, segment_info=args) try: sys.stdout.write(rendered) except UnicodeEncodeError: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index c1df253a..7f5e2e9c 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -76,18 +76,20 @@ class TestConfig(TestCase): def test_zsh(self): from powerline.shell import ShellPowerline - powerline = ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=False) - powerline.renderer.render() - powerline = ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=False) - powerline.renderer.render() + args = Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt') + powerline = ShellPowerline(args, run_once=False) + powerline.renderer.render(segment_info=args) + powerline = ShellPowerline(args, run_once=False) + powerline.renderer.render(segment_info=args) shutdown(powerline) def test_bash(self): from powerline.shell import ShellPowerline - powerline = ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=False) - powerline.renderer.render() - powerline = ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=False) - powerline.renderer.render() + args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]) + powerline = ShellPowerline(args, run_once=False) + powerline.renderer.render(segment_info=args) + powerline = ShellPowerline(args, run_once=False) + powerline.renderer.render(segment_info=args) shutdown(powerline) def test_ipython(self): From 83ed36903c59b6ef442587a8730ec3a9ce1c4694 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 04:15:26 +0400 Subject: [PATCH 0521/1472] Fix bold attr that may leak into next segment --- powerline/renderers/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 40777c46..945dc765 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -26,7 +26,7 @@ class ShellRenderer(Renderer): False, the argument is reset to the terminal defaults. If an argument is a valid color or attribute, it's added to the ANSI escape code. ''' - ansi = [] + ansi = [0] if fg is not None: if fg is False or fg[0] is False: ansi += [39] From bb06207838eaaac8385bbd7154c95821e2efe6ee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 05:17:19 +0400 Subject: [PATCH 0522/1472] Some lint fixes * Fixed handling of empty scalars in python2: ''.join([]) returns str, ''.join([u'smth']) returns unicode * Fixed check_config: it was always checking for themes, even if it was requested to check colorscheme --- powerline/lint/__init__.py | 6 +++--- powerline/lint/markedjson/scanner.py | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6267e2f9..88a67201 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -84,7 +84,7 @@ class Spec(object): if type(value.value) is not t: echoerr(context=self.cmsg.format(key=context_key(context)), context_mark=context_mark, - problem='must be a {0} instance'.format(t.__name__), + problem='{0!r} must be a {1} instance, not {2}'.format(value, t.__name__, type(value.value).__name__), problem_mark=value.mark) return False, True return True, False @@ -380,9 +380,9 @@ def check_config(d, theme, data, context, echoerr): else: # local_themes ext = context[-3][0] - if ext not in data['configs']['themes'] or theme not in data['configs']['themes'][ext]: + if ext not in data['configs'][d] or theme not in data['configs'][d][ext]: echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext), - problem='failed to find configuration file themes/{0}/{1}.json'.format(ext, theme), + problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme), problem_mark=theme.mark) return True, False, True return True, False, False diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index a5750e82..005b8555 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -24,6 +24,12 @@ class ScannerError(MarkedError): pass +try: + from __builtin__ import unicode +except ImportError: + unicode = str + + class SimpleKey: # See below simple keys treatment. def __init__(self, token_number, index, line, column, mark): @@ -379,7 +385,7 @@ class Scanner: chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) self.forward() end_mark = self.get_mark() - return ScalarToken(''.join(chunks), False, start_mark, end_mark, '"') + return ScalarToken(unicode().join(chunks), False, start_mark, end_mark, '"') ESCAPE_REPLACEMENTS = { 'b': '\x08', From c4e5ff8b49fc34b68a9592f1176700ace7df5619 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 05:22:07 +0400 Subject: [PATCH 0523/1472] Added support for all powerline prompts (in2, out, rewrite) Also fixed problem with prompt not really updating. Fixes #141 --- powerline/bindings/ipython/post_0_11.py | 22 ++-- powerline/bindings/ipython/pre_0_11.py | 111 ++++++++++++++++-- .../colorschemes/ipython/default.json | 3 +- powerline/config_files/config.json | 7 +- .../config_files/themes/ipython/default.json | 16 --- powerline/config_files/themes/ipython/in.json | 26 ++++ .../config_files/themes/ipython/in2.json | 13 ++ .../config_files/themes/ipython/out.json | 25 ++++ .../config_files/themes/ipython/rewrite.json | 23 ++++ powerline/ipython.py | 3 + powerline/lint/__init__.py | 9 ++ powerline/renderers/ipython.py | 12 ++ powerline/renderers/vim.py | 8 +- powerline/segments/ipython.py | 7 ++ tests/test_configuration.py | 6 +- 15 files changed, 248 insertions(+), 43 deletions(-) delete mode 100644 powerline/config_files/themes/ipython/default.json create mode 100644 powerline/config_files/themes/ipython/in.json create mode 100644 powerline/config_files/themes/ipython/in2.json create mode 100644 powerline/config_files/themes/ipython/out.json create mode 100644 powerline/config_files/themes/ipython/rewrite.json create mode 100644 powerline/segments/ipython.py diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index e8a685a6..bdb42923 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -5,17 +5,26 @@ from IPython.core.prompts import PromptManager from IPython.core.hooks import TryNext +class IpythonInfo(object): + def __init__(self, shell): + self._shell = shell + + @property + def prompt_count(self): + return self._shell.execution_count + + class PowerlinePromptManager(PromptManager): powerline = None - def __init__(self, powerline, **kwargs): + def __init__(self, powerline, shell): self.powerline = powerline - super(PowerlinePromptManager, self).__init__(**kwargs) + self.powerline_segment_info = IpythonInfo(shell) + self.shell = shell def render(self, name, color=True, *args, **kwargs): - if name != 'in': - return super(PowerlinePromptManager, self).render(name, color, *args, **kwargs) - res, res_nocolor = self.powerline.renderer.render(output_raw=True) + width = None if name == 'in' else self.width + res, res_nocolor = self.powerline.renderer.render(output_raw=True, width=width, matcher_info=name, segment_info=self.powerline_segment_info) self.txtwidth = len(res_nocolor) self.width = self.txtwidth return res if color else res_nocolor @@ -39,8 +48,7 @@ def load_ipython_extension(ip): old_prompt_manager = ip.prompt_manager powerline = ConfigurableIpythonPowerline(ip) - ip.prompt_manager = PowerlinePromptManager(powerline=powerline, - shell=ip.prompt_manager.shell, config=ip.prompt_manager.config) + ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell) def shutdown_hook(): powerline.renderer.shutdown() diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index b83780d4..21387505 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,23 +1,105 @@ # vim:fileencoding=utf-8:noet from powerline.ipython import IpythonPowerline -from IPython.Prompts import BasePrompt +from IPython.Prompts import BasePrompt, Prompt1 from IPython.ipapi import get as get_ipython from IPython.ipapi import TryNext +import re + + +def string(s): + if type(s) is not str: + return s.encode('utf-8') + else: + return s + + +# HACK: ipython tries to only leave us with plain ASCII +class RewriteResult(object): + def __init__(self, prompt): + self.prompt = string(prompt) + + def __str__(self): + return self.prompt + + def __add__(self, s): + if type(s) is not str: + try: + s = s.encode('utf-8') + except AttributeError: + raise NotImplementedError + return RewriteResult(self.prompt + s) + + +class IpythonInfo(object): + def __init__(self, cache): + self._cache = cache + + @property + def prompt_count(self): + return self._cache.prompt_count + class PowerlinePrompt(BasePrompt): - def __init__(self, powerline, *args, **kwargs): + def __init__(self, powerline, powerline_last_in, old_prompt): self.powerline = powerline - super(PowerlinePrompt, self).__init__(*args, **kwargs) + self.powerline_last_in = powerline_last_in + self.powerline_segment_info = IpythonInfo(old_prompt.cache) + self.cache = old_prompt.cache + if hasattr(old_prompt, 'sep'): + self.sep = old_prompt.sep + self.pad_left = False + + def __str__(self): + self.set_p_str() + return string(self.p_str) + + def set_p_str(self, width=None): + self.p_str, self.p_str_nocolor = ( + self.powerline.renderer.render(output_raw=True, + segment_info=self.powerline_segment_info, + matcher_info=self.powerline_prompt_type, + width=width) + ) + + @staticmethod + def set_colors(): + pass + + +class PowerlinePrompt1(PowerlinePrompt): + powerline_prompt_type = 'in' + rspace = re.compile(r'(\s*)$') + + def __str__(self): + self.cache.prompt_count += 1 + self.set_p_str() + self.cache.last_prompt = self.p_str_nocolor.split('\n')[-1] + return string(self.p_str) def set_p_str(self): - self.p_str, self.p_str_nocolor = self.powerline.renderer.render(output_raw=True) + super(PowerlinePrompt1, self).set_p_str() self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group()) - self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces - 1 + self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces + self.powerline_last_in['nrspaces'] = self.nrspaces + self.powerline_last_in['prompt_text_len'] = self.prompt_text_len def auto_rewrite(self): - # TODO color this - return '%s>%s' % ('-' * self.prompt_text_len, ' ' * self.nrspaces) + return RewriteResult(self.powerline.renderer.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) + + (' ' * self.nrspaces)) + +class PowerlinePromptOut(PowerlinePrompt): + powerline_prompt_type = 'out' + + def set_p_str(self): + super(PowerlinePromptOut, self).set_p_str(width=self.powerline_last_in['prompt_text_len']) + spaces = ' ' * self.powerline_last_in['nrspaces'] + self.p_str += spaces + self.p_str_nocolor += spaces + + +class PowerlinePrompt2(PowerlinePromptOut): + powerline_prompt_type = 'in2' class ConfigurableIpythonPowerline(IpythonPowerline): @@ -28,17 +110,20 @@ class ConfigurableIpythonPowerline(IpythonPowerline): super(ConfigurableIpythonPowerline, self).__init__() -def setup(prompt='1', **kwargs): +def setup(**kwargs): ip = get_ipython() powerline = ConfigurableIpythonPowerline(**kwargs) - attr = 'prompt' + prompt - def late_startup_hook(): - old_prompt = getattr(ip.IP.outputcache, attr) - setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline, - old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left)) + last_in = {'nrspaces': 0, 'prompt_text_len': None} + for attr, prompt_class in ( + ('prompt1', PowerlinePrompt1), + ('prompt2', PowerlinePrompt2), + ('prompt_out', PowerlinePromptOut) + ): + old_prompt = getattr(ip.IP.outputcache, attr) + setattr(ip.IP.outputcache, attr, prompt_class(powerline, last_in, old_prompt)) raise TryNext() def shutdown_hook(): diff --git a/powerline/config_files/colorschemes/ipython/default.json b/powerline/config_files/colorschemes/ipython/default.json index b45bed45..d7875916 100644 --- a/powerline/config_files/colorschemes/ipython/default.json +++ b/powerline/config_files/colorschemes/ipython/default.json @@ -2,6 +2,7 @@ "name": "Default color scheme for IPython prompt", "groups": { "virtualenv": { "fg": "white", "bg": "darkcyan" }, - "prompt": { "fg": "gray9", "bg": "gray4" } + "prompt": { "fg": "gray9", "bg": "gray4" }, + "prompt_count": { "fg": "white", "bg": "gray4" } } } diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index f9cf67be..cf9cf771 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -16,7 +16,12 @@ "ext": { "ipython": { "colorscheme": "default", - "theme": "default" + "theme": "in", + "local_themes": { + "rewrite": "rewrite", + "out": "out", + "in2": "in2" + } }, "shell": { "colorscheme": "default", diff --git a/powerline/config_files/themes/ipython/default.json b/powerline/config_files/themes/ipython/default.json deleted file mode 100644 index 95d82566..00000000 --- a/powerline/config_files/themes/ipython/default.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "default_module": "powerline.segments.common", - "segments": { - "left": [ - { - "name": "virtualenv" - }, - { - "type": "string", - "contents": "In:", - "highlight_group": ["prompt"], - "draw_divider": false - } - ] - } -} diff --git a/powerline/config_files/themes/ipython/in.json b/powerline/config_files/themes/ipython/in.json new file mode 100644 index 00000000..bf6be670 --- /dev/null +++ b/powerline/config_files/themes/ipython/in.json @@ -0,0 +1,26 @@ +{ + "default_module": "powerline.segments.common", + "segments": { + "left": [ + { + "name": "virtualenv" + }, + { + "type": "string", + "contents": "In[", + "draw_divider": false, + "highlight_group": ["prompt"] + }, + { + "name": "prompt_count", + "module": "powerline.segments.ipython", + "draw_divider": false + }, + { + "type": "string", + "contents": "]", + "highlight_group": ["prompt"] + } + ] + } +} diff --git a/powerline/config_files/themes/ipython/in2.json b/powerline/config_files/themes/ipython/in2.json new file mode 100644 index 00000000..601fc9e5 --- /dev/null +++ b/powerline/config_files/themes/ipython/in2.json @@ -0,0 +1,13 @@ +{ + "default_module": "powerline.segments.common", + "segments": { + "left": [ + { + "type": "string", + "contents": "", + "width": "auto", + "highlight_group": ["prompt"] + } + ] + } +} diff --git a/powerline/config_files/themes/ipython/out.json b/powerline/config_files/themes/ipython/out.json new file mode 100644 index 00000000..6d4eac53 --- /dev/null +++ b/powerline/config_files/themes/ipython/out.json @@ -0,0 +1,25 @@ +{ + "default_module": "powerline.segments.common", + "segments": { + "left": [ + { + "type": "string", + "contents": "Out[", + "draw_divider": false, + "width": "auto", + "align": "r", + "highlight_group": ["prompt"] + }, + { + "name": "prompt_count", + "module": "powerline.segments.ipython", + "draw_divider": false + }, + { + "type": "string", + "contents": "]", + "highlight_group": ["prompt"] + } + ] + } +} diff --git a/powerline/config_files/themes/ipython/rewrite.json b/powerline/config_files/themes/ipython/rewrite.json new file mode 100644 index 00000000..4616769a --- /dev/null +++ b/powerline/config_files/themes/ipython/rewrite.json @@ -0,0 +1,23 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "", + "draw_divider": false, + "width": "auto", + "highlight_group": ["prompt"] + }, + { + "name": "prompt_count", + "module": "powerline.segments.ipython", + "draw_divider": false + }, + { + "type": "string", + "contents": ">", + "highlight_group": ["prompt"] + } + ] + } +} diff --git a/powerline/ipython.py b/powerline/ipython.py index e1fc4149..64f53ca7 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -14,6 +14,9 @@ class IpythonPowerline(Powerline): else: return super(IpythonPowerline, self).get_config_paths() + def get_local_themes(self, local_themes): + return dict(((type, {'config': self.load_theme_config(name)}) for type, name in local_themes.items())) + def load_main_config(self): r = super(IpythonPowerline, self).load_main_config() if self.config_overrides: diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 88a67201..fe9460d5 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -416,6 +416,15 @@ main_spec = (Spec( local_themes=Spec() .unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec()) ), + ipython=Spec( + colorscheme=colorscheme_spec(), + theme=theme_spec(), + local_themes=Spec( + in2=theme_spec(), + out=theme_spec(), + rewrite=theme_spec(), + ), + ), ).unknown_spec(check_ext, Spec( colorscheme=colorscheme_spec(), diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 2fa63acd..f2d936c2 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet from powerline.renderers.shell import ShellRenderer +from powerline.theme import Theme class IpythonRenderer(ShellRenderer): @@ -8,5 +9,16 @@ class IpythonRenderer(ShellRenderer): escape_hl_start = '\x01' escape_hl_end = '\x02' + def get_theme(self, matcher_info): + if matcher_info == 'in': + return self.theme + else: + match = self.local_themes[matcher_info] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) + return match['theme'] + renderer = IpythonRenderer diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 08ebc0bc..de2135a6 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -46,9 +46,11 @@ class VimRenderer(Renderer): for matcher in self.local_themes.keys(): if matcher(matcher_info): match = self.local_themes[matcher] - if 'config' in match: - match['theme'] = Theme(theme_config=match.pop('config'), top_theme_config=self.theme_config, **self.theme_kwargs) - return match['theme'] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) + return match['theme'] else: return self.theme diff --git a/powerline/segments/ipython.py b/powerline/segments/ipython.py new file mode 100644 index 00000000..c1b62615 --- /dev/null +++ b/powerline/segments/ipython.py @@ -0,0 +1,7 @@ +# vim:fileencoding=utf-8:noet + +from powerline.theme import requires_segment_info + +@requires_segment_info +def prompt_count(segment_info): + return str(segment_info.prompt_count) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 7f5e2e9c..d2c0f5a6 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -101,8 +101,10 @@ class TestConfig(TestCase): theme_overrides = {} powerline = IpyPowerline() - powerline.renderer.render() - powerline.renderer.render() + segment_info = Args(prompt_count=1) + for prompt_type in ['in', 'in2', 'out', 'rewrite']: + powerline.renderer.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.renderer.render(matcher_info=prompt_type, segment_info=segment_info) shutdown(powerline) def test_wm(self): From b517df774bc8005b9e4d7f07275e22c59e8785eb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 05:45:52 +0400 Subject: [PATCH 0524/1472] Fix bytes handling on python3 Fixes #356 --- powerline/bindings/vim/__init__.py | 17 +++++++++++++++-- powerline/matchers/vim.py | 2 +- powerline/renderers/vim.py | 4 +--- powerline/segments/vim.py | 4 ++-- tests/vim.py | 25 +++++++++++++++++++++++++ 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index f9741163..59a400f7 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +import sys + try: import vim except ImportError: @@ -15,7 +17,10 @@ try: def vim_get_func(f, rettype=None): '''Return a vim function binding.''' try: - return vim.bindeval('function("' + f + '")') + func = vim.bindeval('function("' + f + '")') + if sys.version_info >= (3,) and rettype is str: + return (lambda *args, **kwargs: func(*args, **kwargs).decode('utf-8', errors='replace')) + return func except vim.error: return None except AttributeError: @@ -47,4 +52,12 @@ except AttributeError: vim_get_func = VimFunc -getbufvar = vim_get_func('getbufvar') +if sys.version_info < (3,) or not hasattr(vim, 'bindeval'): + getbufvar = vim_get_func('getbufvar') +else: + _getbufvar = vim_get_func('getbufvar') + def getbufvar(*args): + r = _getbufvar(*args) + if type(r) is bytes: + return r.decode('utf-8') + return r diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index fd1db4b6..e46b5ef2 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -7,7 +7,7 @@ from powerline.bindings.vim import getbufvar def help(matcher_info): - return getbufvar(matcher_info['bufnr'], '&buftype') == 'help' + return str(getbufvar(matcher_info['bufnr'], '&buftype')) == 'help' def cmdwin(matcher_info): diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 08ebc0bc..2e3af3a7 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -10,9 +10,7 @@ from powerline.theme import Theme import vim -vim_mode = vim_get_func('mode') -vim_getwinvar = vim_get_func('getwinvar') -vim_setwinvar = vim_get_func('setwinvar') +vim_mode = vim_get_func('mode', rettype=str) mode_translations = { chr(ord('V') - 0x40): '^V', chr(ord('S') - 0x40): '^S', diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f0ae931f..e6f84d9f 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -19,8 +19,8 @@ from collections import defaultdict vim_funcs = { 'virtcol': vim_get_func('virtcol', rettype=int), - 'fnamemodify': vim_get_func('fnamemodify'), - 'expand': vim_get_func('expand'), + 'fnamemodify': vim_get_func('fnamemodify', rettype=str), + 'expand': vim_get_func('expand', rettype=str), 'bufnr': vim_get_func('bufnr', rettype=int), 'line2byte': vim_get_func('line2byte', rettype=int), } diff --git a/tests/vim.py b/tests/vim.py index 6ba67e4a..5c28d708 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -31,6 +31,26 @@ def _logged(func): return f +def _construct_result(r): + import sys + if sys.version_info < (3,): + return r + else: + if type(r) is str: + return r.encode('utf-8') + elif type(r) is dict or type(r) is list: + raise NotImplementedError + return r + + +def _str_func(func): + from functools import wraps + @wraps(func) + def f(*args, **kwargs): + return _construct_result(func(*args, **kwargs)) + return f + + def _log_print(): import sys for entry in _log: @@ -75,6 +95,7 @@ def bindeval(expr): @_logged +@_str_func def _emul_mode(*args): if args and args[0]: return _mode @@ -83,6 +104,7 @@ def _emul_mode(*args): @_logged +@_str_func def _emul_getbufvar(bufnr, varname): if varname[0] == '&': if bufnr not in _buf_options: @@ -98,6 +120,7 @@ def _emul_getbufvar(bufnr, varname): @_logged +@_str_func def _emul_getwinvar(winnr, varname): return _win_scopes[winnr][varname] @@ -115,6 +138,7 @@ def _emul_virtcol(expr): @_logged +@_str_func def _emul_fnamemodify(path, modstring): import os _modifiers = { @@ -130,6 +154,7 @@ def _emul_fnamemodify(path, modstring): @_logged +@_str_func def _emul_expand(expr): if expr == '': return _buffer() From 87e39d0b561f173e31ff3a4af02eb8c9f5fd2e61 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 05:51:41 +0400 Subject: [PATCH 0525/1472] Autodetect python version --- powerline/bindings/vim/plugin/powerline.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 4c014f4c..9bd90b42 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -15,7 +15,8 @@ if ! has('python') && ! has('python3') finish endif -let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', 'py'), '\v^(py)%[thon](3?)$', '\1\2', '') +let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', has('python') ? 'py' : 'py3'), + \'\v^(py)%[thon](3?)$', '\1\2', '') let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') let s:import_cmd = 'from powerline.vim import VimPowerline' From 33592145ae9895ccbbcea88d6c3fd22723f4897a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 05:56:55 +0400 Subject: [PATCH 0526/1472] Also fix VimRenderer.strwidth Fixes #356 --- powerline/renderers/vim.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 2e3af3a7..1631e694 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -8,6 +8,7 @@ from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme import vim +import sys vim_mode = vim_get_func('mode', rettype=str) @@ -51,11 +52,16 @@ class VimRenderer(Renderer): return self.theme if hasattr(vim, 'strwidth'): - @staticmethod - def strwidth(string): - # Does not work with tabs, but neither is strwidth from default - # renderer - return vim.strwidth(string.encode('utf-8')) + if sys.version_info < (3,): + @staticmethod + def strwidth(string): + # Does not work with tabs, but neither is strwidth from default + # renderer + return vim.strwidth(string.encode('utf-8')) + else: + @staticmethod + def strwidth(string): + return vim.strwidth(string) def render(self, window_id, winidx, current): '''Render all segments. From 840ac0109b3af05fb71460ddb1362f7072dd0088 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Mar 2013 00:04:30 +0400 Subject: [PATCH 0527/1472] Port common.branch segment to KwThreaded*, add common.repository_status --- powerline/segments/common.py | 83 ++++++++++++++++++++++++++++++------ tests/test_segments.py | 7 +-- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index a0c54c2a..fdcd8cf5 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -29,25 +29,84 @@ def hostname(only_if_ssh=False): return socket.gethostname() -def branch(status_colors=True): - '''Return the current VCS branch. +class RepositorySegment(KwThreadedSegment): + def __init__(self): + super(RepositorySegment, self).__init__() + self.directories = {} - :param bool status_colors: - determines whether repository status will be used to determine highlighting. Default: True. + @staticmethod + def key(**kwargs): + return os.path.abspath(os.getcwd()) - Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. - ''' - repo = guess(path=os.path.abspath(os.getcwd())) - if repo: - branch = repo.branch() - if status_colors: + def update(self): + # .compute_state() is running only in this method, and only in one + # thread, thus operations with .directories do not need write locks + # (.render() method is not using .directories). If this is changed + # .directories needs redesigning + self.directories.clear() + super(RepositorySegment, self).update() + + def compute_state(self, path): + repo = guess(path=path) + if repo: + if repo.directory in self.directories: + return self.directories[repo.directory] + else: + r = self.process_repo(repo) + self.directories[repo.directory] = r + return r + + +class RepositoryStatusSegment(RepositorySegment): + interval = 2 + + @staticmethod + def process_repo(repo): + return repo.status() + + +repository_status = with_docstring(RepositoryStatusSegment(), +'''Return the status for the current VCS repository.''') + + +class BranchSegment(RepositorySegment): + interval = 0.2 + started_repository_status = False + + @staticmethod + def process_repo(repo): + return repo.branch() + + @staticmethod + def render_one(branch, status_colors=False, **kwargs): + if branch and status_colors: return [{ 'contents': branch, - 'highlight_group': ['branch_dirty' if repo.status() else 'branch_clean', 'branch'], + 'highlight_group': ['branch_dirty' if repository_status() else 'branch_clean', 'branch'], }] else: return branch - return None + + def startup(self, status_colors=False, **kwargs): + super(BranchSegment, self).startup() + if status_colors: + self.started_repository_status = True + repository_status.startup() + + def shutdown(self): + if self.started_repository_status: + repository_status.shutdown() + super(BranchSegment, self).shutdown() + + +branch = with_docstring(BranchSegment(), +'''Return the current VCS branch. + +:param bool status_colors: + determines whether repository status will be used to determine highlighting. Default: True. + +Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. +''') def cwd(dir_shorten_len=None, dir_limit_depth=None): diff --git a/tests/test_segments.py b/tests/test_segments.py index b085bfdd..62978294 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -48,14 +48,15 @@ class TestCommon(TestCase): self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): - with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None)): + with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): self.assertEqual(common.branch(status_colors=False), 'tests') self.assertEqual(common.branch(status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) - with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ')): + with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): self.assertEqual(common.branch(status_colors=False), 'tests') - self.assertEqual(common.branch(), + self.assertEqual(common.branch(status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) + self.assertEqual(common.branch(), 'tests') with replace_module_attr(common, 'guess', lambda path: None): self.assertEqual(common.branch(), None) From 0c9337bd7285e3cd96509204096f3472d2cc3d2d Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Mar 2013 00:05:24 +0400 Subject: [PATCH 0528/1472] Allow multiple shutdown calls --- powerline/lib/threaded.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 8d8ff5d4..dfd65ce1 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -45,8 +45,9 @@ class ThreadedSegment(Thread): self.sleep(monotonic() - start_time) def shutdown(self): - self.keep_going = False - self.update_lock.acquire() + if self.keep_going: + self.keep_going = False + self.update_lock.acquire() def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. From a65a27e673dd46f77391d82e08c8687e1ad8b989 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Mar 2013 00:21:22 +0400 Subject: [PATCH 0529/1472] Allow multiple starts by not subclassing from Thread --- powerline/lib/threaded.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index dfd65ce1..a9347553 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -8,7 +8,7 @@ from time import sleep from threading import Thread, Lock -class ThreadedSegment(Thread): +class ThreadedSegment(object): daemon = True min_sleep_time = 0.1 update_first = True @@ -21,6 +21,7 @@ class ThreadedSegment(Thread): self.keep_going = True self.run_once = True self.did_set_interval = False + self.thread = None def __call__(self, **kwargs): if self.run_once: @@ -32,6 +33,14 @@ class ThreadedSegment(Thread): with self.write_lock: return self.render(**kwargs) + def is_alive(self): + return self.thread and self.thread.is_alive() + + def start(self): + self.thread = Thread(target=self.run) + self.thread.daemon = self.daemon + self.thread.start() + def sleep(self, adjust_time): sleep(max(self.interval - adjust_time, self.min_sleep_time)) From e6dd40bff05e01a832874e69df1291e3fecf7c14 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Mar 2013 08:58:25 +0400 Subject: [PATCH 0530/1472] Fix behavior with run_once: .update() was called too much times --- powerline/lib/threaded.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index a9347553..89c69e93 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -72,7 +72,9 @@ class ThreadedSegment(object): self.set_interval(interval) # Without this we will not have to wait long until receiving bug “I # opened vim, but branch information is only shown after I move cursor”. - if self.update_first: + # + # If running once .update() is called in __call__. + if self.update_first and not self.run_once: self.update_first = False self.update() @@ -115,7 +117,7 @@ class KwThreadedSegment(ThreadedSegment): except KeyError: # self.update_missing has the same reasoning as self.update_first in # parent class - update_state = self.compute_state(key) if self.update_missing else None + update_state = self.compute_state(key) if self.update_missing or self.run_once else None # No locks: render method is already running with write_lock acquired. self.queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) @@ -138,7 +140,7 @@ class KwThreadedSegment(ThreadedSegment): self.set_interval(interval) key = self.key(**kwargs) - if key not in self.queries: + if not self.run_once and key not in self.queries: self.queries[key] = (monotonic(), self.compute_state(key) if self.update_missing else None) @staticmethod From a0219164283ac70754bf8a12197ecfe7596a3513 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Mar 2013 18:33:55 +0400 Subject: [PATCH 0531/1472] Make update_first configurable --- docs/source/powerline_autodoc.py | 6 +++++- powerline/lib/threaded.py | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index a3a3d54b..084c8547 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -28,6 +28,9 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): if isinstance(self.object, ThreadedSegment): args = ['interval'] defaults = [getattr(self.object, 'interval', 1)] + if self.object.update_first: + args.append('update_first') + defaults.append(True) methods = ['render', 'set_state'] if isinstance(self.object, KwThreadedSegment): methods += ['key', 'render_one'] @@ -41,7 +44,8 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): if (arg == 'self' or (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)) or - (method == 'render_one' and -i == len(argspec.args))): + (method == 'render_one' and -i == len(argspec.args)) or + arg in args): continue if argspec.defaults and len(argspec.defaults) >= -i: default = argspec.defaults[i] diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 89c69e93..eedfc142 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -67,14 +67,14 @@ class ThreadedSegment(object): self.interval = interval self.has_set_interval = True - def set_state(self, interval=None, **kwargs): + def set_state(self, update_first=True, interval=None, **kwargs): if not self.did_set_interval or interval: self.set_interval(interval) # Without this we will not have to wait long until receiving bug “I # opened vim, but branch information is only shown after I move cursor”. # # If running once .update() is called in __call__. - if self.update_first and not self.run_once: + if update_first and self.update_first and not self.run_once: self.update_first = False self.update() @@ -99,7 +99,6 @@ def printed(func): class KwThreadedSegment(ThreadedSegment): drop_interval = 10 * 60 - update_missing = True update_first = False def __init__(self): @@ -115,9 +114,7 @@ class KwThreadedSegment(ThreadedSegment): try: update_state = self.queries[key][1] except KeyError: - # self.update_missing has the same reasoning as self.update_first in - # parent class - update_state = self.compute_state(key) if self.update_missing or self.run_once else None + update_state = self.compute_state(key) if self.update_first or self.run_once else None # No locks: render method is already running with write_lock acquired. self.queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) @@ -135,13 +132,18 @@ class KwThreadedSegment(ThreadedSegment): for key in removes: self.queries.pop(key) - def set_state(self, interval=None, **kwargs): + def set_state(self, update_first=True, interval=None, **kwargs): if not self.did_set_interval or (interval < self.interval): self.set_interval(interval) + # Allow only to forbid to compute missing values: in either user + # configuration or in subclasses. + if self.update_first: + self.update_first = update_first + key = self.key(**kwargs) if not self.run_once and key not in self.queries: - self.queries[key] = (monotonic(), self.compute_state(key) if self.update_missing else None) + self.queries[key] = (monotonic(), self.compute_state(key) if self.update_first else None) @staticmethod def render_one(update_state, **kwargs): From 127a600e1b449fa5b04bb74e82a93f3fc86768c4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Mar 2013 16:49:00 +0400 Subject: [PATCH 0532/1472] Rework API for custom vim events --- powerline/bindings/vim/plugin/powerline.vim | 19 +++++---- powerline/segments/vim.py | 44 ++++++++++----------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 9bd90b42..38518e91 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -40,8 +40,6 @@ catch endif endtry endtry -exec s:powerline_pycmd 'powerline = VimPowerline()' -exec s:powerline_pycmd 'del VimPowerline' if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval) let s:pyeval = function(s:powerline_pyeval) @@ -80,18 +78,19 @@ endfunction function! PowerlineRegisterCachePurgerEvent(event) exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent' augroup Powerline - exec 'autocmd!' a:event '*' s:powerline_pycmd.' powerline_launchevent("'.a:event.'")' + exec 'autocmd' a:event '*' s:powerline_pycmd.' powerline_launchevent("'.a:event.'")' augroup END endfunction +augroup Powerline + autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' + autocmd! VimEnter * :redrawstatus! + autocmd! VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()' +augroup END + +exec s:powerline_pycmd 'powerline = VimPowerline()' +exec s:powerline_pycmd 'del VimPowerline' " Is immediately changed when PowerlineNew() function is run. Good for global " value. set statusline=%!PowerlineNew() call PowerlineNew() - -augroup Powerline - autocmd! - autocmd ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' - autocmd VimEnter * :redrawstatus! - autocmd VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()' -augroup END diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c4bb4e4c..94d0ec81 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -47,34 +47,32 @@ vim_modes = { } -eventcaches = defaultdict(lambda: []) -bufeventcaches = defaultdict(lambda: []) +eventfuncs = defaultdict(lambda: []) +bufeventfuncs = defaultdict(lambda: []) +defined_events = set() -def purgeonevents_reg(events, eventcaches=bufeventcaches): - def cache_reg_func(cache): - for event in events: - if event not in eventcaches: - vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")') - eventcaches[event].append(cache) - return cache_reg_func - -purgeall_on_shell = purgeonevents_reg(('ShellCmdPost', 'ShellFilterPost', 'FocusGained'), eventcaches=eventcaches) -purgebuf_on_shell_and_write = purgeonevents_reg(('BufWritePost', 'ShellCmdPost', 'ShellFilterPost', 'FocusGained')) +def purgeonevents_reg(func, events, is_buffer_event=False): + if is_buffer_event: + cureventfuncs = bufeventfuncs + else: + cureventfuncs = eventfuncs + for event in events: + if event not in defined_events: + vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")') + defined_events.add(event) + cureventfuncs[event].append(func) def launchevent(event): - global eventcaches - global bufeventcaches - for cache in eventcaches[event]: - cache.clear() - if bufeventcaches[event]: - buf = int(vim_funcs['expand']('')) - for cache in bufeventcaches[event]: - try: - cache.pop(buf) - except KeyError: - pass + global eventfuncs + global bufeventfuncs + for func in eventfuncs[event]: + func() + if bufeventfuncs[event]: + buffer = vim.buffers[int(vim_funcs['expand']('')) - 1] + for func in bufeventfuncs[event]: + func(buffer) # TODO Remove cache when needed From e151b600b2132b754430e9b8cb1d3dd437b7e9fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Mar 2013 16:50:06 +0400 Subject: [PATCH 0533/1472] =?UTF-8?q?Use=20python=E2=80=99s=20try/catch,?= =?UTF-8?q?=20not=20vim=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #344 --- powerline/bindings/vim/plugin/powerline.vim | 35 ++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 38518e91..83f87296 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -21,24 +21,23 @@ let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') let s:import_cmd = 'from powerline.vim import VimPowerline' try - exec s:powerline_pycmd s:import_cmd -catch - " An error occurred while importing the module, it could be installed - " outside of Python's module search paths. Update sys.path and try again. - exec s:powerline_pycmd 'import sys, vim' - exec s:powerline_pycmd 'sys.path.append(vim.eval(''expand(":h:h:h:h:h")''))' - try - exec s:powerline_pycmd s:import_cmd - let s:launched = 1 - finally - if !exists('s:launched') - call s:CriticalError('An error occurred while importing the Powerline package. - \ This could be caused by an invalid sys.path setting, or by an incompatible - \ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult - \ the troubleshooting section in the documentation for possible solutions.') - finish - endif - endtry + exec s:powerline_pycmd "try:\n" + \ ." ".s:import_cmd."\n" + \ ."except ImportError:\n" + \ ." import sys, vim\n" + \ ." sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))\n" + \ ." ".s:import_cmd + let s:launched = 1 +finally + if !exists('s:launched') + call s:CriticalError('An error occurred while importing the Powerline package. + \ This could be caused by an invalid sys.path setting, or by an incompatible + \ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult + \ the troubleshooting section in the documentation for possible solutions.') + finish + else + unlet s:launched + endif endtry if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval) From cc1c9826961c965caeb5a47c0c5836ddea669988 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Mar 2013 20:47:11 +0400 Subject: [PATCH 0534/1472] Move configuration to /etc/xdg/powerline XDG_CONFIG_DIRS is set in /etc/env.d by configuration from xdg-utils package, but it is not guaranteed to be present in the system --- packages/gentoo/app-misc/powerline/Manifest | 2 +- packages/gentoo/app-misc/powerline/powerline-9999.ebuild | 4 ++++ powerline/__init__.py | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index cdeed146..802c4188 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 3916 SHA256 754f8750aa5c6a455871d44c9478b81278b71318e591a13ef30f763ddb1fbda5 SHA512 48dd9d3ac737417b6a072397a4a50fefc04c0cb4a3c6249b5bf11ee201986f7b25a94b83e6901e8099ae3ea733dc0e650f5ce2234b5143bdbafd906af56916e7 WHIRLPOOL 803298b97adaeb2e3a6cd31c4bd12897373d7ce0722df22a991c684ecd5146fcd87addfc6e428374d924d6cb950dee6bbcf2ea0a262904cc4d04e3eac0b2dcb8 +EBUILD powerline-9999.ebuild 4091 SHA256 d1d13b09e3ebefdaa1b90c50ccceb563bb444ca0f484e6448a993d2612dfbb78 SHA512 47f211249bb85608cb9c5b8e72daba5c36971e294e27843396cbf517bc792db64a4f80e843f5c883f8411ed4c9cef5618f9d617b305303a31a4590636879dcd3 WHIRLPOOL 2fc50e1da46d56d160140d3a87a4d9421bf63d67c792bfd88486e0dc72d379045ed79d152aa060a014e7f6bf4eb232642463014de9523909946bd8b2cbf83371 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 9153ca33..8eadbabb 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -130,6 +130,10 @@ src_install() { fi rm powerline/bindings/bash/powerline.sh elog "" + insinto /etc/xdg/powerline + doins -r powerline/config_files/* + rm -r powerline/config_files + sed -i -e "/DEFAULT_SYSTEM_CONFIG_DIR/ s@None@'/etc/xdg'@" powerline/__init__.py distutils-r1_src_install use doc && dohtml -r docs_output/* } diff --git a/powerline/__init__.py b/powerline/__init__.py index 2fab620e..18779189 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -8,6 +8,9 @@ import sys from powerline.colorscheme import Colorscheme +DEFAULT_SYSTEM_CONFIG_DIR = None + + def open_file(path): return open(path, 'r') @@ -86,7 +89,7 @@ class Powerline(object): config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) config_path = os.path.join(config_home, 'powerline') config_paths = [config_path] - config_dirs = os.environ.get('XDG_CONFIG_DIRS', None) + config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR) if config_dirs is not None: config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')]) plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') From ed435f8063a81ef27969c0a699438a6b5fffbb7b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Mar 2013 17:51:02 +0400 Subject: [PATCH 0535/1472] Add required pl argument to segments Fixes #340 Ref #330 --- docs/source/configuration.rst | 11 + powerline/__init__.py | 94 +++++++- powerline/bindings/zsh/__init__.py | 21 +- powerline/lib/threaded.py | 64 +++--- powerline/lint/__init__.py | 17 +- powerline/renderer.py | 4 +- powerline/renderers/vim.py | 7 +- powerline/segment.py | 1 + powerline/segments/common.py | 101 +++++---- powerline/segments/ipython.py | 2 +- powerline/segments/shell.py | 4 +- powerline/segments/vim.py | 38 ++-- powerline/shell.py | 3 - powerline/theme.py | 32 ++- tests/lib/__init__.py | 64 ++++-- tests/test_configuration.py | 6 +- tests/test_segments.py | 339 +++++++++++++++-------------- 17 files changed, 510 insertions(+), 298 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index e4d8bcbc..4651e5ac 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -155,6 +155,17 @@ Common configuration is a subdictionary that is a value of ``common`` key in :ref:`module segment option `. Paths defined here have priority when searching for modules. +``log_file`` + Defines path which will hold powerline logs. If not present, logging will be + done to stderr. + +``log_level`` + String, determines logging level. Defaults to ``WARNING``. + +``log_format`` + String, determines format of the log messages. Defaults to + ``'%(asctime)s:%(level)s:%(message)s'``. + Extension-specific configuration -------------------------------- diff --git a/powerline/__init__.py b/powerline/__init__.py index 18779189..cd21a94e 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -4,6 +4,7 @@ from __future__ import absolute_import import json import os import sys +import logging from powerline.colorscheme import Colorscheme @@ -25,6 +26,42 @@ def load_json_config(search_paths, config_file, load=json.load, open_file=open_f raise IOError('Config file not found in search path: {0}'.format(config_file)) +class PowerlineState(object): + def __init__(self, logger, environ, getcwd, home): + self.environ = environ + self.getcwd = getcwd + self.home = home or environ.get('HOME', None) + self.logger = logger + self.prefix = None + self.last_msgs = {} + + def _log(self, attr, msg, *args, **kwargs): + prefix = kwargs.get('prefix') or self.prefix + msg = ((prefix + ':') if prefix else '') + msg.format(*args, **kwargs) + key = attr+':'+prefix + if msg != self.last_msgs.get(key): + getattr(self.logger, attr)(msg) + self.last_msgs[key] = msg + + def critical(self, msg, *args, **kwargs): + self._log('critical', msg, *args, **kwargs) + + def exception(self, msg, *args, **kwargs): + self._log('exception', msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + self._log('info', msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + self._log('error', msg, *args, **kwargs) + + def warn(self, msg, *args, **kwargs): + self._log('warning', msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + self._log('debug', msg, *args, **kwargs) + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -37,9 +74,29 @@ class Powerline(object): :param str renderer_module: Overrides renderer module (defaults to ``ext``). Should be the name of the package imported like this: ``powerline.renders.{render_module}``. + :param bool run_once: + Determines whether .renderer.render() method will be run only once + during python session. + :param Logger logger: + If present, no new logger will be created and this logger will be used. + :param dict environ: + Object with ``.__getitem__`` and ``.get`` methods used to obtain + environment variables. Defaults to ``os.environ``. + :param func getcwd: + Function used to get current working directory. Defaults to + ``os.getcwdu`` or ``os.getcwd``. + :param str home: + Home directory. Defaults to ``environ.get('HOME')``. ''' - def __init__(self, ext, renderer_module=None, run_once=False): + def __init__(self, + ext, + renderer_module=None, + run_once=False, + logger=None, + environ=os.environ, + getcwd=getattr(os, 'getcwdu', os.getcwd), + home=None): self.config_paths = self.get_config_paths() # Load main config file @@ -78,7 +135,40 @@ class Powerline(object): 'tmux_escape': common_config.get('additional_escapes') == 'tmux', 'screen_escape': common_config.get('additional_escapes') == 'screen', } - self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options) + + # Create logger + if not logger: + log_format = common_config.get('format', '%(asctime)s:%(level)s:%(message)s') + formatter = logging.Formatter(log_format) + + level = getattr(logging, common_config.get('log_level', 'WARNING')) + handler = self.get_log_handler(common_config) + handler.setLevel(level) + + logger = logging.getLogger('powerline') + logger.setLevel(level) + logger.addHandler(handler) + + pl = PowerlineState(logger=logger, environ=environ, getcwd=getcwd, home=home) + + self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, pl, **options) + + def get_log_handler(self, common_config): + '''Get log handler. + + :param dict common_config: + Common configuration. + + :return: logging.Handler subclass. + ''' + log_file = common_config.get('file', None) + if log_file: + log_dir = os.path.dirname(log_file) + if not os.path.isdir(log_dir): + os.mkdir(log_dir) + return logging.FileHandler(log_file) + else: + return logging.StreamHandler() @staticmethod def get_config_paths(): diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 8be9f6f8..295cd699 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -45,6 +45,22 @@ class Args(object): return None +class Environment(object): + @staticmethod + def __getitem__(key): + try: + return zsh.getvalue(key) + except IndexError as e: + raise KeyError(*e.args) + + @staticmethod + def get(key, default=None): + try: + return zsh.getvalue(key) + except IndexError: + return default + + class Prompt(object): __slots__ = ('render', 'side', 'savedpsvar', 'savedps') @@ -53,9 +69,10 @@ class Prompt(object): self.side = side self.savedpsvar = savedpsvar self.savedps = savedps + self.args = powerline.args def __str__(self): - return self.render(width=zsh.columns(), side=self.side).encode('utf-8') + return self.render(width=zsh.columns(), side=self.side, segment_info=args).encode('utf-8') def __del__(self): if self.savedps: @@ -71,6 +88,6 @@ def set_prompt(powerline, psvar, side): def setup(): - powerline = ShellPowerline(Args()) + powerline = ShellPowerline(Args(), environ=Environment(), getcwd=lambda: zsh.getvalue('PWD')) set_prompt(powerline, 'PS1', 'left') set_prompt(powerline, 'RPS1', 'right') diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index eedfc142..1b74d257 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -23,15 +23,24 @@ class ThreadedSegment(object): self.did_set_interval = False self.thread = None - def __call__(self, **kwargs): + def __call__(self, update_first=True, **kwargs): if self.run_once: + self.pl = kwargs['pl'] self.set_state(**kwargs) self.update() elif not self.is_alive(): - self.startup(**kwargs) + # Without this we will not have to wait long until receiving bug “I + # opened vim, but branch information is only shown after I move + # cursor”. + # + # If running once .update() is called in __call__. + if update_first and self.update_first: + self.update_first = False + self.update() + self.start() with self.write_lock: - return self.render(**kwargs) + return self.render(update_first=update_first, **kwargs) def is_alive(self): return self.thread and self.thread.is_alive() @@ -49,7 +58,10 @@ class ThreadedSegment(object): start_time = monotonic() with self.update_lock: - self.update() + try: + self.update() + except Exception as e: + self.error('Exception while updating: {0}', str(e)) self.sleep(monotonic() - start_time) @@ -67,28 +79,27 @@ class ThreadedSegment(object): self.interval = interval self.has_set_interval = True - def set_state(self, update_first=True, interval=None, **kwargs): + def set_state(self, interval=None, **kwargs): if not self.did_set_interval or interval: self.set_interval(interval) - # Without this we will not have to wait long until receiving bug “I - # opened vim, but branch information is only shown after I move cursor”. - # - # If running once .update() is called in __call__. - if update_first and self.update_first and not self.run_once: - self.update_first = False - self.update() - def startup(self, **kwargs): - # Normally .update() succeeds to run before value is requested, meaning - # that user is getting values he needs directly at vim startup. Without - # .startup() we will not have to wait long until receiving bug “I opened - # vim, but branch information is only shown after I move cursor”. + def startup(self, pl, **kwargs): self.run_once = False + self.pl = pl if not self.is_alive(): self.set_state(**kwargs) self.start() + def error(self, *args, **kwargs): + self.pl.error(prefix=self.__class__.__name__, *args, **kwargs) + + def warn(self, *args, **kwargs): + self.pl.warn(prefix=self.__class__.__name__, *args, **kwargs) + + def debug(self, *args, **kwargs): + self.pl.debug(prefix=self.__class__.__name__, *args, **kwargs) + def printed(func): def f(*args, **kwargs): @@ -109,12 +120,14 @@ class KwThreadedSegment(ThreadedSegment): def key(**kwargs): return frozenset(kwargs.items()) - def render(self, **kwargs): + def render(self, update_first, **kwargs): key = self.key(**kwargs) try: update_state = self.queries[key][1] except KeyError: - update_state = self.compute_state(key) if self.update_first or self.run_once else None + # Allow only to forbid to compute missing values: in either user + # configuration or in subclasses. + update_state = self.compute_state(key) if update_first and self.update_first or self.run_once else None # No locks: render method is already running with write_lock acquired. self.queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) @@ -124,7 +137,10 @@ class KwThreadedSegment(ThreadedSegment): removes = [] for key, (last_query_time, state) in list(self.queries.items()): if last_query_time < monotonic() < last_query_time + self.drop_interval: - updates[key] = (last_query_time, self.compute_state(key)) + try: + updates[key] = (last_query_time, self.compute_state(key)) + except Exception as e: + self.error('Exception while computing state for {0}: {1}', repr(key), str(e)) else: removes.append(key) with self.write_lock: @@ -132,19 +148,13 @@ class KwThreadedSegment(ThreadedSegment): for key in removes: self.queries.pop(key) - def set_state(self, update_first=True, interval=None, **kwargs): + def set_state(self, interval=None, **kwargs): if not self.did_set_interval or (interval < self.interval): self.set_interval(interval) - # Allow only to forbid to compute missing values: in either user - # configuration or in subclasses. if self.update_first: self.update_first = update_first - key = self.key(**kwargs) - if not self.run_once and key not in self.queries: - self.queries[key] = (monotonic(), self.compute_state(key) if self.update_first else None) - @staticmethod def render_one(update_state, **kwargs): return update_state diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 0ab981e4..91078e26 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -8,6 +8,7 @@ import os import re from collections import defaultdict from copy import copy +import logging try: @@ -408,6 +409,11 @@ main_spec = (Spec( # only for existence of the path, not for it being a directory paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(value.value))), lambda value: 'path does not exist: {0}'.format(value)).optional(), + log_file=Spec().type(str).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(value))), + lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))).optional(), + log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)), + lambda value: 'unknown debugging level {0}'.format(value)).optional(), + log_format=Spec().type(str).optional(), ).context_message('Error while loading common configuration (key {key})'), ext=Spec( vim=Spec( @@ -786,6 +792,11 @@ def check_segment_data_key(key, data, context, echoerr): return True, False, False +# FIXME More checks, limit existing to ThreadedSegment instances only +args_spec = Spec( + interval=Spec().either(Spec().type(float), Spec().type(int)).optional(), + update_first=Spec().type(bool).optional(), +).unknown_spec(Spec(), Spec()).optional().copy highlight_group_spec = Spec().type(unicode).copy segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy segments_spec = Spec().optional().list( @@ -801,8 +812,7 @@ segments_spec = Spec().optional().list( before=Spec().type(unicode).optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), align=Spec().oneof(set('lr')).optional(), - # FIXME Check args - args=Spec().type(dict).optional(), + args=args_spec(), contents=Spec().type(unicode).optional(), highlight_group=Spec().list( highlight_group_spec().re('^(?:(?!:divider$).)+$', @@ -819,8 +829,7 @@ theme_spec = (Spec( Spec( after=Spec().type(unicode).optional(), before=Spec().type(unicode).optional(), - # FIXME Check args - args=Spec().type(dict).optional(), + args=args_spec(), contents=Spec().type(unicode).optional(), ), ).optional().context_message('Error while loading segment data (key {key})'), diff --git a/powerline/renderer.py b/powerline/renderer.py index 4fdcff72..7e6433fe 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -18,9 +18,11 @@ def construct_returned_value(rendered_highlighted, segments, output_raw): class Renderer(object): - def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, **options): + def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, pl, **options): self.__dict__.update(options) self.theme_config = theme_config + theme_kwargs['pl'] = pl + self.pl = pl self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 3cf71dfa..59f230d1 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -66,12 +66,7 @@ class VimRenderer(Renderer): return vim.strwidth(string) def render(self, window_id, winidx, current): - '''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. - ''' + '''Render all segments.''' if current: mode = vim_mode(1) mode = mode_translations.get(mode, mode) diff --git a/powerline/segment.py b/powerline/segment.py index 8f37702c..8f166013 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -64,6 +64,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): contents, contents_func, module = get_segment_info(data, segment) highlight_group = segment_type != 'function' and segment.get('highlight_group') or segment.get('name') return { + 'name': segment.get('name'), 'type': segment_type, 'highlight_group': highlight_group, 'divider_highlight_group': None, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index fdcd8cf5..e58e3d1d 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -18,13 +18,13 @@ from powerline.lib.humanize_bytes import humanize_bytes from collections import namedtuple -def hostname(only_if_ssh=False): +def hostname(pl, only_if_ssh=False): '''Return the current hostname. :param bool only_if_ssh: only return the hostname if currently in an SSH session ''' - if only_if_ssh and not os.environ.get('SSH_CLIENT'): + if only_if_ssh and not pl.environ.get('SSH_CLIENT'): return None return socket.gethostname() @@ -35,8 +35,8 @@ class RepositorySegment(KwThreadedSegment): self.directories = {} @staticmethod - def key(**kwargs): - return os.path.abspath(os.getcwd()) + def key(pl, **kwargs): + return os.path.abspath(pl.getcwd()) def update(self): # .compute_state() is running only in this method, and only in one @@ -82,16 +82,16 @@ class BranchSegment(RepositorySegment): if branch and status_colors: return [{ 'contents': branch, - 'highlight_group': ['branch_dirty' if repository_status() else 'branch_clean', 'branch'], + 'highlight_group': ['branch_dirty' if repository_status(**kwargs) else 'branch_clean', 'branch'], }] else: return branch def startup(self, status_colors=False, **kwargs): - super(BranchSegment, self).startup() + super(BranchSegment, self).startup(**kwargs) if status_colors: self.started_repository_status = True - repository_status.startup() + repository_status.startup(**kwargs) def shutdown(self): if self.started_repository_status: @@ -109,7 +109,7 @@ Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. ''') -def cwd(dir_shorten_len=None, dir_limit_depth=None): +def cwd(pl, dir_shorten_len=None, dir_limit_depth=None): '''Return the current working directory. Returns a segment list to create a breadcrumb-like effect. @@ -126,18 +126,16 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): ''' import re try: - try: - cwd = os.getcwdu() - except AttributeError: - cwd = os.getcwd() + cwd = pl.getcwd() except OSError as e: if e.errno == 2: # user most probably deleted the directory # this happens when removing files from Mercurial repos for example + pl.warn('Current directory not found') cwd = "[not found]" else: raise - home = os.environ.get('HOME') + home = pl.home if home: cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) @@ -160,7 +158,7 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None): return ret -def date(format='%Y-%m-%d', istime=False): +def date(pl, format='%Y-%m-%d', istime=False): '''Return the current date. :param str format: @@ -177,7 +175,7 @@ def date(format='%Y-%m-%d', istime=False): }] -def fuzzy_time(): +def fuzzy_time(pl): '''Display the current time as fuzzy time, e.g. "quarter past six".''' hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'] minute_str = { @@ -241,7 +239,7 @@ class ExternalIpSegment(ThreadedSegment): with self.write_lock: self.ip = ip - def render(self): + def render(self, **kwargs): if not hasattr(self, 'ip'): return None return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}] @@ -373,7 +371,8 @@ class WeatherSegment(ThreadedSegment): self.location = ','.join([location_data['city'], location_data['region_name'], location_data['country_name']]) - except (TypeError, ValueError): + except (TypeError, ValueError) as e: + self.error('Failed to get location: {0}', str(e)) return query_data = { 'q': @@ -389,13 +388,19 @@ class WeatherSegment(ThreadedSegment): condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition_code = int(condition['code']) temp = float(condition['temp']) - except (KeyError, TypeError, ValueError): + except (KeyError, TypeError, ValueError) as e: + self.error('Failed to get weather conditions: {0}', str(e)) return try: icon_names = weather_conditions_codes[condition_code] - except IndexError: - icon_names = (('not_available' if condition_code == 3200 else 'unknown'),) + except IndexError as e: + if condition_code == 3200: + icon_names = ('not_available',) + self.warn('Weather is not available for location {0}', self.location) + else: + icon_names = ('unknown',) + self.error('Unknown condition code: {0}', condition_code) with self.write_lock: self.temp = temp @@ -472,7 +477,7 @@ Also uses ``weather_conditions_{condition}`` for all weather conditions supporte ''') -def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): +def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): '''Return system load average. Highlights using ``system_load_good``, ``system_load_bad`` and @@ -500,6 +505,7 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2): try: cpu_num = cpu_count() except NotImplementedError: + pl.warn('Unable to get CPU count: method is not implemented') return None ret = [] for avg in os.getloadavg(): @@ -539,10 +545,10 @@ try: if data: yield interface, data.bytes_recv, data.bytes_sent - def _get_user(): + def _get_user(pl): return psutil.Process(os.getpid()).username - def cpu_load_percent(measure_interval=.5): + def cpu_load_percent(pl, measure_interval=.5): '''Return the average CPU load as a percentage. Requires the ``psutil`` module. @@ -569,10 +575,10 @@ except ImportError: if x is not None: yield interface, x[0], x[1] - def _get_user(): # NOQA - return os.environ.get('USER', None) + def _get_user(pl): # NOQA + return pl.environ.get('USER', None) - def cpu_load_percent(measure_interval=.5): # NOQA + def cpu_load_percent(pl, measure_interval=.5): # NOQA '''Return the average CPU load as a percentage. Requires the ``psutil`` module. @@ -580,13 +586,14 @@ except ImportError: :param float measure_interval: interval used to measure CPU load (in seconds) ''' + pl.warn('psutil package is not installed, thus CPU load is not available') return None username = False -def user(): +def user(pl): '''Return the current user. Highlights the user with the ``superuser`` if the effective user ID is 0. @@ -595,8 +602,9 @@ def user(): ''' global username if username is False: - username = _get_user() + username = _get_user(pl) if username is None: + pl.warn('Failed to get username') return None try: euid = os.geteuid() @@ -625,7 +633,7 @@ else: @add_divider_highlight_group('background:divider') -def uptime(format='{days}d {hours:02d}h {minutes:02d}m'): +def uptime(pl, format='{days}d {hours:02d}h {minutes:02d}m'): '''Return system uptime. :param str format: @@ -636,7 +644,11 @@ def uptime(format='{days}d {hours:02d}h {minutes:02d}m'): ''' try: seconds = _get_uptime() - except (IOError, NotImplementedError): + except IOError as e: + pl.error('Failed to get uptime: {0}', e) + return None + except NotImplementedError: + pl.warn('Unable to get uptime. You should install psutil package') return None minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) @@ -705,7 +717,10 @@ class NetworkLoadSegment(KwThreadedSegment): t2, b2 = idata['last'] measure_interval = t2 - t1 - if None in (b1, b2) or measure_interval == 0: + if None in (b1, b2): + return None + if measure_interval == 0: + self.error('Measure interval is zero. This should not happen') return None r = [] @@ -762,9 +777,9 @@ Highlight groups used: ``network_load_sent_gradient`` (gradient) or ``network_lo ''') -def virtualenv(): +def virtualenv(pl): '''Return the name of the current Python virtualenv.''' - return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None + return os.path.basename(pl.environ.get('VIRTUAL_ENV', '')) or None _IMAPKey = namedtuple('Key', 'username password server port folder') @@ -774,12 +789,12 @@ class EmailIMAPSegment(KwThreadedSegment): interval = 60 @staticmethod - def key(username, password, server='imap.gmail.com', port=993, folder='INBOX'): + def key(username, password, server='imap.gmail.com', port=993, folder='INBOX', **kwargs): return _IMAPKey(username, password, server, port, folder) - @staticmethod - def compute_state(key): + def compute_state(self, key): if not key.username or not key.password: + self.warn('Username and password are not configured') return None try: import imaplib @@ -789,8 +804,6 @@ class EmailIMAPSegment(KwThreadedSegment): rc, message = mail.status(key.folder, '(UNSEEN)') unread_str = message[0].decode('utf-8') unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) - except socket.gaierror: - return None except imaplib.IMAP4.error as e: unread_count = str(e) return unread_count @@ -842,7 +855,7 @@ class NowPlayingSegment(object): 'stop': '■', } - def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', *args, **kwargs): + def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', **kwargs): player_func = getattr(self, 'player_{0}'.format(player)) stats = { 'state': None, @@ -853,7 +866,7 @@ class NowPlayingSegment(object): 'elapsed': None, 'total': None, } - func_stats = player_func(*args, **kwargs) + func_stats = player_func(**kwargs) if not func_stats: return None stats.update(func_stats) @@ -884,7 +897,7 @@ class NowPlayingSegment(object): def _convert_seconds(seconds): return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) - def player_cmus(self): + def player_cmus(self, pl): '''Return cmus player information. cmus-remote -Q returns data with multi-level information i.e. @@ -922,7 +935,7 @@ class NowPlayingSegment(object): 'total': self._convert_seconds(now_playing.get('duration', 0)), } - def player_mpd(self, host='localhost', port=6600): + def player_mpd(self, pl, host='localhost', port=6600): try: import mpd client = mpd.MPDClient() @@ -954,7 +967,7 @@ class NowPlayingSegment(object): 'total': now_playing[3], } - def player_spotify(self): + def player_spotify(self, pl): try: import dbus except ImportError: @@ -982,7 +995,7 @@ class NowPlayingSegment(object): 'total': self._convert_seconds(info.get('mpris:length') / 1e6), } - def player_rhythmbox(self): + def player_rhythmbox(self, pl): now_playing = self._run_cmd(['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) if not now_playing: return diff --git a/powerline/segments/ipython.py b/powerline/segments/ipython.py index c1b62615..4946c4c3 100644 --- a/powerline/segments/ipython.py +++ b/powerline/segments/ipython.py @@ -3,5 +3,5 @@ from powerline.theme import requires_segment_info @requires_segment_info -def prompt_count(segment_info): +def prompt_count(pl, segment_info): return str(segment_info.prompt_count) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 5bcbe5ac..acad81a7 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -4,7 +4,7 @@ from powerline.theme import requires_segment_info @requires_segment_info -def last_status(segment_info): +def last_status(pl, segment_info): '''Return last exit code. Highlight groups used: ``exit_fail`` @@ -15,7 +15,7 @@ def last_status(segment_info): @requires_segment_info -def last_pipe_status(segment_info): +def last_pipe_status(pl, segment_info): '''Return last pipe status. Highlight groups used: ``exit_fail``, ``exit_success`` diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 94d0ec81..19e7ebfa 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -94,7 +94,7 @@ def window_cached(func): @requires_segment_info -def mode(segment_info, override=None): +def mode(pl, segment_info, override=None): '''Return the current vim mode. :param dict override: @@ -112,7 +112,7 @@ def mode(segment_info, override=None): @requires_segment_info -def modified_indicator(segment_info, text='+'): +def modified_indicator(pl, segment_info, text='+'): '''Return a file modified indicator. :param string text: @@ -122,7 +122,7 @@ def modified_indicator(segment_info, text='+'): @requires_segment_info -def paste_indicator(segment_info, text='PASTE'): +def paste_indicator(pl, segment_info, text='PASTE'): '''Return a paste mode indicator. :param string text: @@ -132,7 +132,7 @@ def paste_indicator(segment_info, text='PASTE'): @requires_segment_info -def readonly_indicator(segment_info, text=''): +def readonly_indicator(pl, segment_info, text=''): '''Return a read-only indicator. :param string text: @@ -142,7 +142,7 @@ def readonly_indicator(segment_info, text=''): @requires_segment_info -def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False): +def file_directory(pl, segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False): '''Return file directory (head component of the file path). :param bool shorten_user: @@ -165,13 +165,15 @@ def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_ho @requires_segment_info -def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): +def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]'): '''Return file name (tail component of the file path). :param bool display_no_file: display a string if the buffer is missing a file name :param str no_file_text: the string to display if the buffer is missing a file name + + Highlight groups used: ``file_name_no_file`` or ``file_name``, ``file_name``. ''' name = segment_info['buffer'].name if not name: @@ -187,7 +189,7 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): @window_cached -def file_size(suffix='B', si_prefix=False): +def file_size(pl, suffix='B', si_prefix=False): '''Return file size in &encoding. :param str suffix: @@ -204,7 +206,7 @@ def file_size(suffix='B', si_prefix=False): @requires_segment_info @add_divider_highlight_group('background:divider') -def file_format(segment_info): +def file_format(pl, segment_info): '''Return file format (i.e. line ending type). :return: file format or None if unknown or missing file format @@ -216,7 +218,7 @@ def file_format(segment_info): @requires_segment_info @add_divider_highlight_group('background:divider') -def file_encoding(segment_info): +def file_encoding(pl, segment_info): '''Return file encoding/character set. :return: file encoding/character set or None if unknown or missing file encoding @@ -228,7 +230,7 @@ def file_encoding(segment_info): @requires_segment_info @add_divider_highlight_group('background:divider') -def file_type(segment_info): +def file_type(pl, segment_info): '''Return file type. :return: file type or None if unknown file type @@ -239,7 +241,7 @@ def file_type(segment_info): @requires_segment_info -def line_percent(segment_info, gradient=False): +def line_percent(pl, segment_info, gradient=False): '''Return the cursor position in the file as a percentage. :param bool gradient: @@ -260,20 +262,20 @@ def line_percent(segment_info, gradient=False): @requires_segment_info -def line_current(segment_info): +def line_current(pl, segment_info): '''Return the current cursor line.''' return str(segment_info['window'].cursor[0]) @requires_segment_info -def col_current(segment_info): +def col_current(pl, segment_info): '''Return the current cursor column. ''' return str(segment_info['window'].cursor[1] + 1) @window_cached -def virtcol_current(): +def virtcol_current(pl): '''Return current visual column with concealed characters ingored Highlight groups used: ``virtcol_current`` or ``col_current``. @@ -282,7 +284,7 @@ def virtcol_current(): 'highlight_group': ['virtcol_current', 'col_current']}] -def modified_buffers(text='+ ', join_str=','): +def modified_buffers(pl, text='+ ', join_str=','): '''Return a comma-separated list of modified buffers. :param str text: @@ -366,16 +368,16 @@ class BranchSegment(RepositorySegment): return [{ 'contents': update_state, - 'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info) else 'branch_clean'] + 'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info, **kwargs) else 'branch_clean'] if status_colors else []) + ['branch'], 'divider_highlight_group': 'branch:divider', }] def startup(self, status_colors=False, **kwargs): - super(BranchSegment, self).startup() + super(BranchSegment, self).startup(**kwargs) if status_colors: self.started_repository_status = True - repository_status.startup() + repository_status.startup(**kwargs) def shutdown(self): if self.started_repository_status: diff --git a/powerline/shell.py b/powerline/shell.py index 388a5e7b..91e7538c 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -19,9 +19,6 @@ class ShellPowerline(Powerline): self.theme_option = mergeargs(args.theme_option) or {} super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once) - def get_segment_info(self): - return self.args - def load_main_config(self): r = super(ShellPowerline, self).load_main_config() if self.args.config: diff --git a/powerline/theme.py b/powerline/theme.py index 9b84ba5f..266b351e 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -24,7 +24,7 @@ def requires_segment_info(func): class Theme(object): - def __init__(self, ext, theme_config, common_config, top_theme_config=None, run_once=False): + def __init__(self, ext, theme_config, common_config, pl, top_theme_config=None, run_once=False): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = { @@ -35,16 +35,22 @@ class Theme(object): 'contents': None, 'highlight': {'fg': False, 'bg': False, 'attr': 0} } + self.pl = pl theme_configs = [theme_config] if top_theme_config: theme_configs.append(top_theme_config) get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module')) for side in ['left', 'right']: - self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) - if not run_once: - for segment in self.segments[side]: + for segment in theme_config['segments'].get(side, []): + segment = get_segment(segment, side) + if not run_once: if segment['startup']: - segment['startup'](**segment['args']) + try: + segment['startup'](pl=pl, **segment['args']) + except Exception as e: + pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) + continue + self.segments[side].append(segment) def shutdown(self): for segments in self.segments.values(): @@ -71,11 +77,17 @@ class Theme(object): parsed_segments = [] for segment in self.segments[side]: if segment['type'] == 'function': - if (hasattr(segment['contents_func'], 'powerline_requires_segment_info') - and segment['contents_func'].powerline_requires_segment_info): - contents = segment['contents_func'](segment_info=segment_info, **segment['args']) - else: - contents = segment['contents_func'](**segment['args']) + self.pl.prefix = segment['name'] + try: + if (hasattr(segment['contents_func'], 'powerline_requires_segment_info') + and segment['contents_func'].powerline_requires_segment_info): + contents = segment['contents_func'](pl=self.pl, segment_info=segment_info, **segment['args']) + else: + contents = segment['contents_func'](pl=self.pl, **segment['args']) + except Exception as e: + self.pl.exception('Exception while computing segment: {0}', str(e)) + continue + if contents is None: continue if isinstance(contents, list): diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 8d72fb4e..a32e4294 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -4,6 +4,28 @@ import sys import os +class Pl(object): + def __init__(self): + self.errors = [] + self.warns = [] + self.debugs = [] + self._cwd = None + self.prefix = None + self.environ = {} + self.home = None + + def getcwd(self): + if isinstance(self._cwd, Exception): + raise self._cwd + else: + return self._cwd + + for meth in ('error', 'warn', 'debug'): + exec ('''def {0}(self, msg, *args, **kwargs): + self.{0}s.append((kwargs.get('prefix') or self.prefix, msg, args, kwargs)) + ''').format(meth) + + class Args(object): theme_option = None config = None @@ -63,50 +85,58 @@ def new_module(name, **kwargs): return module -class ModuleAttrReplace(object): - def __init__(self, module, attr, new): - self.module = module +class AttrReplace(object): + def __init__(self, obj, attr, new): + self.obj = obj self.attr = attr self.new = new def __enter__(self): try: - self.old = getattr(self.module, self.attr) + self.old = getattr(self.obj, self.attr) except AttributeError: pass - setattr(self.module, self.attr, self.new) + setattr(self.obj, self.attr, self.new) def __exit__(self, *args): try: - setattr(self.module, self.attr, self.old) + setattr(self.obj, self.attr, self.old) except AttributeError: - delattr(self.module, self.attr) + delattr(self.obj, self.attr) -replace_module_attr = ModuleAttrReplace +replace_attr = AttrReplace def replace_module_module(module, name, **kwargs): - return replace_module_attr(module, name, new_module(name, **kwargs)) + return replace_attr(module, name, new_module(name, **kwargs)) -class EnvReplace(object): - def __init__(self, name, new): - self.name = name +class ItemReplace(object): + def __init__(self, d, key, new, r=None): + self.key = key self.new = new + self.d = d + self.r = r def __enter__(self): - self.old = os.environ.get(self.name) - os.environ[self.name] = self.new + self.old = self.d.get(self.key) + self.d[self.key] = self.new + return self.r def __exit__(self, *args): if self.old is None: try: - os.environ.pop(self.name) + self.d.pop(self.key) except KeyError: pass else: - os.environ[self.name] = self.old + self.d[self.key] = self.old -replace_env = EnvReplace +def replace_env(key, new, d=None): + r = None + if not d: + r = Pl() + d = r.environ + return ItemReplace(d, key, new, r) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d2c0f5a6..ec6b2917 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -7,7 +7,7 @@ import tests.vim as vim_module import sys import os import json -from tests.lib import Args, urllib_read, replace_module_attr +from tests.lib import Args, urllib_read, replace_attr from tests import TestCase @@ -67,7 +67,7 @@ class TestConfig(TestCase): from imp import reload reload(common) from powerline.shell import ShellPowerline - with replace_module_attr(common, 'urllib_read', urllib_read): + with replace_attr(common, 'urllib_read', urllib_read): powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) powerline.renderer.render() powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) @@ -112,7 +112,7 @@ class TestConfig(TestCase): from imp import reload reload(common) from powerline import Powerline - with replace_module_attr(common, 'urllib_read', urllib_read): + with replace_attr(common, 'urllib_read', urllib_read): Powerline(ext='wm', renderer_module='pango_markup', run_once=True).renderer.render() reload(common) diff --git a/tests/test_segments.py b/tests/test_segments.py index 62978294..478566ab 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -4,7 +4,7 @@ from powerline.segments import shell, common import tests.vim as vim_module import sys import os -from tests.lib import Args, urllib_read, replace_module_attr, new_module, replace_module_module, replace_env +from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl from tests import TestCase @@ -13,14 +13,16 @@ vim = None class TestShell(TestCase): def test_last_status(self): - self.assertEqual(shell.last_status(Args(last_exit_code=10)), + pl = Pl() + self.assertEqual(shell.last_status(pl=pl, segment_info=Args(last_exit_code=10)), [{'contents': '10', 'highlight_group': 'exit_fail'}]) - self.assertEqual(shell.last_status(Args(last_exit_code=None)), None) + self.assertEqual(shell.last_status(pl=pl, segment_info=Args(last_exit_code=None)), None) def test_last_pipe_status(self): - self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[])), None) - self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[0, 0, 0])), None) - self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[0, 2, 0])), + pl = Pl() + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[])), None) + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[0, 0, 0])), None) + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[0, 2, 0])), [{'contents': '0', 'highlight_group': 'exit_success'}, {'contents': '2', 'highlight_group': 'exit_fail'}, {'contents': '0', 'highlight_group': 'exit_success'}]) @@ -28,220 +30,227 @@ class TestShell(TestCase): class TestCommon(TestCase): def test_hostname(self): - with replace_env('SSH_CLIENT', '192.168.0.12 40921 22'): + with replace_env('SSH_CLIENT', '192.168.0.12 40921 22') as pl: with replace_module_module(common, 'socket', gethostname=lambda: 'abc'): - self.assertEqual(common.hostname(), 'abc') - self.assertEqual(common.hostname(only_if_ssh=True), 'abc') - os.environ.pop('SSH_CLIENT') - self.assertEqual(common.hostname(), 'abc') - self.assertEqual(common.hostname(only_if_ssh=True), None) + self.assertEqual(common.hostname(pl=pl), 'abc') + self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), 'abc') + pl.environ.pop('SSH_CLIENT') + self.assertEqual(common.hostname(pl=pl), 'abc') + self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), None) def test_user(self): - new_os = new_module('os', environ={'USER': 'def'}, getpid=lambda: 1) + new_os = new_module('os', getpid=lambda: 1) new_psutil = new_module('psutil', Process=lambda pid: Args(username='def')) - with replace_module_attr(common, 'os', new_os): - with replace_module_attr(common, 'psutil', new_psutil): - self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) - new_os.geteuid = lambda: 1 - self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}]) - new_os.geteuid = lambda: 0 - self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) + with replace_env('USER', 'def') as pl: + with replace_attr(common, 'os', new_os): + with replace_attr(common, 'psutil', new_psutil): + self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}]) + new_os.geteuid = lambda: 1 + self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}]) + new_os.geteuid = lambda: 0 + self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): - with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): - self.assertEqual(common.branch(status_colors=False), 'tests') - self.assertEqual(common.branch(status_colors=True), + pl = Pl() + pl._cwd = os.getcwd() + with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): + self.assertEqual(common.branch(pl=pl, status_colors=False), 'tests') + self.assertEqual(common.branch(pl=pl, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) - with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): - self.assertEqual(common.branch(status_colors=False), 'tests') - self.assertEqual(common.branch(status_colors=True), + with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): + self.assertEqual(common.branch(pl=pl, status_colors=False), 'tests') + self.assertEqual(common.branch(pl=pl, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) - self.assertEqual(common.branch(), 'tests') - with replace_module_attr(common, 'guess', lambda path: None): - self.assertEqual(common.branch(), None) + self.assertEqual(common.branch(pl=pl), 'tests') + with replace_attr(common, 'guess', lambda path: None): + self.assertEqual(common.branch(pl=pl), None) def test_cwd(self): - new_os = new_module('os', path=os.path, environ={}, sep='/') - new_os.getcwd = lambda: '/abc/def/ghi/foo/bar' - with replace_module_attr(common, 'os', new_os): - self.assertEqual(common.cwd(), + new_os = new_module('os', path=os.path, sep='/') + pl = Pl() + with replace_attr(common, 'os', new_os): + pl._cwd = '/abc/def/ghi/foo/bar' + self.assertEqual(common.cwd(pl=pl), [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'def', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - new_os.getcwdu = lambda: '/abc/def/ghi/foo/bar' - self.assertEqual(common.cwd(), - [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'def', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - new_os.environ['HOME'] = '/abc/def/ghi' - self.assertEqual(common.cwd(), + pl.home = '/abc/def/ghi' + self.assertEqual(common.cwd(pl=pl), [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(dir_limit_depth=3), + self.assertEqual(common.cwd(pl=pl, dir_limit_depth=3), [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(dir_limit_depth=1), + self.assertEqual(common.cwd(pl=pl, dir_limit_depth=1), [{'contents': '⋯', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2), + self.assertEqual(common.cwd(pl=pl, dir_limit_depth=2, dir_shorten_len=2), [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'fo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) ose = OSError() ose.errno = 2 - - def raises(exc): - raise exc - - new_os.getcwdu = lambda: raises(ose) - self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2), + pl._cwd = ose + self.assertEqual(common.cwd(pl=pl, dir_limit_depth=2, dir_shorten_len=2), [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - new_os.getcwdu = lambda: raises(OSError()) - self.assertRaises(OSError, common.cwd, tuple(), {'dir_limit_depth': 2, 'dir_shorten_len': 2}) - new_os.getcwdu = lambda: raises(ValueError()) - self.assertRaises(ValueError, common.cwd, tuple(), {'dir_limit_depth': 2, 'dir_shorten_len': 2}) + pl._cwd = OSError() + self.assertRaises(OSError, common.cwd, pl=pl, dir_limit_depth=2, dir_shorten_len=2) + pl._cwd = ValueError() + self.assertRaises(ValueError, common.cwd, pl=pl, dir_limit_depth=2, dir_shorten_len=2) def test_date(self): - with replace_module_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))): - self.assertEqual(common.date(), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}]) - self.assertEqual(common.date(format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) + pl = Pl() + with replace_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))): + self.assertEqual(common.date(pl=pl), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}]) + self.assertEqual(common.date(pl=pl, format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) def test_fuzzy_time(self): time = Args(hour=0, minute=45) - with replace_module_attr(common, 'datetime', Args(now=lambda: time)): - self.assertEqual(common.fuzzy_time(), 'quarter to one') + pl = Pl() + with replace_attr(common, 'datetime', Args(now=lambda: time)): + self.assertEqual(common.fuzzy_time(pl=pl), 'quarter to one') time.hour = 23 time.minute = 59 - self.assertEqual(common.fuzzy_time(), 'round about midnight') + self.assertEqual(common.fuzzy_time(pl=pl), 'round about midnight') time.minute = 33 - self.assertEqual(common.fuzzy_time(), 'twenty-five to twelve') + self.assertEqual(common.fuzzy_time(pl=pl), 'twenty-five to twelve') time.minute = 60 - self.assertEqual(common.fuzzy_time(), 'twelve o\'clock') + self.assertEqual(common.fuzzy_time(pl=pl), 'twelve o\'clock') def test_external_ip(self): - with replace_module_attr(common, 'urllib_read', urllib_read): - self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}]) + pl = Pl() + with replace_attr(common, 'urllib_read', urllib_read): + self.assertEqual(common.external_ip(pl=pl), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}]) def test_uptime(self): - with replace_module_attr(common, '_get_uptime', lambda: 65536): - self.assertEqual(common.uptime(), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}]) + pl = Pl() + with replace_attr(common, '_get_uptime', lambda: 65536): + self.assertEqual(common.uptime(pl=pl), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}]) def _get_uptime(): raise NotImplementedError - with replace_module_attr(common, '_get_uptime', _get_uptime): - self.assertEqual(common.uptime(), None) + with replace_attr(common, '_get_uptime', _get_uptime): + self.assertEqual(common.uptime(pl=pl), None) def test_weather(self): - with replace_module_attr(common, 'urllib_read', urllib_read): - self.assertEqual(common.weather(), [ + pl = Pl() + with replace_attr(common, 'urllib_read', urllib_read): + self.assertEqual(common.weather(pl=pl), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) - self.assertEqual(common.weather(temp_coldest=0, temp_hottest=100), [ + self.assertEqual(common.weather(pl=pl, temp_coldest=0, temp_hottest=100), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} ]) - self.assertEqual(common.weather(temp_coldest=-100, temp_hottest=-50), [ + self.assertEqual(common.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} ]) - self.assertEqual(common.weather(icons={'cloudy': 'o'}), [ + self.assertEqual(common.weather(pl=pl, icons={'cloudy': 'o'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) - self.assertEqual(common.weather(icons={'partly_cloudy_day': 'x'}), [ + self.assertEqual(common.weather(pl=pl, icons={'partly_cloudy_day': 'x'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) - self.assertEqual(common.weather(unit='F'), [ + self.assertEqual(common.weather(pl=pl, unit='F'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} ]) - self.assertEqual(common.weather(unit='K'), [ + self.assertEqual(common.weather(pl=pl, unit='K'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} ]) - self.assertEqual(common.weather(temp_format='{temp:.1e}C'), [ + self.assertEqual(common.weather(pl=pl, temp_format='{temp:.1e}C'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} ]) def test_system_load(self): + pl = Pl() with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): - with replace_module_attr(common, 'cpu_count', lambda: 2): - self.assertEqual(common.system_load(), + with replace_attr(common, 'cpu_count', lambda: 2): + self.assertEqual(common.system_load(pl=pl), [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) - self.assertEqual(common.system_load(format='{avg:.0f}', threshold_good=0, threshold_bad=1), + self.assertEqual(common.system_load(pl=pl, format='{avg:.0f}', threshold_good=0, threshold_bad=1), [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) def test_cpu_load_percent(self): + pl = Pl() with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3): - self.assertEqual(common.cpu_load_percent(), '52%') + self.assertEqual(common.cpu_load_percent(pl=pl), '52%') def test_network_load(self): - def _get_bytes(interface): + def gb(interface): return None - with replace_module_attr(common, '_get_bytes', _get_bytes): - self.assertEqual(common.network_load(), None) - l = [0, 0] - def _get_bytes2(interface): - l[0] += 1200 - l[1] += 2400 - return tuple(l) + f = [gb] + def _get_bytes(interface): + return f[0](interface) + + pl = Pl() + + with replace_attr(common, '_get_bytes', _get_bytes): + common.network_load.startup(pl=pl) + self.assertEqual(common.network_load(pl=pl), None) + common.network_load.sleep(0) + self.assertEqual(common.network_load(pl=pl), None) + + l = [0, 0] + + def gb2(interface): + l[0] += 1200 + l[1] += 2400 + return tuple(l) + f[0] = gb2 - from imp import reload - reload(common) - with replace_module_attr(common, '_get_bytes', _get_bytes2): - common.network_load.startup() common.network_load.sleep(0) common.network_load.sleep(0) - self.assertEqual(common.network_load(), [ + self.assertEqual(common.network_load(pl=pl), [ {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}'), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', suffix='bps'), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', si_prefix=True), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', recv_max=0), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) class ApproxEqual(object): def __eq__(self, i): return abs(i - 50.0) < 1 - self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', sent_max=4800), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, ]) def test_virtualenv(self): - with replace_env('VIRTUAL_ENV', '/abc/def/ghi'): - self.assertEqual(common.virtualenv(), 'ghi') - os.environ.pop('VIRTUAL_ENV') - self.assertEqual(common.virtualenv(), None) + with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as pl: + self.assertEqual(common.virtualenv(pl=pl), 'ghi') + pl.environ.pop('VIRTUAL_ENV') + self.assertEqual(common.virtualenv(pl=pl), None) def test_email_imap_alert(self): # TODO @@ -254,131 +263,145 @@ class TestCommon(TestCase): class TestVim(TestCase): def test_mode(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.mode(segment_info=segment_info), 'NORMAL') - self.assertEqual(vim.mode(segment_info=segment_info, override={'i': 'INS'}), 'NORMAL') - self.assertEqual(vim.mode(segment_info=segment_info, override={'n': 'NORM'}), 'NORM') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'NORMAL') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'i': 'INS'}), 'NORMAL') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'n': 'NORM'}), 'NORM') with vim_module._with('mode', 'i') as segment_info: - self.assertEqual(vim.mode(segment_info=segment_info), 'INSERT') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'INSERT') with vim_module._with('mode', chr(ord('V') - 0x40)) as segment_info: - self.assertEqual(vim.mode(segment_info=segment_info), 'V·BLCK') - self.assertEqual(vim.mode(segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'V·BLCK') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') def test_modified_indicator(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.modified_indicator(segment_info=segment_info), None) + self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), None) segment_info['buffer'][0] = 'abc' try: - self.assertEqual(vim.modified_indicator(segment_info=segment_info), '+') - self.assertEqual(vim.modified_indicator(segment_info=segment_info, text='-'), '-') + self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), '+') + self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info, text='-'), '-') finally: vim_module._bw(segment_info['bufnr']) def test_paste_indicator(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.paste_indicator(segment_info=segment_info), None) + self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info), None) with vim_module._with('options', paste=1): - self.assertEqual(vim.paste_indicator(segment_info=segment_info), 'PASTE') - self.assertEqual(vim.paste_indicator(segment_info=segment_info, text='P'), 'P') + self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info), 'PASTE') + self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info, text='P'), 'P') def test_readonly_indicator(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.readonly_indicator(segment_info=segment_info), None) + self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), None) with vim_module._with('bufoptions', readonly=1): - self.assertEqual(vim.readonly_indicator(segment_info=segment_info), '') - self.assertEqual(vim.readonly_indicator(segment_info=segment_info, text='L'), 'L') + self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), '') + self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info, text='L'), 'L') def test_file_directory(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_directory(segment_info=segment_info), None) - with replace_env('HOME', '/home/foo'): + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), None) + with replace_env('HOME', '/home/foo', os.environ): with vim_module._with('buffer', '/tmp/abc') as segment_info: - self.assertEqual(vim.file_directory(segment_info=segment_info), '/tmp/') + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/') os.environ['HOME'] = '/tmp' - self.assertEqual(vim.file_directory(segment_info=segment_info), '~/') + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '~/') def test_file_name(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_name(segment_info=segment_info), None) - self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True), + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), None) + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True), [{'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']}]) - self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True, no_file_text='X'), + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True, no_file_text='X'), [{'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']}]) with vim_module._with('buffer', '/tmp/abc') as segment_info: - self.assertEqual(vim.file_name(segment_info=segment_info), 'abc') + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), 'abc') with vim_module._with('buffer', '/tmp/’’') as segment_info: - self.assertEqual(vim.file_name(segment_info=segment_info), '’’') + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), '’’') def test_file_size(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_size(segment_info=segment_info), '0 B') + self.assertEqual(vim.file_size(pl=pl, segment_info=segment_info), '0 B') with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info: - self.assertEqual(vim.file_size(segment_info=segment_info), '0 B') + self.assertEqual(vim.file_size(pl=pl, segment_info=segment_info), '0 B') def test_file_opts(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_format(segment_info=segment_info), + self.assertEqual(vim.file_format(pl=pl, segment_info=segment_info), [{'divider_highlight_group': 'background:divider', 'contents': 'unix'}]) - self.assertEqual(vim.file_encoding(segment_info=segment_info), + self.assertEqual(vim.file_encoding(pl=pl, segment_info=segment_info), [{'divider_highlight_group': 'background:divider', 'contents': 'utf-8'}]) - self.assertEqual(vim.file_type(segment_info=segment_info), None) + self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), None) with vim_module._with('bufoptions', filetype='python'): - self.assertEqual(vim.file_type(segment_info=segment_info), + self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), [{'divider_highlight_group': 'background:divider', 'contents': 'python'}]) def test_line_percent(self): + pl = Pl() segment_info = vim_module._get_segment_info() segment_info['buffer'][0:-1] = [str(i) for i in range(100)] try: - self.assertEqual(vim.line_percent(segment_info=segment_info), '1') + self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '1') vim_module._set_cursor(50, 0) - self.assertEqual(vim.line_percent(segment_info=segment_info), '50') - self.assertEqual(vim.line_percent(segment_info=segment_info, gradient=True), + self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '50') + self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info, gradient=True), [{'contents': '50', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 50 * 100.0 / 101}]) finally: vim_module._bw(segment_info['bufnr']) def test_cursor_current(self): + pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.line_current(segment_info=segment_info), '1') - self.assertEqual(vim.col_current(segment_info=segment_info), '1') - self.assertEqual(vim.virtcol_current(segment_info=segment_info), + self.assertEqual(vim.line_current(pl=pl, segment_info=segment_info), '1') + self.assertEqual(vim.col_current(pl=pl, segment_info=segment_info), '1') + self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info), [{'highlight_group': ['virtcol_current', 'col_current'], 'contents': '1'}]) def test_modified_buffers(self): - self.assertEqual(vim.modified_buffers(), None) + pl = Pl() + self.assertEqual(vim.modified_buffers(pl=pl), None) def test_branch(self): + pl = Pl() with vim_module._with('buffer', '/foo') as segment_info: - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): - self.assertEqual(vim.branch(segment_info=segment_info), + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True), + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): - self.assertEqual(vim.branch(segment_info=segment_info), + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True), + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) def test_file_vcs_status(self): + pl = Pl() with vim_module._with('buffer', '/foo') as segment_info: - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): - self.assertEqual(vim.file_vcs_status(segment_info=segment_info), + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): + self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), [{'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'}]) - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)): - self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None) + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)): + self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None) with vim_module._with('buffer', '/bar') as segment_info: with vim_module._with('bufoptions', buftype='nofile'): - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): - self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None) + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): + self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None) def test_repository_status(self): + pl = Pl() segment_info = vim_module._get_segment_info() - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): - self.assertEqual(vim.repository_status(segment_info=segment_info), None) - with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): - self.assertEqual(vim.repository_status(segment_info=segment_info), 'DU') + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): + self.assertEqual(vim.repository_status(pl=pl, segment_info=segment_info), None) + with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): + self.assertEqual(vim.repository_status(pl=pl, segment_info=segment_info), 'DU') old_cwd = None From 8a51d99389f87c9b6a2fc896f8a24537a854d783 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Mar 2013 18:55:43 +0400 Subject: [PATCH 0536/1472] Some fixes for zsh, remove some exceptions handling Those exceptions are now handled and logged at top level, thus no need to handle them in segment. Ref #330, it is now fixed for zsh/zpython Note: do not use zsh/zpython + python3, it does not work properly, even though this changeset contains fixes for it as well Fixes #360 --- powerline/bindings/zsh/__init__.py | 24 +++++++++++--- powerline/lib/threaded.py | 2 +- powerline/segments/common.py | 51 +++++++++++------------------- powerline/shell.py | 4 +-- tests/test_segments.py | 9 +++--- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 295cd699..ff82a5af 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -45,24 +45,31 @@ class Args(object): return None +def string(s): + if type(s) is bytes: + return s.decode('utf-8', errors='replace') + else: + return str(s) + + class Environment(object): @staticmethod def __getitem__(key): try: - return zsh.getvalue(key) + return string(zsh.getvalue(key)) except IndexError as e: raise KeyError(*e.args) @staticmethod def get(key, default=None): try: - return zsh.getvalue(key) + return string(zsh.getvalue(key)) except IndexError: return default class Prompt(object): - __slots__ = ('render', 'side', 'savedpsvar', 'savedps') + __slots__ = ('render', 'side', 'savedpsvar', 'savedps', 'args') def __init__(self, powerline, side, savedpsvar=None, savedps=None): self.render = powerline.renderer.render @@ -72,7 +79,13 @@ class Prompt(object): self.args = powerline.args def __str__(self): - return self.render(width=zsh.columns(), side=self.side, segment_info=args).encode('utf-8') + r = self.render(width=zsh.columns(), side=self.side, segment_info=self.args) + if type(r) is not str: + if type(r) is bytes: + return r.decode('utf-8') + else: + return r.encode('utf-8') + return r def __del__(self): if self.savedps: @@ -88,6 +101,7 @@ def set_prompt(powerline, psvar, side): def setup(): - powerline = ShellPowerline(Args(), environ=Environment(), getcwd=lambda: zsh.getvalue('PWD')) + environ = Environment() + powerline = ShellPowerline(Args(), environ=environ, getcwd=lambda: environ['PWD']) set_prompt(powerline, 'PS1', 'left') set_prompt(powerline, 'RPS1', 'right') diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 1b74d257..73fdb33f 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -140,7 +140,7 @@ class KwThreadedSegment(ThreadedSegment): try: updates[key] = (last_query_time, self.compute_state(key)) except Exception as e: - self.error('Exception while computing state for {0}: {1}', repr(key), str(e)) + self.exception('Exception while computing state for {0}: {1}', repr(key), str(e)) else: removes.append(key) with self.write_lock: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e58e3d1d..f5d8af6d 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -366,14 +366,10 @@ class WeatherSegment(ThreadedSegment): # Do not lock attribute assignments in this branch: they are used # only in .update() if not self.location: - try: - location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip())) - self.location = ','.join([location_data['city'], - location_data['region_name'], - location_data['country_name']]) - except (TypeError, ValueError) as e: - self.error('Failed to get location: {0}', str(e)) - return + location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip())) + self.location = ','.join([location_data['city'], + location_data['region_name'], + location_data['country_name']]) query_data = { 'q': 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' @@ -382,15 +378,14 @@ class WeatherSegment(ThreadedSegment): } self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data) - try: - raw_response = urllib_read(self.url) - response = json.loads(raw_response) - condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] - condition_code = int(condition['code']) - temp = float(condition['temp']) - except (KeyError, TypeError, ValueError) as e: - self.error('Failed to get weather conditions: {0}', str(e)) + raw_response = urllib_read(self.url) + if not raw_response: + self.error('Failed to get response') return + response = json.loads(raw_response) + condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] + condition_code = int(condition['code']) + temp = float(condition['temp']) try: icon_names = weather_conditions_codes[condition_code] @@ -560,14 +555,11 @@ try: return '{0}%'.format(cpu_percent) except ImportError: def _get_bytes(interface): # NOQA - try: - with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: - rx = int(file_obj.read()) - with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: - tx = int(file_obj.read()) - return (rx, tx) - except IOError: - return None + with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: + rx = int(file_obj.read()) + with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: + tx = int(file_obj.read()) + return (rx, tx) def _get_interfaces(): for interface in os.listdir('/sys/class/net'): @@ -591,6 +583,8 @@ except ImportError: username = False +# os.geteuid is not available on windows +_geteuid = getattr(os, 'geteuid', lambda: 1) def user(pl): @@ -606,11 +600,7 @@ def user(pl): if username is None: pl.warn('Failed to get username') return None - try: - euid = os.geteuid() - except AttributeError: - # os.geteuid is not available on windows - euid = 1 + euid = _geteuid() return [{ 'contents': username, 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], @@ -644,9 +634,6 @@ def uptime(pl, format='{days}d {hours:02d}h {minutes:02d}m'): ''' try: seconds = _get_uptime() - except IOError as e: - pl.error('Failed to get uptime: {0}', e) - return None except NotImplementedError: pl.warn('Unable to get uptime. You should install psutil package') return None diff --git a/powerline/shell.py b/powerline/shell.py index 91e7538c..4d54de0a 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -14,10 +14,10 @@ def mergeargs(argvalue): class ShellPowerline(Powerline): - def __init__(self, args, run_once=False): + def __init__(self, args, run_once=False, **kwargs): self.args = args self.theme_option = mergeargs(args.theme_option) or {} - super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once) + super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once, **kwargs) def load_main_config(self): r = super(ShellPowerline, self).load_main_config() diff --git a/tests/test_segments.py b/tests/test_segments.py index 478566ab..0278512d 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -44,11 +44,10 @@ class TestCommon(TestCase): with replace_env('USER', 'def') as pl: with replace_attr(common, 'os', new_os): with replace_attr(common, 'psutil', new_psutil): - self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}]) - new_os.geteuid = lambda: 1 - self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}]) - new_os.geteuid = lambda: 0 - self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) + with replace_attr(common, '_geteuid', lambda: 5): + self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}]) + with replace_attr(common, '_geteuid', lambda: 0): + self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) def test_branch(self): pl = Pl() From 7370876c35c1dd543706711bd9a828398e00b549 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Mar 2013 19:03:43 +0400 Subject: [PATCH 0537/1472] Make scripts/powerline use os.environ['PWD'] for .getcwd() if present Closes #330 --- scripts/powerline | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/powerline b/scripts/powerline index ffae9863..0926a760 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -13,7 +13,10 @@ except ImportError: if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() - powerline = ShellPowerline(args, run_once=True) + kwargs = {} + if 'PWD' in os.environ: + kwargs['getcwd'] = lambda: os.environ['PWD'] + powerline = ShellPowerline(args, run_once=True, **kwargs) rendered = powerline.renderer.render(width=args.width, side=args.side, segment_info=args) try: sys.stdout.write(rendered) From 63a50ad2000e48f7183fc26506615aacfb898827 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Mar 2013 23:44:00 +0400 Subject: [PATCH 0538/1472] Remove run_once from ShellPowerline, fix update_first run_once will be passed to Powerline as a part of **kwargs --- powerline/lib/threaded.py | 1 - powerline/shell.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 73fdb33f..e613a3a8 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -35,7 +35,6 @@ class ThreadedSegment(object): # # If running once .update() is called in __call__. if update_first and self.update_first: - self.update_first = False self.update() self.start() diff --git a/powerline/shell.py b/powerline/shell.py index 4d54de0a..4e8aa789 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -14,10 +14,10 @@ def mergeargs(argvalue): class ShellPowerline(Powerline): - def __init__(self, args, run_once=False, **kwargs): + def __init__(self, args, **kwargs): self.args = args self.theme_option = mergeargs(args.theme_option) or {} - super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once, **kwargs) + super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, **kwargs) def load_main_config(self): r = super(ShellPowerline, self).load_main_config() From ca2f0cc873316661e75d691ce3922d5bdfbfc992 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 18:45:20 +0400 Subject: [PATCH 0539/1472] Do not wait for update lock, exit --- powerline/lib/threaded.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index e613a3a8..ca42c05b 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -23,9 +23,9 @@ class ThreadedSegment(object): self.did_set_interval = False self.thread = None - def __call__(self, update_first=True, **kwargs): + def __call__(self, pl, update_first=True, **kwargs): if self.run_once: - self.pl = kwargs['pl'] + self.pl = pl self.set_state(**kwargs) self.update() elif not self.is_alive(): @@ -39,7 +39,7 @@ class ThreadedSegment(object): self.start() with self.write_lock: - return self.render(update_first=update_first, **kwargs) + return self.render(update_first=update_first, pl=pl, **kwargs) def is_alive(self): return self.thread and self.thread.is_alive() @@ -56,11 +56,19 @@ class ThreadedSegment(object): while self.keep_going: start_time = monotonic() - with self.update_lock: - try: - self.update() - except Exception as e: - self.error('Exception while updating: {0}', str(e)) + try: + if self.update_lock.acquire(False): + try: + self.update() + except Exception as e: + self.error('Exception while updating: {0}', str(e)) + else: + return + finally: + # Release lock in any case. If it is not locked in this thread, + # it was done in main thread in .shutdown method, and the lock + # will never be released. + self.update_lock.release() self.sleep(monotonic() - start_time) From 14d957bdf94241b3042fd215ed9fe451c805ece7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 19:35:12 +0400 Subject: [PATCH 0540/1472] Skip crashed segments --- powerline/lib/threaded.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index ca42c05b..147de23b 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -22,6 +22,8 @@ class ThreadedSegment(object): self.run_once = True self.did_set_interval = False self.thread = None + self.skip = False + self.crashed_value = None def __call__(self, pl, update_first=True, **kwargs): if self.run_once: @@ -38,6 +40,8 @@ class ThreadedSegment(object): self.update() self.start() + if self.skip: + return self.crashed_value with self.write_lock: return self.render(update_first=update_first, pl=pl, **kwargs) @@ -62,6 +66,9 @@ class ThreadedSegment(object): self.update() except Exception as e: self.error('Exception while updating: {0}', str(e)) + self.skip = True + else: + self.skip = False else: return finally: @@ -122,6 +129,7 @@ class KwThreadedSegment(ThreadedSegment): def __init__(self): super(KwThreadedSegment, self).__init__() self.queries = {} + self.crashed = set() @staticmethod def key(**kwargs): @@ -129,12 +137,16 @@ class KwThreadedSegment(ThreadedSegment): def render(self, update_first, **kwargs): key = self.key(**kwargs) + if key in self.crashed: + return self.crashed_value + try: update_state = self.queries[key][1] except KeyError: # Allow only to forbid to compute missing values: in either user # configuration or in subclasses. update_state = self.compute_state(key) if update_first and self.update_first or self.run_once else None + # No locks: render method is already running with write_lock acquired. self.queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) @@ -148,10 +160,13 @@ class KwThreadedSegment(ThreadedSegment): updates[key] = (last_query_time, self.compute_state(key)) except Exception as e: self.exception('Exception while computing state for {0}: {1}', repr(key), str(e)) + with self.write_lock: + self.crashed.add(key) else: removes.append(key) with self.write_lock: self.queries.update(updates) + self.crashed -= set(updates) for key in removes: self.queries.pop(key) From 1351207462b9f0ac7f4c49578e4baaf58aada07a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 19:59:56 +0400 Subject: [PATCH 0541/1472] Some fixes for flake8 --- docs/source/powerline_autodoc.py | 4 ++-- powerline/__init__.py | 3 ++- powerline/bindings/ipython/pre_0_11.py | 3 ++- powerline/bindings/vim/__init__.py | 1 + powerline/lint/markedjson/scanner.py | 2 +- powerline/renderers/vim.py | 2 +- powerline/segments/common.py | 5 +++-- powerline/segments/ipython.py | 1 + tests/lib/__init__.py | 1 - tests/test_segments.py | 3 +++ tests/vim.py | 1 + tools/generate_gradients.py | 2 +- 12 files changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 084c8547..38f0a4cb 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -1,13 +1,13 @@ from sphinx.ext import autodoc from sphinx.util.inspect import getargspec -from inspect import ArgSpec, getargspec, formatargspec +from inspect import ArgSpec, formatargspec from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from itertools import count try: from __builtin__ import unicode except ImportError: - unicode = lambda s, enc: s + unicode = lambda s, enc: s # NOQA def formatvalue(val): diff --git a/powerline/__init__.py b/powerline/__init__.py index cd21a94e..f8d0043f 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -38,7 +38,7 @@ class PowerlineState(object): def _log(self, attr, msg, *args, **kwargs): prefix = kwargs.get('prefix') or self.prefix msg = ((prefix + ':') if prefix else '') + msg.format(*args, **kwargs) - key = attr+':'+prefix + key = attr + ':' + prefix if msg != self.last_msgs.get(key): getattr(self.logger, attr)(msg) self.last_msgs[key] = msg @@ -144,6 +144,7 @@ class Powerline(object): level = getattr(logging, common_config.get('log_level', 'WARNING')) handler = self.get_log_handler(common_config) handler.setLevel(level) + handler.setFormatter(formatter) logger = logging.getLogger('powerline') logger.setLevel(level) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 21387505..64f9b206 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet from powerline.ipython import IpythonPowerline -from IPython.Prompts import BasePrompt, Prompt1 +from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython from IPython.ipapi import TryNext @@ -88,6 +88,7 @@ class PowerlinePrompt1(PowerlinePrompt): return RewriteResult(self.powerline.renderer.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) + (' ' * self.nrspaces)) + class PowerlinePromptOut(PowerlinePrompt): powerline_prompt_type = 'out' diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 59a400f7..3d7f0c08 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -56,6 +56,7 @@ if sys.version_info < (3,) or not hasattr(vim, 'bindeval'): getbufvar = vim_get_func('getbufvar') else: _getbufvar = vim_get_func('getbufvar') + def getbufvar(*args): r = _getbufvar(*args) if type(r) is bytes: diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index 005b8555..2183f651 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -27,7 +27,7 @@ class ScannerError(MarkedError): try: from __builtin__ import unicode except ImportError: - unicode = str + unicode = str # NOQA class SimpleKey: diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 59f230d1..63900eb4 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -61,7 +61,7 @@ class VimRenderer(Renderer): # renderer return vim.strwidth(string.encode('utf-8')) else: - @staticmethod + @staticmethod # NOQA def strwidth(string): return vim.strwidth(string) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f5d8af6d..3537b687 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -389,7 +389,7 @@ class WeatherSegment(ThreadedSegment): try: icon_names = weather_conditions_codes[condition_code] - except IndexError as e: + except IndexError: if condition_code == 3200: icon_names = ('not_available',) self.warn('Weather is not available for location {0}', self.location) @@ -561,7 +561,7 @@ except ImportError: tx = int(file_obj.read()) return (rx, tx) - def _get_interfaces(): + def _get_interfaces(): # NOQA for interface in os.listdir('/sys/class/net'): x = _get_bytes(interface) if x is not None: @@ -613,6 +613,7 @@ if os.path.exists('/proc/uptime'): return int(float(f.readline().split()[0])) elif 'psutil' in globals(): from time import time + def _get_uptime(): # NOQA # psutil.BOOT_TIME is not subject to clock adjustments, but time() is. # Thus it is a fallback to /proc/uptime reading and not the reverse. diff --git a/powerline/segments/ipython.py b/powerline/segments/ipython.py index 4946c4c3..b9fa8fba 100644 --- a/powerline/segments/ipython.py +++ b/powerline/segments/ipython.py @@ -2,6 +2,7 @@ from powerline.theme import requires_segment_info + @requires_segment_info def prompt_count(pl, segment_info): return str(segment_info.prompt_count) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index a32e4294..7e47c7e2 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,7 +1,6 @@ # vim:fileencoding=utf-8:noet import imp import sys -import os class Pl(object): diff --git a/tests/test_segments.py b/tests/test_segments.py index 0278512d..6ef9d459 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -196,6 +196,7 @@ class TestCommon(TestCase): return None f = [gb] + def _get_bytes(interface): return f[0](interface) @@ -237,9 +238,11 @@ class TestCommon(TestCase): {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) + class ApproxEqual(object): def __eq__(self, i): return abs(i - 50.0) < 1 + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, diff --git a/tests/vim.py b/tests/vim.py index 5c28d708..3b046590 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -45,6 +45,7 @@ def _construct_result(r): def _str_func(func): from functools import wraps + @wraps(func) def f(*args, **kwargs): return _construct_result(func(*args, **kwargs)) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 1fd4a137..e4d3d058 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -7,7 +7,7 @@ from itertools import groupby try: from __builtin__ import unicode except ImportError: - unicode = str + unicode = str # NOQA if len(sys.argv) == 1 or sys.argv[1] == '--help': From 6c495374a033c4a74391ca7f5865997a0f6f106f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 20:03:09 +0400 Subject: [PATCH 0542/1472] Fix exec call for non-python-2.7 --- tests/lib/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 7e47c7e2..03528cdb 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -20,9 +20,8 @@ class Pl(object): return self._cwd for meth in ('error', 'warn', 'debug'): - exec ('''def {0}(self, msg, *args, **kwargs): - self.{0}s.append((kwargs.get('prefix') or self.prefix, msg, args, kwargs)) - ''').format(meth) + exec (('def {0}(self, msg, *args, **kwargs):\n' + ' self.{0}s.append((kwargs.get("prefix") or self.prefix, msg, args, kwargs))\n').format(meth)) class Args(object): From 7764bcc6a12d4487d81df3a191ecf4a910b65636 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 20:21:21 +0400 Subject: [PATCH 0543/1472] Fix logging format Fixes #361 --- powerline/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index f8d0043f..a3a80420 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -138,7 +138,7 @@ class Powerline(object): # Create logger if not logger: - log_format = common_config.get('format', '%(asctime)s:%(level)s:%(message)s') + log_format = common_config.get('format', '%(asctime)s:%(levelname)s:%(message)s') formatter = logging.Formatter(log_format) level = getattr(logging, common_config.get('log_level', 'WARNING')) From d27c720279f7e94663119b6cfab9cd342b9330f6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 20:36:28 +0400 Subject: [PATCH 0544/1472] =?UTF-8?q?Fix=20race=20condition,=20don?= =?UTF-8?q?=E2=80=99t=20rely=20on=20detect=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit detect feature is to be tested though --- tests/test_segments.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index 6ef9d459..9ce53e27 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -192,6 +192,7 @@ class TestCommon(TestCase): self.assertEqual(common.cpu_load_percent(pl=pl), '52%') def test_network_load(self): + from time import sleep def gb(interface): return None @@ -204,9 +205,12 @@ class TestCommon(TestCase): with replace_attr(common, '_get_bytes', _get_bytes): common.network_load.startup(pl=pl) - self.assertEqual(common.network_load(pl=pl), None) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) common.network_load.sleep(0) - self.assertEqual(common.network_load(pl=pl), None) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) + while 'prev' not in common.network_load.interfaces.get('eth0', {}): + sleep(0.1) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) l = [0, 0] @@ -216,25 +220,25 @@ class TestCommon(TestCase): return tuple(l) f[0] = gb2 - common.network_load.sleep(0) - common.network_load.sleep(0) - self.assertEqual(common.network_load(pl=pl), [ + while not common.network_load.interfaces.get('eth0', {}).get('prev', (None, None))[1]: + sleep(0.1) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}'), [ + self.assertEqual(common.network_load(pl=pl, interface='eth0', recv_format='r {value}', sent_format='s {value}'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps'), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps', interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) @@ -243,7 +247,7 @@ class TestCommon(TestCase): def __eq__(self, i): return abs(i - 50.0) < 1 - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800), [ + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, ]) From b6143cf42949fad797e9dc33d5c93110a442b81e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 21:04:09 +0400 Subject: [PATCH 0545/1472] Readd log_ prefix and expand log_file --- powerline/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index a3a80420..845081fe 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -138,7 +138,7 @@ class Powerline(object): # Create logger if not logger: - log_format = common_config.get('format', '%(asctime)s:%(levelname)s:%(message)s') + log_format = common_config.get('log_format', '%(asctime)s:%(levelname)s:%(message)s') formatter = logging.Formatter(log_format) level = getattr(logging, common_config.get('log_level', 'WARNING')) @@ -162,8 +162,9 @@ class Powerline(object): :return: logging.Handler subclass. ''' - log_file = common_config.get('file', None) + log_file = common_config.get('log_file', None) if log_file: + log_file = os.path.expanduser(log_file) log_dir = os.path.dirname(log_file) if not os.path.isdir(log_dir): os.mkdir(log_dir) From 80e55b0c817d1a251ca333c6973ed3b250d68031 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 21:25:55 +0400 Subject: [PATCH 0546/1472] Replace update_lock with shutdon_event --- powerline/lib/threaded.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 147de23b..73aff250 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -5,18 +5,17 @@ from __future__ import absolute_import from powerline.lib.time import monotonic from time import sleep -from threading import Thread, Lock +from threading import Thread, Lock, Event class ThreadedSegment(object): - daemon = True min_sleep_time = 0.1 update_first = True interval = 1 def __init__(self): super(ThreadedSegment, self).__init__() - self.update_lock = Lock() + self.shutdown_event = Event() self.write_lock = Lock() self.keep_going = True self.run_once = True @@ -50,39 +49,27 @@ class ThreadedSegment(object): def start(self): self.thread = Thread(target=self.run) - self.thread.daemon = self.daemon self.thread.start() def sleep(self, adjust_time): - sleep(max(self.interval - adjust_time, self.min_sleep_time)) + self.shutdown_event.wait(max(self.interval - adjust_time, self.min_sleep_time)) + if self.shutdown_event.is_set(): + self.keep_going = False def run(self): while self.keep_going: start_time = monotonic() - try: - if self.update_lock.acquire(False): - try: - self.update() - except Exception as e: - self.error('Exception while updating: {0}', str(e)) - self.skip = True - else: - self.skip = False - else: - return - finally: - # Release lock in any case. If it is not locked in this thread, - # it was done in main thread in .shutdown method, and the lock - # will never be released. - self.update_lock.release() - + self.update() + except Exception as e: + self.error('Exception while updating: {0}', str(e)) + self.skip = True + else: + self.skip = False self.sleep(monotonic() - start_time) def shutdown(self): - if self.keep_going: - self.keep_going = False - self.update_lock.acquire() + self.shutdown_event.set() def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. From 00271c2a0c98a18dc8806e62b3ac9b36c7aa3d66 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 21:30:17 +0400 Subject: [PATCH 0547/1472] Shut down network_load segment in tests --- tests/test_segments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index 9ce53e27..beb354de 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -251,6 +251,7 @@ class TestCommon(TestCase): {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, ]) + common.network_load.shutdown() def test_virtualenv(self): with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as pl: From 3809b8b3b5be159e3e492a0db0c99da29dbfd14b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 21:38:08 +0400 Subject: [PATCH 0548/1472] Allow multiple shutdowns with multiple starts --- powerline/lib/threaded.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 73aff250..0089e1c7 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -17,7 +17,6 @@ class ThreadedSegment(object): super(ThreadedSegment, self).__init__() self.shutdown_event = Event() self.write_lock = Lock() - self.keep_going = True self.run_once = True self.did_set_interval = False self.thread = None @@ -48,6 +47,7 @@ class ThreadedSegment(object): return self.thread and self.thread.is_alive() def start(self): + self.keep_going = True self.thread = Thread(target=self.run) self.thread.start() From 3aab9ef96c7b8b0c4c48be4cca2d8f8813099110 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 21:40:11 +0400 Subject: [PATCH 0549/1472] Make zsh/zpython also call .shutdown correctly --- powerline/bindings/zsh/__init__.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index ff82a5af..420f0e86 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,9 +1,18 @@ # vim:fileencoding=utf-8:noet import zsh +import atexit from powerline.shell import ShellPowerline from powerline.lib import parsedotval +used_powerlines = [] + + +def shutdown(): + for powerline in used_powerlines: + powerline.renderer.shutdown() + + def get_var_config(var): try: return [parsedotval(i) for i in zsh.getvalue(var).items()] @@ -69,17 +78,17 @@ class Environment(object): class Prompt(object): - __slots__ = ('render', 'side', 'savedpsvar', 'savedps', 'args') + __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args') def __init__(self, powerline, side, savedpsvar=None, savedps=None): - self.render = powerline.renderer.render + self.powerline = powerline self.side = side self.savedpsvar = savedpsvar self.savedps = savedps self.args = powerline.args def __str__(self): - r = self.render(width=zsh.columns(), side=self.side, segment_info=self.args) + r = self.powerline.renderer.render(width=zsh.columns(), side=self.side, segment_info=self.args) if type(r) is not str: if type(r) is bytes: return r.decode('utf-8') @@ -90,6 +99,9 @@ class Prompt(object): def __del__(self): if self.savedps: zsh.setvalue(self.savedpsvar, self.savedps) + used_powerlines.remove(self.powerline) + if self.powerline not in used_powerlines: + self.powerline.renderer.shutdown() def set_prompt(powerline, psvar, side): @@ -103,5 +115,8 @@ def set_prompt(powerline, psvar, side): def setup(): environ = Environment() powerline = ShellPowerline(Args(), environ=environ, getcwd=lambda: environ['PWD']) + used_powerlines.append(powerline) + used_powerlines.append(powerline) set_prompt(powerline, 'PS1', 'left') set_prompt(powerline, 'RPS1', 'right') + atexit.register(shutdown) From 8c63d20880635efe8d6200172c0f9415dc8fe6ad Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Mar 2013 22:39:31 +0400 Subject: [PATCH 0550/1472] Fix update_first --- powerline/lib/threaded.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 0089e1c7..19b841f5 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -37,6 +37,8 @@ class ThreadedSegment(object): if update_first and self.update_first: self.update() self.start() + elif not self.updated: + self.update() if self.skip: return self.crashed_value @@ -80,9 +82,10 @@ class ThreadedSegment(object): self.interval = interval self.has_set_interval = True - def set_state(self, interval=None, **kwargs): + def set_state(self, interval=None, update_first=True, **kwargs): if not self.did_set_interval or interval: self.set_interval(interval) + self.updated = not (update_first and self.update_first) def startup(self, pl, **kwargs): self.run_once = False @@ -111,12 +114,13 @@ def printed(func): class KwThreadedSegment(ThreadedSegment): drop_interval = 10 * 60 - update_first = False + update_first = True def __init__(self): super(KwThreadedSegment, self).__init__() self.queries = {} self.crashed = set() + self.updated = True @staticmethod def key(**kwargs): @@ -157,7 +161,7 @@ class KwThreadedSegment(ThreadedSegment): for key in removes: self.queries.pop(key) - def set_state(self, interval=None, **kwargs): + def set_state(self, interval=None, update_first=True, **kwargs): if not self.did_set_interval or (interval < self.interval): self.set_interval(interval) From 92652ca5c4f8cb0f4deeaaa4b2efdc74388049f1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 00:10:54 +0400 Subject: [PATCH 0551/1472] Some fixes for flake8 --- powerline/lib/threaded.py | 1 - tests/test_segments.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 19b841f5..50d7895e 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -4,7 +4,6 @@ from __future__ import absolute_import from powerline.lib.time import monotonic -from time import sleep from threading import Thread, Lock, Event diff --git a/tests/test_segments.py b/tests/test_segments.py index beb354de..87c460f1 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -193,6 +193,7 @@ class TestCommon(TestCase): def test_network_load(self): from time import sleep + def gb(interface): return None From 491b48fd9cf74798ca20b30379f56971690f95c0 Mon Sep 17 00:00:00 2001 From: kierun Date: Mon, 25 Mar 2013 08:03:44 +0000 Subject: [PATCH 0552/1472] Added terminus font and urxvt documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit How to use terminus and urxvt and still get the powerline glyphs . --- docs/source/tipstricks.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/source/tipstricks.rst b/docs/source/tipstricks.rst index e445ab95..91cbb6ee 100644 --- a/docs/source/tipstricks.rst +++ b/docs/source/tipstricks.rst @@ -35,3 +35,15 @@ statusline: set laststatus=2 " Always display the statusline in all windows set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline) + +Terminus font and urxvt +======================= + +The Terminus fonts does not have the powerline glyths and unless someone submits a patch to +the font author, it is unlikely to happen. However, Andre Klärner came up with this work around: +In your ``~/.Xdefault`` file add the following: + +``urxvt*font: xft:Terminus:pixelsize=12,xft:Inconsolata\ for\ Powerline:pixelsize=12`` + +This will allow urxvt to fallback onto the Inconsolata fonts in case it does not find the right +glyths within the terminus font. From 80ddbfbf9a6030a1d70e18c66d30111bccb37662 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 00:11:58 +0400 Subject: [PATCH 0553/1472] Split Powerline.__init__ into __init__ and create_renderer Target: with long-living Powerline objects periodically reload configuration recreating renderer. Use file watchers to watch for configuration. Configuration should be able to be safely reloaded in non-blocking mode in a separate thread up to the time when it comes to recreating renderer. This commit does not add anything that actually reloads the configuration, multiple runs of .create_renderer were not tested. --- powerline/__init__.py | 171 ++++++++++++++------ powerline/bindings/ipython/post_0_11.py | 4 +- powerline/bindings/ipython/pre_0_11.py | 6 +- powerline/bindings/qtile/widget.py | 2 +- powerline/bindings/vim/plugin/powerline.vim | 4 +- powerline/bindings/zsh/__init__.py | 6 +- scripts/powerline | 2 +- tests/test_configuration.py | 22 +-- 8 files changed, 146 insertions(+), 71 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 845081fe..f48fad1d 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -8,6 +8,8 @@ import logging from powerline.colorscheme import Colorscheme +from threading import Lock + DEFAULT_SYSTEM_CONFIG_DIR = None @@ -97,64 +99,124 @@ class Powerline(object): environ=os.environ, getcwd=getattr(os, 'getcwdu', os.getcwd), home=None): + self.ext = ext + self.renderer_module = renderer_module or ext + self.run_once = run_once + self.logger = logger + self.environ = environ + self.getcwd = getcwd + self.home = home + self.config_paths = self.get_config_paths() - # Load main config file - config = self.load_main_config() - common_config = config['common'] - ext_config = config['ext'][ext] - self.ext = ext + self.renderer_lock = Lock() - # Load and initialize colorscheme - colorscheme_config = self.load_colorscheme_config(ext_config['colorscheme']) - colors_config = self.load_colors_config() - colorscheme = Colorscheme(colorscheme_config, colors_config) + self.prev_common_config = None + self.prev_ext_config = None - # Load and initialize extension theme - theme_config = self.load_theme_config(ext_config.get('theme', 'default')) - common_config['paths'] = [os.path.expanduser(path) for path in common_config.get('paths', [])] - self.import_paths = common_config['paths'] - theme_kwargs = { - 'ext': ext, - 'common_config': common_config, - 'run_once': run_once, - } - local_themes = self.get_local_themes(ext_config.get('local_themes')) + self.create_renderer(load_main_config=True, load_colors=True, load_colorscheme=True, load_theme=True) - # Load and initialize extension renderer - renderer_module_name = renderer_module or ext - renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) - try: - Renderer = __import__(renderer_module_import, fromlist=['renderer']).renderer - except ImportError as e: - sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) - sys.exit(1) - options = { - 'term_truecolor': common_config.get('term_truecolor', False), - 'ambiwidth': common_config.get('ambiwidth', 1), - 'tmux_escape': common_config.get('additional_escapes') == 'tmux', - 'screen_escape': common_config.get('additional_escapes') == 'screen', - } + def create_renderer(self, load_main_config=False, load_colors=False, load_colorscheme=False, load_theme=False): + '''(Re)create renderer object. Can be used after Powerline object was + successfully initialized. If any of the below parameters except + ``load_main_config`` is True renderer object will be recreated. - # Create logger - if not logger: - log_format = common_config.get('log_format', '%(asctime)s:%(levelname)s:%(message)s') - formatter = logging.Formatter(log_format) + :param bool load_main_config: + Determines whether main configuration file (:file:`config.json`) + should be loaded. If appropriate configuration changes implies + ``load_colorscheme`` and ``load_theme`` and recreation of renderer + object. Won’t trigger recreation if only unrelated configuration + changed. + :param bool load_colors: + Determines whether colors configuration from :file:`colors.json` + should be (re)loaded. + :param bool load_colorscheme: + Determines whether colorscheme configuration should be (re)loaded. + :param bool load_theme: + Determines whether theme configuration should be reloaded. - level = getattr(logging, common_config.get('log_level', 'WARNING')) - handler = self.get_log_handler(common_config) - handler.setLevel(level) - handler.setFormatter(formatter) + Note: reloading of local themes should be taken care of in renderer. + ''' + common_config_differs = False + ext_config_differs = False + if load_main_config: + config = self.load_main_config() + self.common_config = config['common'] + if self.common_config != self.prev_common_config: + common_config_differs = True + self.prev_common_config = self.common_config + self.common_config['paths'] = [os.path.expanduser(path) for path in self.common_config.get('paths', [])] + self.import_paths = self.common_config['paths'] - logger = logging.getLogger('powerline') - logger.setLevel(level) - logger.addHandler(handler) + if not self.logger: + log_format = self.common_config.get('log_format', '%(asctime)s:%(levelname)s:%(message)s') + formatter = logging.Formatter(log_format) - pl = PowerlineState(logger=logger, environ=environ, getcwd=getcwd, home=home) + level = getattr(logging, self.common_config.get('log_level', 'WARNING')) + handler = self.get_log_handler() + handler.setLevel(level) + handler.setFormatter(formatter) - self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, pl, **options) + self.logger = logging.getLogger('powerline') + self.logger.setLevel(level) + self.logger.addHandler(handler) - def get_log_handler(self, common_config): + self.pl = PowerlineState(logger=self.logger, environ=self.environ, getcwd=self.getcwd, home=self.home) + + self.renderer_options = { + 'term_truecolor': self.common_config.get('term_truecolor', False), + 'ambiwidth': self.common_config.get('ambiwidth', 1), + 'tmux_escape': self.common_config.get('additional_escapes') == 'tmux', + 'screen_escape': self.common_config.get('additional_escapes') == 'screen', + } + + self.theme_kwargs = { + 'ext': self.ext, + 'common_config': self.common_config, + 'run_once': self.run_once, + } + + self.ext_config = config['ext'][self.ext] + if self.ext_config != self.prev_ext_config: + ext_config_differs = True + if not self.prev_ext_config or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes'): + self.local_themes = self.get_local_themes(self.ext_config.get('local_themes')) + load_colorscheme = (load_colorscheme + or not self.prev_ext_config + or self.prev_ext_config['colorscheme'] != self.ext_config['colorscheme']) + load_theme = (load_theme + or not self.prev_ext_config + or self.prev_ext_config['theme'] != self.ext_config['theme']) + + create_renderer = load_colors or load_colorscheme or load_theme or common_config_differs or ext_config_differs + + if load_colors: + colors_config = self.load_colors_config() + + if load_colorscheme or load_colors: + colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) + self.colorscheme = Colorscheme(colorscheme_config, colors_config) + + if load_theme: + self.theme_config = self.load_theme_config(self.ext_config.get('theme', 'default')) + + if create_renderer: + renderer_module_import = 'powerline.renderers.{0}'.format(self.renderer_module) + try: + Renderer = __import__(renderer_module_import, fromlist=['renderer']).renderer + except Exception as e: + self.pl.exception('Failed to import renderer module: {0}', str(e)) + sys.exit(1) + + with self.renderer_lock: + self.renderer = Renderer(self.theme_config, + self.local_themes, + self.theme_kwargs, + self.colorscheme, + self.pl, + **self.renderer_options) + + def get_log_handler(self): '''Get log handler. :param dict common_config: @@ -162,7 +224,7 @@ class Powerline(object): :return: logging.Handler subclass. ''' - log_file = common_config.get('log_file', None) + log_file = self.common_config.get('log_file', None) if log_file: log_file = os.path.expanduser(log_file) log_dir = os.path.dirname(log_file) @@ -237,3 +299,16 @@ class Powerline(object): ``__init__`` arguments, refer to its documentation. ''' return None + + def render(self, *args, **kwargs): + '''Lock renderer from modifications and pass all arguments further to + ``self.renderer.render()``. + ''' + with self.renderer_lock: + return self.renderer.render(*args, **kwargs) + + def shutdown(self): + '''Lock renderer from modifications and run its ``.shutdown()`` method. + ''' + with self.renderer_lock: + self.renderer.shutdown() diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index bdb42923..f91a5aed 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -24,7 +24,7 @@ class PowerlinePromptManager(PromptManager): def render(self, name, color=True, *args, **kwargs): width = None if name == 'in' else self.width - res, res_nocolor = self.powerline.renderer.render(output_raw=True, width=width, matcher_info=name, segment_info=self.powerline_segment_info) + res, res_nocolor = self.powerline.render(output_raw=True, width=width, matcher_info=name, segment_info=self.powerline_segment_info) self.txtwidth = len(res_nocolor) self.width = self.txtwidth return res if color else res_nocolor @@ -51,7 +51,7 @@ def load_ipython_extension(ip): ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell) def shutdown_hook(): - powerline.renderer.shutdown() + powerline.shutdown() raise TryNext() ip.hooks.shutdown_hook.add(shutdown_hook) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 64f9b206..628c39bc 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -56,7 +56,7 @@ class PowerlinePrompt(BasePrompt): def set_p_str(self, width=None): self.p_str, self.p_str_nocolor = ( - self.powerline.renderer.render(output_raw=True, + self.powerline.render(output_raw=True, segment_info=self.powerline_segment_info, matcher_info=self.powerline_prompt_type, width=width) @@ -85,7 +85,7 @@ class PowerlinePrompt1(PowerlinePrompt): self.powerline_last_in['prompt_text_len'] = self.prompt_text_len def auto_rewrite(self): - return RewriteResult(self.powerline.renderer.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) + return RewriteResult(self.powerline.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) + (' ' * self.nrspaces)) @@ -128,7 +128,7 @@ def setup(**kwargs): raise TryNext() def shutdown_hook(): - powerline.renderer.shutdown() + powerline.shutdown() raise TryNext() ip.IP.hooks.late_startup_hook.add(late_startup_hook) diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index 71d8d3fa..6c1e6602 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -15,7 +15,7 @@ class Powerline(base._TextBox): def update(self): if not self.configured: return True - self.text = self.powerline.renderer.render(side='right') + self.text = self.powerline.render(side='right') self.bar.draw() return True diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 83f87296..71b9fb99 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -67,7 +67,7 @@ endfunction function! Powerline(window_id) let winidx = index(map(range(1, winnr('$')), 's:GetWinID(v:val)'), a:window_id) let current = w:window_id is# a:window_id - return s:pyeval('powerline.renderer.render('. a:window_id .', '. winidx .', '. current .')') + return s:pyeval('powerline.render('. a:window_id .', '. winidx .', '. current .')') endfunction function! PowerlineNew() @@ -84,7 +84,7 @@ endfunction augroup Powerline autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' autocmd! VimEnter * :redrawstatus! - autocmd! VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()' + autocmd! VimLeave * :exec s:powerline_pycmd 'powerline.shutdown()' augroup END exec s:powerline_pycmd 'powerline = VimPowerline()' diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 420f0e86..4f00129a 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -10,7 +10,7 @@ used_powerlines = [] def shutdown(): for powerline in used_powerlines: - powerline.renderer.shutdown() + powerline.shutdown() def get_var_config(var): @@ -88,7 +88,7 @@ class Prompt(object): self.args = powerline.args def __str__(self): - r = self.powerline.renderer.render(width=zsh.columns(), side=self.side, segment_info=self.args) + r = self.powerline.render(width=zsh.columns(), side=self.side, segment_info=self.args) if type(r) is not str: if type(r) is bytes: return r.decode('utf-8') @@ -101,7 +101,7 @@ class Prompt(object): zsh.setvalue(self.savedpsvar, self.savedps) used_powerlines.remove(self.powerline) if self.powerline not in used_powerlines: - self.powerline.renderer.shutdown() + self.powerline.shutdown() def set_prompt(powerline, psvar, side): diff --git a/scripts/powerline b/scripts/powerline index 0926a760..dcc23fa5 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -17,7 +17,7 @@ if __name__ == '__main__': if 'PWD' in os.environ: kwargs['getcwd'] = lambda: os.environ['PWD'] powerline = ShellPowerline(args, run_once=True, **kwargs) - rendered = powerline.renderer.render(width=args.width, side=args.side, segment_info=args) + rendered = powerline.render(width=args.width, side=args.side, segment_info=args) try: sys.stdout.write(rendered) except UnicodeEncodeError: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index ec6b2917..8cb7531c 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -18,7 +18,7 @@ SBLOCK = chr(ord('S') - 0x40) def shutdown(powerline): from powerline.segments import common, vim try: - powerline.renderer.shutdown() + powerline.shutdown() finally: # After shutdown threads are useless, it is needed to recreate them. from imp import reload @@ -39,7 +39,7 @@ class TestConfig(TestCase): powerline = VimPowerline() def check_output(*args): - out = powerline.renderer.render(*args + (0 if mode == 'nc' else 1,)) + out = powerline.render(*args + (0 if mode == 'nc' else 1,)) if out in outputs: self.fail('Duplicate in set #{0} for mode {1!r} (previously defined in set #{2} for mode {3!r})'.format(i, mode, *outputs[out])) outputs[out] = (i, mode) @@ -69,27 +69,27 @@ class TestConfig(TestCase): from powerline.shell import ShellPowerline with replace_attr(common, 'urllib_read', urllib_read): powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) - powerline.renderer.render() + powerline.render() powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) - powerline.renderer.render() + powerline.render() shutdown(powerline) def test_zsh(self): from powerline.shell import ShellPowerline args = Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt') powerline = ShellPowerline(args, run_once=False) - powerline.renderer.render(segment_info=args) + powerline.render(segment_info=args) powerline = ShellPowerline(args, run_once=False) - powerline.renderer.render(segment_info=args) + powerline.render(segment_info=args) shutdown(powerline) def test_bash(self): from powerline.shell import ShellPowerline args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]) powerline = ShellPowerline(args, run_once=False) - powerline.renderer.render(segment_info=args) + powerline.render(segment_info=args) powerline = ShellPowerline(args, run_once=False) - powerline.renderer.render(segment_info=args) + powerline.render(segment_info=args) shutdown(powerline) def test_ipython(self): @@ -103,8 +103,8 @@ class TestConfig(TestCase): powerline = IpyPowerline() segment_info = Args(prompt_count=1) for prompt_type in ['in', 'in2', 'out', 'rewrite']: - powerline.renderer.render(matcher_info=prompt_type, segment_info=segment_info) - powerline.renderer.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.render(matcher_info=prompt_type, segment_info=segment_info) shutdown(powerline) def test_wm(self): @@ -113,7 +113,7 @@ class TestConfig(TestCase): reload(common) from powerline import Powerline with replace_attr(common, 'urllib_read', urllib_read): - Powerline(ext='wm', renderer_module='pango_markup', run_once=True).renderer.render() + Powerline(ext='wm', renderer_module='pango_markup', run_once=True).render() reload(common) From 559b5caef29cfdb7d448e27bdea07913e62ffee5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 08:01:53 +0400 Subject: [PATCH 0554/1472] Take file_watcher from @kovidgoyal develop branch --- powerline/lib/file_watcher.py | 303 ++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 powerline/lib/file_watcher.py diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py new file mode 100644 index 00000000..6dbb3197 --- /dev/null +++ b/powerline/lib/file_watcher.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:noet +from __future__ import unicode_literals, absolute_import + +__copyright__ = '2013, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os, sys, errno, time +from threading import RLock + +class INotifyError(Exception): + pass + +class INotifyWatch(object): + + is_stat_based = False + + # See for the flags defined below + + # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. + ACCESS = 0x00000001 # File was accessed. + MODIFY = 0x00000002 # File was modified. + ATTRIB = 0x00000004 # Metadata changed. + CLOSE_WRITE = 0x00000008 # Writtable file was closed. + CLOSE_NOWRITE = 0x00000010 # Unwrittable file closed. + OPEN = 0x00000020 # File was opened. + MOVED_FROM = 0x00000040 # File was moved from X. + MOVED_TO = 0x00000080 # File was moved to Y. + CREATE = 0x00000100 # Subfile was created. + DELETE = 0x00000200 # Subfile was deleted. + DELETE_SELF = 0x00000400 # Self was deleted. + MOVE_SELF = 0x00000800 # Self was moved. + + # Events sent by the kernel. + UNMOUNT = 0x00002000 # Backing fs was unmounted. + Q_OVERFLOW = 0x00004000 # Event queued overflowed. + IGNORED = 0x00008000 # File was ignored. + + # Helper events. + CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. + MOVE = (MOVED_FROM | MOVED_TO) # Moves. + + # Special flags. + ONLYDIR = 0x01000000 # Only watch the path if it is a directory. + DONT_FOLLOW = 0x02000000 # Do not follow a sym link. + EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. + MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. + ISDIR = 0x40000000 # Event occurred against dir. + ONESHOT = 0x80000000 # Only send event once. + + # All events which a program can wait on. + ALL_EVENTS = (ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | + OPEN | MOVED_FROM | MOVED_TO | CREATE | DELETE | + DELETE_SELF | MOVE_SELF) + + # See + CLOEXEC = 0x80000 + NONBLOCK = 0x800 + + def __init__(self, inotify_fd, add_watch, rm_watch, read, expire_time=10): + import ctypes, struct + self._add_watch, self._rm_watch = add_watch, rm_watch + self._read = read + # We keep a reference to os to prevent it from being deleted + # during interpreter shutdown, which would lead to errors in the + # __del__ method + self.os = os + self.watches = {} + self.modified = {} + self.last_query = {} + self._buf = ctypes.create_string_buffer(5000) + self.fenc = sys.getfilesystemencoding() or 'utf-8' + self.hdr = struct.Struct(b'iIII') + if self.fenc == 'ascii': + self.fenc = 'utf-8' + self.lock = RLock() + self.expire_time = expire_time * 60 + self._inotify_fd = inotify_fd + + def handle_error(self): + import ctypes + eno = ctypes.get_errno() + raise OSError(eno, self.os.strerror(eno)) + + def __del__(self): + # This method can be called during interpreter shutdown, which means we + # must do the absolute minimum here. Note that there could be running + # daemon threads that are trying to call other methods on this object. + try: + self.os.close(self._inotify_fd) + except (AttributeError, TypeError): + pass + + def read(self): + import ctypes + buf = [] + while True: + num = self._read(self._inotify_fd, self._buf, len(self._buf)) + if num == 0: + break + if num < 0: + en = ctypes.get_errno() + if en == errno.EAGAIN: + break # No more data + if en == errno.EINTR: + continue # Interrupted, try again + raise OSError(en, self.os.strerror(en)) + buf.append(self._buf.raw[:num]) + raw = b''.join(buf) + pos = 0 + lraw = len(raw) + while lraw - pos >= self.hdr.size: + wd, mask, cookie, name_len = self.hdr.unpack_from(raw, pos) + # We dont care about names as we only watch files + pos += self.hdr.size + name_len + self.process_event(wd, mask, cookie) + + def expire_watches(self): + now = time.time() + for path, last_query in tuple(self.last_query.items()): + if last_query - now > self.expire_time: + self.unwatch(path) + + def process_event(self, wd, mask, cookie): + for path, num in tuple(self.watches.items()): + if num == wd: + if mask & self.IGNORED: + self.watches.pop(path, None) + self.modified.pop(path, None) + self.last_query.pop(path, None) + else: + self.modified[path] = True + + def unwatch(self, path): + ''' Remove the watch for path. Raises an OSError if removing the watch + fails for some reason. ''' + path = self.os.path.abspath(path) + with self.lock: + self.modified.pop(path, None) + self.last_query.pop(path, None) + wd = self.watches.pop(path, None) + if wd is not None: + if self._rm_watch(self._inotify_fd, wd) != 0: + self.handle_error() + + def watch(self, path): + ''' Register a watch for the file named path. Raises an OSError if path + does not exist. ''' + import ctypes + path = self.os.path.abspath(path) + with self.lock: + if path not in self.watches: + bpath = path if isinstance(path, bytes) else path.encode(self.fenc) + wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), + self.MODIFY | self.ATTRIB | self.MOVE_SELF | self.DELETE_SELF) + if wd == -1: + self.handle_error() + self.watches[path] = wd + self.modified[path] = False + + def __call__(self, path): + ''' Return True if path has been modified since the last call. Can + raise OSError if the path does not exist. ''' + path = self.os.path.abspath(path) + with self.lock: + self.last_query[path] = time.time() + self.expire_watches() + if path not in self.watches: + # Try to re-add the watch, it will fail if the file does not + # exist/you dont have permission + self.watch(path) + return True + self.read() + if path not in self.modified: + # An ignored event was received which means the path has been + # automatically unwatched + return True + ans = self.modified[path] + if ans: + self.modified[path] = False + return ans + + def close(self): + with self.lock: + for path in tuple(self.watches): + try: + self.unwatch(path) + except OSError: + pass + if hasattr(self, '_inotify_fd'): + self.os.close(self._inotify_fd) + del self.os + del self._add_watch + del self._rm_watch + del self._inotify_fd + +def get_inotify(expire_time=10): + ''' Initialize the inotify based file watcher ''' + import ctypes + if not hasattr(ctypes, 'c_ssize_t'): + raise INotifyError('You need python >= 2.7 to use inotify') + from ctypes.util import find_library + name = find_library('c') + if not name: + raise INotifyError('Cannot find C library') + libc = ctypes.CDLL(name, use_errno=True) + for function in ("inotify_add_watch", "inotify_init1", "inotify_rm_watch"): + if not hasattr(libc, function): + raise INotifyError('libc is too old') + # inotify_init1() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) + init1 = prototype(('inotify_init1', libc), ((1, "flags", 0),)) + + # inotify_add_watch() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) + add_watch = prototype(('inotify_add_watch', libc), ( + (1, "fd"), (1, "pathname"), (1, "mask")), use_errno=True) + + # inotify_rm_watch() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) + rm_watch = prototype(('inotify_rm_watch', libc), ( + (1, "fd"), (1, "wd")), use_errno=True) + + # read() + prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) + read = prototype(('read', libc), ( + (1, "fd"), (1, "buf"), (1, "count")), use_errno=True) + + + inotify_fd = init1(INotifyWatch.CLOEXEC|INotifyWatch.NONBLOCK) + if inotify_fd == -1: + raise INotifyError(os.strerror(ctypes.get_errno())) + return INotifyWatch(inotify_fd, add_watch, rm_watch, read, expire_time=expire_time) + +class StatWatch(object): + + is_stat_based = True + + def __init__(self): + self.watches = {} + self.lock = RLock() + + def watch(self, path): + path = os.path.abspath(path) + with self.lock: + self.watches[path] = os.path.getmtime(path) + + def unwatch(self, path): + path = os.path.abspath(path) + with self.lock: + self.watches.pop(path, None) + + def __call__(self, path): + path = os.path.abspath(path) + with self.lock: + if path not in self.watches: + self.watches[path] = os.path.getmtime(path) + return True + mtime = os.path.getmtime(path) + if mtime != self.watches[path]: + self.watches[path] = mtime + return True + return False + + def close(self): + with self.lock: + self.watches = {} + +def create_file_watcher(use_stat=False, expire_time=10): + ''' + Create an object that can watch for changes to specified files. To use: + + watcher = create_file_watcher() + watcher(path1) # Will return True if path1 has changed since the last time this was called. Always returns True the first time. + watcher.unwatch(path1) + + Uses inotify if available, otherwise tracks mtimes. expire_time is the + number of minutes after the last query for a given path for the inotify + watch for that path to be automatically removed. This conserves kernel + resources. + ''' + if use_stat: + return StatWatch() + try: + return get_inotify(expire_time=expire_time) + except INotifyError: + pass + return StatWatch() + +if __name__ == '__main__': + watcher = create_file_watcher() + print ('Using watcher: %s'%watcher.__class__.__name__) + print ('Watching %s, press Ctrl-C to quit'%sys.argv[-1]) + watcher.watch(sys.argv[-1]) + try: + while True: + if watcher(sys.argv[-1]): + print ('%s has changed'%sys.argv[-1]) + time.sleep(1) + except KeyboardInterrupt: + pass + watcher.close() + From 5d1089f2520a92f528109c79776bed62c916dd2f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 08:10:42 +0400 Subject: [PATCH 0555/1472] Some fixes for flake8, remove executable bit and shebang --- powerline/lib/file_watcher.py | 75 +++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 6dbb3197..b17b4fcf 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -1,16 +1,20 @@ -#!/usr/bin/env python # vim:fileencoding=UTF-8:noet from __future__ import unicode_literals, absolute_import __copyright__ = '2013, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, sys, errno, time +import os +import sys +import errno +import time from threading import RLock + class INotifyError(Exception): pass + class INotifyWatch(object): is_stat_based = False @@ -18,38 +22,38 @@ class INotifyWatch(object): # See for the flags defined below # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. - ACCESS = 0x00000001 # File was accessed. - MODIFY = 0x00000002 # File was modified. - ATTRIB = 0x00000004 # Metadata changed. - CLOSE_WRITE = 0x00000008 # Writtable file was closed. + ACCESS = 0x00000001 # File was accessed. + MODIFY = 0x00000002 # File was modified. + ATTRIB = 0x00000004 # Metadata changed. + CLOSE_WRITE = 0x00000008 # Writtable file was closed. CLOSE_NOWRITE = 0x00000010 # Unwrittable file closed. - OPEN = 0x00000020 # File was opened. - MOVED_FROM = 0x00000040 # File was moved from X. - MOVED_TO = 0x00000080 # File was moved to Y. - CREATE = 0x00000100 # Subfile was created. - DELETE = 0x00000200 # Subfile was deleted. - DELETE_SELF = 0x00000400 # Self was deleted. - MOVE_SELF = 0x00000800 # Self was moved. + OPEN = 0x00000020 # File was opened. + MOVED_FROM = 0x00000040 # File was moved from X. + MOVED_TO = 0x00000080 # File was moved to Y. + CREATE = 0x00000100 # Subfile was created. + DELETE = 0x00000200 # Subfile was deleted. + DELETE_SELF = 0x00000400 # Self was deleted. + MOVE_SELF = 0x00000800 # Self was moved. # Events sent by the kernel. - UNMOUNT = 0x00002000 # Backing fs was unmounted. - Q_OVERFLOW = 0x00004000 # Event queued overflowed. - IGNORED = 0x00008000 # File was ignored. + UNMOUNT = 0x00002000 # Backing fs was unmounted. + Q_OVERFLOW = 0x00004000 # Event queued overflowed. + IGNORED = 0x00008000 # File was ignored. # Helper events. - CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. - MOVE = (MOVED_FROM | MOVED_TO) # Moves. + CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. + MOVE = (MOVED_FROM | MOVED_TO) # Moves. # Special flags. - ONLYDIR = 0x01000000 # Only watch the path if it is a directory. - DONT_FOLLOW = 0x02000000 # Do not follow a sym link. - EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. - MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. - ISDIR = 0x40000000 # Event occurred against dir. - ONESHOT = 0x80000000 # Only send event once. + ONLYDIR = 0x01000000 # Only watch the path if it is a directory. + DONT_FOLLOW = 0x02000000 # Do not follow a sym link. + EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. + MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. + ISDIR = 0x40000000 # Event occurred against dir. + ONESHOT = 0x80000000 # Only send event once. # All events which a program can wait on. - ALL_EVENTS = (ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | + ALL_EVENTS = (ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | CREATE | DELETE | DELETE_SELF | MOVE_SELF) @@ -58,7 +62,8 @@ class INotifyWatch(object): NONBLOCK = 0x800 def __init__(self, inotify_fd, add_watch, rm_watch, read, expire_time=10): - import ctypes, struct + import ctypes + import struct self._add_watch, self._rm_watch = add_watch, rm_watch self._read = read # We keep a reference to os to prevent it from being deleted @@ -101,9 +106,9 @@ class INotifyWatch(object): if num < 0: en = ctypes.get_errno() if en == errno.EAGAIN: - break # No more data + break # No more data if en == errno.EINTR: - continue # Interrupted, try again + continue # Interrupted, try again raise OSError(en, self.os.strerror(en)) buf.append(self._buf.raw[:num]) raw = b''.join(buf) @@ -194,6 +199,7 @@ class INotifyWatch(object): del self._rm_watch del self._inotify_fd + def get_inotify(expire_time=10): ''' Initialize the inotify based file watcher ''' import ctypes @@ -226,14 +232,13 @@ def get_inotify(expire_time=10): read = prototype(('read', libc), ( (1, "fd"), (1, "buf"), (1, "count")), use_errno=True) - - inotify_fd = init1(INotifyWatch.CLOEXEC|INotifyWatch.NONBLOCK) + inotify_fd = init1(INotifyWatch.CLOEXEC | INotifyWatch.NONBLOCK) if inotify_fd == -1: raise INotifyError(os.strerror(ctypes.get_errno())) return INotifyWatch(inotify_fd, add_watch, rm_watch, read, expire_time=expire_time) -class StatWatch(object): +class StatWatch(object): is_stat_based = True def __init__(self): @@ -266,6 +271,7 @@ class StatWatch(object): with self.lock: self.watches = {} + def create_file_watcher(use_stat=False, expire_time=10): ''' Create an object that can watch for changes to specified files. To use: @@ -289,15 +295,14 @@ def create_file_watcher(use_stat=False, expire_time=10): if __name__ == '__main__': watcher = create_file_watcher() - print ('Using watcher: %s'%watcher.__class__.__name__) - print ('Watching %s, press Ctrl-C to quit'%sys.argv[-1]) + print ('Using watcher: %s' % watcher.__class__.__name__) + print ('Watching %s, press Ctrl-C to quit' % sys.argv[-1]) watcher.watch(sys.argv[-1]) try: while True: if watcher(sys.argv[-1]): - print ('%s has changed'%sys.argv[-1]) + print ('%s has changed' % sys.argv[-1]) time.sleep(1) except KeyboardInterrupt: pass watcher.close() - From 7646c949e2c7a0cb1bcc60538e78a0ef3916c8a4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 08:59:37 +0400 Subject: [PATCH 0556/1472] Automatically reload configuration Needs testing --- powerline/__init__.py | 87 +++++++++++++++++++++++++++------- powerline/lib/threaded.py | 27 +++++------ powerline/lint/__init__.py | 14 ++---- tests/test_configuration.py | 94 +++++++++++++++---------------------- 4 files changed, 124 insertions(+), 98 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index f48fad1d..04513b8b 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -7,8 +7,9 @@ import sys import logging from powerline.colorscheme import Colorscheme +from powerline.lib.file_watcher import create_file_watcher -from threading import Lock +from threading import Lock, Thread, Event DEFAULT_SYSTEM_CONFIG_DIR = None @@ -18,16 +19,20 @@ def open_file(path): return open(path, 'r') -def load_json_config(search_paths, config_file, load=json.load, open_file=open_file): +def find_config_file(search_paths, config_file): config_file += '.json' for path in search_paths: config_file_path = os.path.join(path, config_file) if os.path.isfile(config_file_path): - with open_file(config_file_path) as config_file_fp: - return load(config_file_fp) + return config_file_path raise IOError('Config file not found in search path: {0}'.format(config_file)) +def load_json_config(config_file_path, load=json.load, open_file=open_file): + with open_file(config_file_path) as config_file_fp: + return load(config_file_fp) + + class PowerlineState(object): def __init__(self, logger, environ, getcwd, home): self.environ = environ @@ -110,6 +115,11 @@ class Powerline(object): self.config_paths = self.get_config_paths() self.renderer_lock = Lock() + self.configs_lock = Lock() + self.shutdown_event = Event() + self.watcher = create_file_watcher() + self.configs = {} + self.thread = None self.prev_common_config = None self.prev_ext_config = None @@ -134,8 +144,6 @@ class Powerline(object): Determines whether colorscheme configuration should be (re)loaded. :param bool load_theme: Determines whether theme configuration should be reloaded. - - Note: reloading of local themes should be taken care of in renderer. ''' common_config_differs = False ext_config_differs = False @@ -208,13 +216,24 @@ class Powerline(object): self.pl.exception('Failed to import renderer module: {0}', str(e)) sys.exit(1) + # Renderer updates configuration file via segments’ .startup thus it + # should be locked to prevent state when configuration was updated, + # but .render still uses old renderer. with self.renderer_lock: - self.renderer = Renderer(self.theme_config, - self.local_themes, - self.theme_kwargs, - self.colorscheme, - self.pl, - **self.renderer_options) + try: + renderer = Renderer(self.theme_config, + self.local_themes, + self.theme_kwargs, + self.colorscheme, + self.pl, + **self.renderer_options) + except Exception as e: + self.pl.exception('Failed to construct renderer object: {0}', str(e)) + else: + self.renderer = renderer + + if not self.run_once and not self.is_alive(): + self.start() def get_log_handler(self): '''Get log handler. @@ -250,6 +269,14 @@ class Powerline(object): config_paths.append(plugin_path) return config_paths + def _load_config(self, cfg_path, type): + '''Load configuration and setup watcher.''' + path = find_config_file(self.config_paths, cfg_path) + with self.configs_lock: + self.configs[path] = type + self.watcher.watch(path) + return load_json_config(path) + def load_theme_config(self, name): '''Get theme configuration. @@ -258,14 +285,14 @@ class Powerline(object): :return: dictionary with :ref:`theme configuration ` ''' - return load_json_config(self.config_paths, os.path.join('themes', self.ext, name)) + return self._load_config(os.path.join('themes', self.ext, name), 'theme') def load_main_config(self): '''Get top-level configuration. :return: dictionary with :ref:`top-level configuration `. ''' - return load_json_config(self.config_paths, 'config') + return self._load_config('config', 'main_config') def load_colorscheme_config(self, name): '''Get colorscheme. @@ -275,14 +302,14 @@ class Powerline(object): :return: dictionary with :ref:`colorscheme configuration `. ''' - return load_json_config(self.config_paths, os.path.join('colorschemes', self.ext, name)) + return self._load_config(os.path.join('colorschemes', self.ext, name), 'colorscheme') def load_colors_config(self): '''Get colorscheme. :return: dictionary with :ref:`colors configuration `. ''' - return load_json_config(self.config_paths, 'colors') + return self._load_config('colors', 'colors') @staticmethod def get_local_themes(local_themes): @@ -310,5 +337,33 @@ class Powerline(object): def shutdown(self): '''Lock renderer from modifications and run its ``.shutdown()`` method. ''' + self.shutdown_event.set() with self.renderer_lock: self.renderer.shutdown() + + def is_alive(self): + return self.thread and self.thread.is_alive() + + def start(self): + self.thread = Thread(target=self.run) + self.thread.start() + + def run(self): + while not self.shutdown_event.is_set(): + kwargs = {} + with self.configs_lock: + for path, type in self.configs.items(): + if self.watcher(path): + kwargs['load_' + type] = True + if kwargs: + try: + self.create_renderer(**kwargs) + except Exception as e: + self.pl.exception('Failed to create renderer: {0}', str(e)) + self.shutdown_event.wait(10) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.shutdown() diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 50d7895e..6ac4ab32 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -17,7 +17,6 @@ class ThreadedSegment(object): self.shutdown_event = Event() self.write_lock = Lock() self.run_once = True - self.did_set_interval = False self.thread = None self.skip = False self.crashed_value = None @@ -48,17 +47,12 @@ class ThreadedSegment(object): return self.thread and self.thread.is_alive() def start(self): - self.keep_going = True + self.shutdown_event.clear() self.thread = Thread(target=self.run) self.thread.start() - def sleep(self, adjust_time): - self.shutdown_event.wait(max(self.interval - adjust_time, self.min_sleep_time)) - if self.shutdown_event.is_set(): - self.keep_going = False - def run(self): - while self.keep_going: + while not self.shutdown_event.is_set(): start_time = monotonic() try: self.update() @@ -67,7 +61,7 @@ class ThreadedSegment(object): self.skip = True else: self.skip = False - self.sleep(monotonic() - start_time) + self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) def shutdown(self): self.shutdown_event.set() @@ -79,19 +73,18 @@ class ThreadedSegment(object): # .set_interval(). interval = interval or getattr(self, 'interval') self.interval = interval - self.has_set_interval = True def set_state(self, interval=None, update_first=True, **kwargs): - if not self.did_set_interval or interval: - self.set_interval(interval) - self.updated = not (update_first and self.update_first) + self.set_interval(interval) + self.updated = not (update_first and self.update_first) def startup(self, pl, **kwargs): self.run_once = False self.pl = pl + self.set_state(**kwargs) + if not self.is_alive(): - self.set_state(**kwargs) self.start() def error(self, *args, **kwargs): @@ -161,12 +154,14 @@ class KwThreadedSegment(ThreadedSegment): self.queries.pop(key) def set_state(self, interval=None, update_first=True, **kwargs): - if not self.did_set_interval or (interval < self.interval): - self.set_interval(interval) + self.set_interval(interval) if self.update_first: self.update_first = update_first + with self.write_lock: + self.queries.clear() + @staticmethod def render_one(update_state, **kwargs): return update_state diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 91078e26..31d70798 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,5 +1,5 @@ from powerline.lint.markedjson import load -from powerline import load_json_config, Powerline +from powerline import load_json_config, find_config_file, Powerline from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes import itertools @@ -21,14 +21,6 @@ def open_file(path): return open(path, 'rb') -def find_config(search_paths, config_file): - config_file += '.json' - for path in search_paths: - if os.path.isfile(os.path.join(path, config_file)): - return path - return None - - EMPTYTUPLE = tuple() @@ -893,7 +885,7 @@ def check(path=None): hadproblem = False try: - main_config = load_json_config(search_paths, 'config', load=load_config, open_file=open_file) + main_config = load_json_config(find_config_file(search_paths, 'config'), load=load_config, open_file=open_file) except IOError: main_config = {} sys.stderr.write('\nConfiguration file not found: config.json\n') @@ -909,7 +901,7 @@ def check(path=None): import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] try: - colors_config = load_json_config(search_paths, 'colors', load=load_config, open_file=open_file) + colors_config = load_json_config(find_config_file(search_paths, 'colors'), load=load_config, open_file=open_file) except IOError: colors_config = {} sys.stderr.write('\nConfiguration file not found: colors.json\n') diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 8cb7531c..f5d766c5 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -15,17 +15,6 @@ VBLOCK = chr(ord('V') - 0x40) SBLOCK = chr(ord('S') - 0x40) -def shutdown(powerline): - from powerline.segments import common, vim - try: - powerline.shutdown() - finally: - # After shutdown threads are useless, it is needed to recreate them. - from imp import reload - reload(common) - reload(vim) - - class TestConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline @@ -36,31 +25,30 @@ class TestConfig(TestCase): outputs = {} i = 0 mode = None - powerline = VimPowerline() - def check_output(*args): - out = powerline.render(*args + (0 if mode == 'nc' else 1,)) - if out in outputs: - self.fail('Duplicate in set #{0} for mode {1!r} (previously defined in set #{2} for mode {3!r})'.format(i, mode, *outputs[out])) - outputs[out] = (i, mode) + with VimPowerline() as powerline: + def check_output(*args): + out = powerline.render(*args + (0 if mode == 'nc' else 1,)) + if out in outputs: + self.fail('Duplicate in set #{0} for mode {1!r} (previously defined in set #{2} for mode {3!r})'.format(i, mode, *outputs[out])) + outputs[out] = (i, mode) - with vim_module._with('buffer', 'foo.txt'): - with vim_module._with('globals', powerline_config_path=cfg_path): - exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) - try: - for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: - if mode != 'nc': - vim_module._start_mode(mode) - check_output(1, 0) - for args, kwargs in buffers: - i += 1 - if mode in exclude: - continue - with vim_module._with(*args, **kwargs): - check_output(1, 0) - finally: - vim_module._start_mode('n') - shutdown(powerline) + with vim_module._with('buffer', 'foo.txt'): + with vim_module._with('globals', powerline_config_path=cfg_path): + exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) + try: + for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: + if mode != 'nc': + vim_module._start_mode(mode) + check_output(1, 0) + for args, kwargs in buffers: + i += 1 + if mode in exclude: + continue + with vim_module._with(*args, **kwargs): + check_output(1, 0) + finally: + vim_module._start_mode('n') def test_tmux(self): from powerline.segments import common @@ -68,29 +56,26 @@ class TestConfig(TestCase): reload(common) from powerline.shell import ShellPowerline with replace_attr(common, 'urllib_read', urllib_read): - powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) - powerline.render() - powerline = ShellPowerline(Args(ext=['tmux']), run_once=False) - powerline.render() - shutdown(powerline) + with ShellPowerline(Args(ext=['tmux']), run_once=False) as powerline: + powerline.render() + with ShellPowerline(Args(ext=['tmux']), run_once=False) as powerline: + powerline.render() def test_zsh(self): from powerline.shell import ShellPowerline args = Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt') - powerline = ShellPowerline(args, run_once=False) - powerline.render(segment_info=args) - powerline = ShellPowerline(args, run_once=False) - powerline.render(segment_info=args) - shutdown(powerline) + with ShellPowerline(args, run_once=False) as powerline: + powerline.render(segment_info=args) + with ShellPowerline(args, run_once=False) as powerline: + powerline.render(segment_info=args) def test_bash(self): from powerline.shell import ShellPowerline args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]) - powerline = ShellPowerline(args, run_once=False) - powerline.render(segment_info=args) - powerline = ShellPowerline(args, run_once=False) - powerline.render(segment_info=args) - shutdown(powerline) + with ShellPowerline(args, run_once=False) as powerline: + powerline.render(segment_info=args) + with ShellPowerline(args, run_once=False) as powerline: + powerline.render(segment_info=args) def test_ipython(self): from powerline.ipython import IpythonPowerline @@ -100,12 +85,11 @@ class TestConfig(TestCase): config_overrides = None theme_overrides = {} - powerline = IpyPowerline() - segment_info = Args(prompt_count=1) - for prompt_type in ['in', 'in2', 'out', 'rewrite']: - powerline.render(matcher_info=prompt_type, segment_info=segment_info) - powerline.render(matcher_info=prompt_type, segment_info=segment_info) - shutdown(powerline) + with IpyPowerline() as powerline: + segment_info = Args(prompt_count=1) + for prompt_type in ['in', 'in2', 'out', 'rewrite']: + powerline.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.render(matcher_info=prompt_type, segment_info=segment_info) def test_wm(self): from powerline.segments import common From fc6636cf57562a8838a71a09e99194c601558d69 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 18:41:38 +0400 Subject: [PATCH 0557/1472] Clear Powerline.configs when loading main configuration It should eventually clear out stale watches. --- powerline/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/powerline/__init__.py b/powerline/__init__.py index 04513b8b..fccebc2f 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -292,6 +292,13 @@ class Powerline(object): :return: dictionary with :ref:`top-level configuration `. ''' + with self.configs_lock: + self.configs.clear() + # Watches for unused files will be cleared automatically after some + # time, no need to do this here, especially considering that + # a) most of them are used and thus will be recreated in other + # load_* calls; + # b) it is hard to tell which ones stopped being useful. return self._load_config('config', 'main_config') def load_colorscheme_config(self, name): From 3f53aa298ad3052bd26770f698c0ac65ed826962 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 18:43:29 +0400 Subject: [PATCH 0558/1472] Use global watcher --- powerline/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index fccebc2f..278e688a 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -14,6 +14,8 @@ from threading import Lock, Thread, Event DEFAULT_SYSTEM_CONFIG_DIR = None +watcher = None + def open_file(path): return open(path, 'r') @@ -104,6 +106,7 @@ class Powerline(object): environ=os.environ, getcwd=getattr(os, 'getcwdu', os.getcwd), home=None): + global watcher self.ext = ext self.renderer_module = renderer_module or ext self.run_once = run_once @@ -117,10 +120,12 @@ class Powerline(object): self.renderer_lock = Lock() self.configs_lock = Lock() self.shutdown_event = Event() - self.watcher = create_file_watcher() self.configs = {} self.thread = None + if not watcher: + watcher = create_file_watcher() + self.prev_common_config = None self.prev_ext_config = None @@ -271,10 +276,11 @@ class Powerline(object): def _load_config(self, cfg_path, type): '''Load configuration and setup watcher.''' + global watcher path = find_config_file(self.config_paths, cfg_path) with self.configs_lock: self.configs[path] = type - self.watcher.watch(path) + watcher.watch(path) return load_json_config(path) def load_theme_config(self, name): @@ -356,11 +362,12 @@ class Powerline(object): self.thread.start() def run(self): + global watcher while not self.shutdown_event.is_set(): kwargs = {} with self.configs_lock: for path, type in self.configs.items(): - if self.watcher(path): + if watcher(path): kwargs['load_' + type] = True if kwargs: try: From ada5dede257ce80b9a37b666289ae0ff55ddb5b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 18:45:43 +0400 Subject: [PATCH 0559/1472] Fix network_load test It was using old network_load.sleep and also was not shutting down network_load in case it failed test --- tests/test_segments.py | 86 +++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index 87c460f1..e74fcbe1 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -206,53 +206,55 @@ class TestCommon(TestCase): with replace_attr(common, '_get_bytes', _get_bytes): common.network_load.startup(pl=pl) - self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) - common.network_load.sleep(0) - self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) - while 'prev' not in common.network_load.interfaces.get('eth0', {}): - sleep(0.1) - self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) + try: + self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) + sleep(common.network_load.interval) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) + while 'prev' not in common.network_load.interfaces.get('eth0', {}): + sleep(0.1) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), None) - l = [0, 0] + l = [0, 0] - def gb2(interface): - l[0] += 1200 - l[1] += 2400 - return tuple(l) - f[0] = gb2 + def gb2(interface): + l[0] += 1200 + l[1] += 2400 + return tuple(l) + f[0] = gb2 - while not common.network_load.interfaces.get('eth0', {}).get('prev', (None, None))[1]: - sleep(0.1) - self.assertEqual(common.network_load(pl=pl, interface='eth0'), [ - {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, - {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) - self.assertEqual(common.network_load(pl=pl, interface='eth0', recv_format='r {value}', sent_format='s {value}'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, - {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps', interface='eth0'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']}, - {'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True, interface='eth0'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']}, - {'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0, interface='eth0'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, - {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) + while not common.network_load.interfaces.get('eth0', {}).get('prev', (None, None))[1]: + sleep(0.1) + self.assertEqual(common.network_load(pl=pl, interface='eth0'), [ + {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) + self.assertEqual(common.network_load(pl=pl, interface='eth0', recv_format='r {value}', sent_format='s {value}'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps', interface='eth0'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True, interface='eth0'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0, interface='eth0'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + ]) - class ApproxEqual(object): - def __eq__(self, i): - return abs(i - 50.0) < 1 + class ApproxEqual(object): + def __eq__(self, i): + return abs(i - 50.0) < 1 - self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800, interface='eth0'), [ - {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, - {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, - ]) - common.network_load.shutdown() + self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800, interface='eth0'), [ + {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, + ]) + finally: + common.network_load.shutdown() def test_virtualenv(self): with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as pl: From 27c9a05782a45fe9db181d95673d45f9542bda9b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Mar 2013 19:04:18 +0400 Subject: [PATCH 0560/1472] Change indentation of closing }, ], ) and combinations of them --- powerline/__init__.py | 2 +- powerline/colorscheme.py | 2 +- powerline/segments/common.py | 72 ++++++++++++++++++------------------ powerline/segments/vim.py | 8 ++-- powerline/theme.py | 4 +- setup.py | 8 ++-- tests/test_segments.py | 28 +++++++------- 7 files changed, 62 insertions(+), 62 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 278e688a..0bc3c4b1 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -187,7 +187,7 @@ class Powerline(object): 'ext': self.ext, 'common_config': self.common_config, 'run_once': self.run_once, - } + } self.ext_config = config['ext'][self.ext] if self.ext_config != self.prev_ext_config: diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index c5d09f6f..3b30118f 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -109,7 +109,7 @@ class Colorscheme(object): 'fg': pick_color(group_props['fg']), 'bg': pick_color(group_props['bg']), 'attr': get_attr_flag(group_props.get('attr', [])), - } + } # 0 1 2 3 4 5 6 7 8 9 diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 3537b687..b6ea6a71 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -83,7 +83,7 @@ class BranchSegment(RepositorySegment): return [{ 'contents': branch, 'highlight_group': ['branch_dirty' if repository_status(**kwargs) else 'branch_clean', 'branch'], - }] + }] else: return branch @@ -153,7 +153,7 @@ def cwd(pl, dir_shorten_len=None, dir_limit_depth=None): ret.append({ 'contents': part, 'divider_highlight_group': 'cwd:divider', - }) + }) ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] return ret @@ -190,7 +190,7 @@ def fuzzy_time(pl): 45: 'quarter to', 50: 'ten to', 55: 'five to', - } + } special_case_str = { (23, 58): 'round about midnight', (23, 59): 'round about midnight', @@ -198,7 +198,7 @@ def fuzzy_time(pl): (0, 1): 'round about midnight', (0, 2): 'round about midnight', (12, 0): 'noon', - } + } now = datetime.now() @@ -337,17 +337,17 @@ weather_conditions_icons = { } temp_conversions = { - 'C': lambda temp: temp, - 'F': lambda temp: (temp * 9 / 5) + 32, - 'K': lambda temp: temp + 273.15, - } + 'C': lambda temp: temp, + 'F': lambda temp: (temp * 9 / 5) + 32, + 'K': lambda temp: temp + 273.15, +} # Note: there are also unicode characters for units: ℃, ℉ and K temp_units = { - 'C': '°C', - 'F': '°F', - 'K': 'K', - } + 'C': '°C', + 'F': '°F', + 'K': 'K', +} class WeatherSegment(ThreadedSegment): @@ -371,11 +371,11 @@ class WeatherSegment(ThreadedSegment): location_data['region_name'], location_data['country_name']]) query_data = { - 'q': - 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' - 'select * from we where location="{0}" and unit="c"'.format(self.location).encode('utf-8'), - 'format': 'json', - } + 'q': + 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' + 'select * from we where location="{0}" and unit="c"'.format(self.location).encode('utf-8'), + 'format': 'json', + } self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data) raw_response = urllib_read(self.url) @@ -423,19 +423,19 @@ class WeatherSegment(ThreadedSegment): gradient_level = (self.temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest) groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather'] return [ - { + { 'contents': icon + ' ', 'highlight_group': groups, 'divider_highlight_group': 'background:divider', - }, - { + }, + { 'contents': temp_format.format(temp=temp), 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': gradient_level, - }, - ] + }, + ] weather = with_docstring(WeatherSegment(), @@ -517,7 +517,7 @@ def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': gradient_level, - }) + }) ret[0]['draw_divider'] = True ret[0]['contents'] += ' ' ret[1]['contents'] += ' ' @@ -602,9 +602,9 @@ def user(pl): return None euid = _geteuid() return [{ - 'contents': username, - 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], - }] + 'contents': username, + 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], + }] if os.path.exists('/proc/uptime'): @@ -724,7 +724,7 @@ class NetworkLoadSegment(KwThreadedSegment): 'contents': format.format(value=humanize_bytes(value, suffix, si_prefix)), 'divider_highlight_group': 'background:divider', 'highlight_group': hl_groups, - }) + }) if is_gradient: max = kwargs[max_key] if value >= max: @@ -804,13 +804,13 @@ class EmailIMAPSegment(KwThreadedSegment): return [{ 'contents': str(unread_count), 'highlight_group': 'email_alert', - }] + }] else: return [{ 'contents': str(unread_count), 'highlight_group': ['email_alert_gradient', 'email_alert'], 'gradient_level': unread_count * 100.0 / max_msgs, - }] + }] email_imap_alert = with_docstring(EmailIMAPSegment(), @@ -841,7 +841,7 @@ class NowPlayingSegment(object): 'play': '▶', 'pause': '▮▮', 'stop': '■', - } + } def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', **kwargs): player_func = getattr(self, 'player_{0}'.format(player)) @@ -853,7 +853,7 @@ class NowPlayingSegment(object): 'title': None, 'elapsed': None, 'total': None, - } + } func_stats = player_func(**kwargs) if not func_stats: return None @@ -921,7 +921,7 @@ class NowPlayingSegment(object): 'title': now_playing.get('title'), 'elapsed': self._convert_seconds(now_playing.get('position', 0)), 'total': self._convert_seconds(now_playing.get('duration', 0)), - } + } def player_mpd(self, pl, host='localhost', port=6600): try: @@ -942,7 +942,7 @@ class NowPlayingSegment(object): 'title': now_playing.get('title'), 'elapsed': self._convert_seconds(now_playing.get('elapsed', 0)), 'total': self._convert_seconds(now_playing.get('time', 0)), - } + } except ImportError: now_playing = self._run_cmd(['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) if not now_playing: @@ -953,7 +953,7 @@ class NowPlayingSegment(object): 'artist': now_playing[1], 'title': now_playing[2], 'total': now_playing[3], - } + } def player_spotify(self, pl): try: @@ -981,7 +981,7 @@ class NowPlayingSegment(object): 'artist': info.get('xesam:artist')[0], 'title': info.get('xesam:title'), 'total': self._convert_seconds(info.get('mpris:length') / 1e6), - } + } def player_rhythmbox(self, pl): now_playing = self._run_cmd(['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) @@ -994,5 +994,5 @@ class NowPlayingSegment(object): 'title': now_playing[2], 'elapsed': now_playing[3], 'total': now_playing[4], - } + } now_playing = NowPlayingSegment() diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 19e7ebfa..4cfe7ec6 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -181,7 +181,7 @@ def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]') return [{ 'contents': no_file_text, 'highlight_group': ['file_name_no_file', 'file_name'], - }] + }] else: return None file_name = vim_funcs['fnamemodify'](name, ':~:.:t') @@ -258,7 +258,7 @@ def line_percent(pl, segment_info, gradient=False): 'contents': str(int(round(percentage))), 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': percentage, - }] + }] @requires_segment_info @@ -371,7 +371,7 @@ class BranchSegment(RepositorySegment): 'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info, **kwargs) else 'branch_clean'] if status_colors else []) + ['branch'], 'divider_highlight_group': 'branch:divider', - }] + }] def startup(self, status_colors=False, **kwargs): super(BranchSegment, self).startup(**kwargs) @@ -422,7 +422,7 @@ class FileVCSStatusSegment(KwWindowThreadedSegment): ret.append({ 'contents': status, 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], - }) + }) return ret return None diff --git a/powerline/theme.py b/powerline/theme.py index 266b351e..ed5e5788 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -30,11 +30,11 @@ class Theme(object): self.segments = { 'left': [], 'right': [], - } + } self.EMPTY_SEGMENT = { 'contents': None, 'highlight': {'fg': False, 'bg': False, 'attr': 0} - } + } self.pl = pl theme_configs = [theme_config] if top_theme_config: diff --git a/setup.py b/setup.py index 61986522..3d0fcb7e 100755 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( scripts=[ 'scripts/powerline', 'scripts/powerline-lint', - ], + ], keywords='', packages=find_packages(exclude=('tests', 'tests.*')), include_package_data=True, @@ -35,7 +35,7 @@ setup( extras_require={ 'docs': [ 'Sphinx', - ], - }, + ], + }, test_suite='tests' if not old_python else None, - ) +) diff --git a/tests/test_segments.py b/tests/test_segments.py index e74fcbe1..da9bc6a8 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -143,35 +143,35 @@ class TestCommon(TestCase): self.assertEqual(common.weather(pl=pl), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} - ]) + ]) self.assertEqual(common.weather(pl=pl, temp_coldest=0, temp_hottest=100), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} - ]) + ]) self.assertEqual(common.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} - ]) + ]) self.assertEqual(common.weather(pl=pl, icons={'cloudy': 'o'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} - ]) + ]) self.assertEqual(common.weather(pl=pl, icons={'partly_cloudy_day': 'x'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} - ]) + ]) self.assertEqual(common.weather(pl=pl, unit='F'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} - ]) + ]) self.assertEqual(common.weather(pl=pl, unit='K'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} - ]) + ]) self.assertEqual(common.weather(pl=pl, temp_format='{temp:.1e}C'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} - ]) + ]) def test_system_load(self): pl = Pl() @@ -227,23 +227,23 @@ class TestCommon(TestCase): self.assertEqual(common.network_load(pl=pl, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) + ]) self.assertEqual(common.network_load(pl=pl, interface='eth0', recv_format='r {value}', sent_format='s {value}'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) + ]) self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps', interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) + ]) self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) + ]) self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, - ]) + ]) class ApproxEqual(object): def __eq__(self, i): @@ -252,7 +252,7 @@ class TestCommon(TestCase): self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800, interface='eth0'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, - ]) + ]) finally: common.network_load.shutdown() From 878b808e9e2adc5545283e12cdd0cb106d8fd16a Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Mar 2013 08:12:35 +0400 Subject: [PATCH 0561/1472] Workaround deadlock in ipython shutdown_hook is called after all non-daemon threads exit, but it is needed for them to exit. Thus I had to use daemon threads in ipython or find some hack to make .shutdown be called earlier. --- powerline/__init__.py | 12 ++++++++++-- powerline/ipython.py | 2 +- powerline/lib/threaded.py | 5 +++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 0bc3c4b1..49090a22 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -36,7 +36,7 @@ def load_json_config(config_file_path, load=json.load, open_file=open_file): class PowerlineState(object): - def __init__(self, logger, environ, getcwd, home): + def __init__(self, use_daemon_threads, logger, environ, getcwd, home): self.environ = environ self.getcwd = getcwd self.home = home or environ.get('HOME', None) @@ -103,6 +103,7 @@ class Powerline(object): renderer_module=None, run_once=False, logger=None, + use_daemon_threads = False, environ=os.environ, getcwd=getattr(os, 'getcwdu', os.getcwd), home=None): @@ -114,6 +115,7 @@ class Powerline(object): self.environ = environ self.getcwd = getcwd self.home = home + self.use_daemon_threads = use_daemon_threads self.config_paths = self.get_config_paths() @@ -174,7 +176,7 @@ class Powerline(object): self.logger.setLevel(level) self.logger.addHandler(handler) - self.pl = PowerlineState(logger=self.logger, environ=self.environ, getcwd=self.getcwd, home=self.home) + self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.environ, self.getcwd, self.home) self.renderer_options = { 'term_truecolor': self.common_config.get('term_truecolor', False), @@ -234,6 +236,8 @@ class Powerline(object): **self.renderer_options) except Exception as e: self.pl.exception('Failed to construct renderer object: {0}', str(e)) + if not hasattr(self, 'renderer'): + raise else: self.renderer = renderer @@ -351,6 +355,8 @@ class Powerline(object): '''Lock renderer from modifications and run its ``.shutdown()`` method. ''' self.shutdown_event.set() + if self.use_daemon_threads and self.is_alive(): + self.thread.join() with self.renderer_lock: self.renderer.shutdown() @@ -359,6 +365,8 @@ class Powerline(object): def start(self): self.thread = Thread(target=self.run) + if self.use_daemon_threads: + self.thread.daemon = True self.thread.start() def run(self): diff --git a/powerline/ipython.py b/powerline/ipython.py index 64f53ca7..bed41514 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -6,7 +6,7 @@ from powerline.lib import mergedicts class IpythonPowerline(Powerline): def __init__(self): - super(IpythonPowerline, self).__init__('ipython') + super(IpythonPowerline, self).__init__('ipython', use_daemon_threads=True) def get_config_paths(self): if self.path: diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 6ac4ab32..617ba060 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -11,6 +11,7 @@ class ThreadedSegment(object): min_sleep_time = 0.1 update_first = True interval = 1 + daemon = False def __init__(self): super(ThreadedSegment, self).__init__() @@ -49,6 +50,7 @@ class ThreadedSegment(object): def start(self): self.shutdown_event.clear() self.thread = Thread(target=self.run) + self.thread.daemon = self.daemon self.thread.start() def run(self): @@ -65,6 +67,8 @@ class ThreadedSegment(object): def shutdown(self): self.shutdown_event.set() + if self.daemon and self.is_alive(): + self.thread.join() def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. @@ -81,6 +85,7 @@ class ThreadedSegment(object): def startup(self, pl, **kwargs): self.run_once = False self.pl = pl + self.daemon = pl.use_daemon_threads self.set_state(**kwargs) From e5db01193cf92f96eb1c0934042b179dd5a2739c Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Mar 2013 08:20:32 +0400 Subject: [PATCH 0562/1472] Add missing PowerlineState.use_daemon_threads assignment --- powerline/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 49090a22..6c5496d0 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -41,8 +41,9 @@ class PowerlineState(object): self.getcwd = getcwd self.home = home or environ.get('HOME', None) self.logger = logger - self.prefix = None + self.prefix = '' self.last_msgs = {} + self.use_daemon_threads = use_daemon_threads def _log(self, attr, msg, *args, **kwargs): prefix = kwargs.get('prefix') or self.prefix From ee23c57d6a9de728d22b4518304d09a5d01f98c3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Mar 2013 18:37:29 +0400 Subject: [PATCH 0563/1472] Purge configs in another place Loading main configuration does not necessary imply loading other configurations --- powerline/__init__.py | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 6c5496d0..ab7bb88d 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -10,6 +10,7 @@ from powerline.colorscheme import Colorscheme from powerline.lib.file_watcher import create_file_watcher from threading import Lock, Thread, Event +from collections import defaultdict DEFAULT_SYSTEM_CONFIG_DIR = None @@ -123,7 +124,7 @@ class Powerline(object): self.renderer_lock = Lock() self.configs_lock = Lock() self.shutdown_event = Event() - self.configs = {} + self.configs = defaultdict(set) self.thread = None if not watcher: @@ -132,14 +133,14 @@ class Powerline(object): self.prev_common_config = None self.prev_ext_config = None - self.create_renderer(load_main_config=True, load_colors=True, load_colorscheme=True, load_theme=True) + self.create_renderer(load_main=True, load_colors=True, load_colorscheme=True, load_theme=True) - def create_renderer(self, load_main_config=False, load_colors=False, load_colorscheme=False, load_theme=False): + def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): '''(Re)create renderer object. Can be used after Powerline object was successfully initialized. If any of the below parameters except - ``load_main_config`` is True renderer object will be recreated. + ``load_main`` is True renderer object will be recreated. - :param bool load_main_config: + :param bool load_main: Determines whether main configuration file (:file:`config.json`) should be loaded. If appropriate configuration changes implies ``load_colorscheme`` and ``load_theme`` and recreation of renderer @@ -155,7 +156,8 @@ class Powerline(object): ''' common_config_differs = False ext_config_differs = False - if load_main_config: + if load_main: + self._purge_configs('main') config = self.load_main_config() self.common_config = config['common'] if self.common_config != self.prev_common_config: @@ -207,13 +209,16 @@ class Powerline(object): create_renderer = load_colors or load_colorscheme or load_theme or common_config_differs or ext_config_differs if load_colors: + self._purge_configs('colors') colors_config = self.load_colors_config() if load_colorscheme or load_colors: + self._purge_configs('colorscheme') colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) self.colorscheme = Colorscheme(colorscheme_config, colors_config) if load_theme: + self._purge_configs('theme') self.theme_config = self.load_theme_config(self.ext_config.get('theme', 'default')) if create_renderer: @@ -284,10 +289,17 @@ class Powerline(object): global watcher path = find_config_file(self.config_paths, cfg_path) with self.configs_lock: - self.configs[path] = type + self.configs[type].add(path) watcher.watch(path) return load_json_config(path) + def _purge_configs(self, type): + try: + with self.configs_lock: + self.configs.pop(type) + except KeyError: + pass + def load_theme_config(self, name): '''Get theme configuration. @@ -303,14 +315,7 @@ class Powerline(object): :return: dictionary with :ref:`top-level configuration `. ''' - with self.configs_lock: - self.configs.clear() - # Watches for unused files will be cleared automatically after some - # time, no need to do this here, especially considering that - # a) most of them are used and thus will be recreated in other - # load_* calls; - # b) it is hard to tell which ones stopped being useful. - return self._load_config('config', 'main_config') + return self._load_config('config', 'main') def load_colorscheme_config(self, name): '''Get colorscheme. @@ -375,9 +380,10 @@ class Powerline(object): while not self.shutdown_event.is_set(): kwargs = {} with self.configs_lock: - for path, type in self.configs.items(): - if watcher(path): - kwargs['load_' + type] = True + for type, paths in self.configs.items(): + for path in paths: + if watcher(path): + kwargs['load_' + type] = True if kwargs: try: self.create_renderer(**kwargs) From 38081239f2399b2f56fce137d01f6fe092d62be3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Mar 2013 21:31:05 +0400 Subject: [PATCH 0564/1472] Remove self.sleep call --- powerline/segments/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index b6ea6a71..a8e7582b 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -16,6 +16,7 @@ from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docs from powerline.lib.time import monotonic from powerline.lib.humanize_bytes import humanize_bytes from collections import namedtuple +from time import sleep def hostname(pl, only_if_ssh=False): @@ -691,7 +692,7 @@ class NetworkLoadSegment(KwThreadedSegment): idata = {} if self.run_once: idata['prev'] = (monotonic(), _get_bytes(interface)) - self.sleep(0) + self.shutdown_event.wait(self.interval) self.interfaces[interface] = idata idata['last'] = (monotonic(), _get_bytes(interface)) From ff6fd64339a87fda7935bcdeb65449c9a2bf6ef5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 13:56:03 +0400 Subject: [PATCH 0565/1472] Only access watcher through proxy This is a fix for powerline-daemon: otherwise the first powerline object that did `watcher(file)` will receive file changed event and other powerline objects will not. --- powerline/__init__.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index ab7bb88d..55704e5b 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -18,6 +18,33 @@ DEFAULT_SYSTEM_CONFIG_DIR = None watcher = None +class MultiClientWatcher(object): + subscribers = set() + received_events = {} + + def watch(self, file): + watcher.watch(file) + + def __call__(self, file): + if file in self.received_events and self not in self.received_events[file]: + self.received_events[file].add(self) + if self.received_events >= self.subscribers: + self.received_events.pop(file) + return True + + if watcher(file): + self.received_events[file] = set([self]) + return True + + return False + + def __del__(self): + try: + self.subscribers.remove(self) + except KeyError: + pass + + def open_file(path): return open(path, 'r') @@ -129,6 +156,7 @@ class Powerline(object): if not watcher: watcher = create_file_watcher() + self.watcher = MultiClientWatcher() self.prev_common_config = None self.prev_ext_config = None @@ -290,7 +318,7 @@ class Powerline(object): path = find_config_file(self.config_paths, cfg_path) with self.configs_lock: self.configs[type].add(path) - watcher.watch(path) + self.watcher.watch(path) return load_json_config(path) def _purge_configs(self, type): @@ -361,10 +389,10 @@ class Powerline(object): '''Lock renderer from modifications and run its ``.shutdown()`` method. ''' self.shutdown_event.set() - if self.use_daemon_threads and self.is_alive(): - self.thread.join() with self.renderer_lock: self.renderer.shutdown() + if self.use_daemon_threads and self.is_alive(): + self.thread.join() def is_alive(self): return self.thread and self.thread.is_alive() @@ -382,7 +410,7 @@ class Powerline(object): with self.configs_lock: for type, paths in self.configs.items(): for path in paths: - if watcher(path): + if self.watcher(path): kwargs['load_' + type] = True if kwargs: try: From 29f29213a9f648ed13600861cb667d2959073d0b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 14:07:24 +0400 Subject: [PATCH 0566/1472] Remove ThreadedSegment.write_lock Assuming getattr(self, 'update_value') and setattr(self, 'update_value', value) are atomic. True with cpython unless somebody defined __getattribute__ or __setattr__. --- powerline/bindings/vim/plugin/powerline.vim | 2 +- powerline/lib/threaded.py | 57 ++++++++++----------- powerline/segments/common.py | 47 +++++++++-------- powerline/segments/vim.py | 4 +- tests/lib/__init__.py | 1 + 5 files changed, 54 insertions(+), 57 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 71b9fb99..1ee4b00d 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -84,7 +84,7 @@ endfunction augroup Powerline autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' autocmd! VimEnter * :redrawstatus! - autocmd! VimLeave * :exec s:powerline_pycmd 'powerline.shutdown()' + autocmd! VimLeavePre * :exec s:powerline_pycmd 'powerline.shutdown()' augroup END exec s:powerline_pycmd 'powerline = VimPowerline()' diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 617ba060..43e12bbb 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -16,33 +16,39 @@ class ThreadedSegment(object): def __init__(self): super(ThreadedSegment, self).__init__() self.shutdown_event = Event() - self.write_lock = Lock() self.run_once = True self.thread = None self.skip = False self.crashed_value = None + self.update_value = None def __call__(self, pl, update_first=True, **kwargs): if self.run_once: self.pl = pl self.set_state(**kwargs) - self.update() + update_value = self.get_update_value(True) elif not self.is_alive(): # Without this we will not have to wait long until receiving bug “I # opened vim, but branch information is only shown after I move # cursor”. # # If running once .update() is called in __call__. - if update_first and self.update_first: - self.update() + update_value = self.get_update_value(update_first and self.update_first) self.start() elif not self.updated: - self.update() + update_value = self.get_update_value(True) + else: + update_value = self.update_value if self.skip: return self.crashed_value - with self.write_lock: - return self.render(update_first=update_first, pl=pl, **kwargs) + + return self.render(update_value, update_first=update_first, pl=pl, **kwargs) + + def get_update_value(self, update=False): + if update: + self.update_value = self.update(self.update_value) + return self.update_value def is_alive(self): return self.thread and self.thread.is_alive() @@ -57,7 +63,7 @@ class ThreadedSegment(object): while not self.shutdown_event.is_set(): start_time = monotonic() try: - self.update() + self.update(self.update_value) except Exception as e: self.error('Exception while updating: {0}', str(e)) self.skip = True @@ -115,48 +121,42 @@ class KwThreadedSegment(ThreadedSegment): def __init__(self): super(KwThreadedSegment, self).__init__() - self.queries = {} - self.crashed = set() self.updated = True + self.update_value = ({}, set()) @staticmethod def key(**kwargs): return frozenset(kwargs.items()) - def render(self, update_first, **kwargs): + def render(self, update_value, update_first, **kwargs): + queries, crashed = update_value key = self.key(**kwargs) - if key in self.crashed: + if key in crashed: return self.crashed_value try: - update_state = self.queries[key][1] + update_state = queries[key][1] except KeyError: # Allow only to forbid to compute missing values: in either user # configuration or in subclasses. update_state = self.compute_state(key) if update_first and self.update_first or self.run_once else None - # No locks: render method is already running with write_lock acquired. - self.queries[key] = (monotonic(), update_state) + queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) - def update(self): + def update(self, old_update_value): updates = {} - removes = [] - for key, (last_query_time, state) in list(self.queries.items()): + crashed = set() + update_value = (updates, crashed) + queries = old_update_value[0] + for key, (last_query_time, state) in queries.items(): if last_query_time < monotonic() < last_query_time + self.drop_interval: try: updates[key] = (last_query_time, self.compute_state(key)) except Exception as e: self.exception('Exception while computing state for {0}: {1}', repr(key), str(e)) - with self.write_lock: - self.crashed.add(key) - else: - removes.append(key) - with self.write_lock: - self.queries.update(updates) - self.crashed -= set(updates) - for key in removes: - self.queries.pop(key) + crashed.add(key) + return update_value def set_state(self, interval=None, update_first=True, **kwargs): self.set_interval(interval) @@ -164,9 +164,6 @@ class KwThreadedSegment(ThreadedSegment): if self.update_first: self.update_first = update_first - with self.write_lock: - self.queries.clear() - @staticmethod def render_one(update_state, **kwargs): return update_state diff --git a/powerline/segments/common.py b/powerline/segments/common.py index a8e7582b..4c3e8739 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -39,13 +39,13 @@ class RepositorySegment(KwThreadedSegment): def key(pl, **kwargs): return os.path.abspath(pl.getcwd()) - def update(self): + def update(self, *args): # .compute_state() is running only in this method, and only in one # thread, thus operations with .directories do not need write locks # (.render() method is not using .directories). If this is changed # .directories needs redesigning self.directories.clear() - super(RepositorySegment, self).update() + return super(RepositorySegment, self).update(*args) def compute_state(self, path): repo = guess(path=path) @@ -231,19 +231,19 @@ def _external_ip(query_url='http://ipv4.icanhazip.com/'): class ExternalIpSegment(ThreadedSegment): + interval = 10 + def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs): self.query_url = query_url super(ExternalIpSegment, self).set_state(**kwargs) - def update(self): - ip = _external_ip(query_url=self.query_url) - with self.write_lock: - self.ip = ip + def update(self, old_ip): + return _external_ip(query_url=self.query_url) - def render(self, **kwargs): - if not hasattr(self, 'ip'): + def render(self, ip, **kwargs): + if not ip: return None - return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}] + return [{'contents': ip, 'divider_highlight_group': 'background:divider'}] external_ip = with_docstring(ExternalIpSegment(), @@ -357,10 +357,9 @@ class WeatherSegment(ThreadedSegment): def set_state(self, location_query=None, **kwargs): self.location = location_query self.url = None - self.condition = {} super(WeatherSegment, self).set_state(**kwargs) - def update(self): + def update(self, old_weather): import json if not self.url: @@ -398,31 +397,31 @@ class WeatherSegment(ThreadedSegment): icon_names = ('unknown',) self.error('Unknown condition code: {0}', condition_code) - with self.write_lock: - self.temp = temp - self.icon_names = icon_names + return (temp, icon_names) - def render(self, icons=None, unit='C', temp_format=None, temp_coldest=-30, temp_hottest=40, **kwargs): - if not hasattr(self, 'icon_names'): + def render(self, weather, icons=None, unit='C', temp_format=None, temp_coldest=-30, temp_hottest=40, **kwargs): + if not weather: return None - for icon_name in self.icon_names: + temp, icon_names = weather + + for icon_name in icon_names: if icons: if icon_name in icons: icon = icons[icon_name] break else: - icon = weather_conditions_icons[self.icon_names[-1]] + icon = weather_conditions_icons[icon_names[-1]] temp_format = temp_format or ('{temp:.0f}' + temp_units[unit]) - temp = temp_conversions[unit](self.temp) - if self.temp <= temp_coldest: + converted_temp = temp_conversions[unit](temp) + if temp <= temp_coldest: gradient_level = 0 - elif self.temp >= temp_hottest: + elif temp >= temp_hottest: gradient_level = 100 else: - gradient_level = (self.temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest) - groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather'] + gradient_level = (temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest) + groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather'] return [ { 'contents': icon + ' ', @@ -430,7 +429,7 @@ class WeatherSegment(ThreadedSegment): 'divider_highlight_group': 'background:divider', }, { - 'contents': temp_format.format(temp=temp), + 'contents': temp_format.format(temp=converted_temp), 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 4cfe7ec6..f61a98c6 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -318,13 +318,13 @@ class RepositorySegment(KwWindowThreadedSegment): # FIXME os.getcwd() is not a proper variant for non-current buffers return segment_info['buffer'].name or os.getcwd() - def update(self): + def update(self, *args): # .compute_state() is running only in this method, and only in one # thread, thus operations with .directories do not need write locks # (.render() method is not using .directories). If this is changed # .directories needs redesigning self.directories.clear() - super(RepositorySegment, self).update() + return super(RepositorySegment, self).update(*args) def compute_state(self, path): repo = guess(path=path) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 03528cdb..69cc7b7a 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -12,6 +12,7 @@ class Pl(object): self.prefix = None self.environ = {} self.home = None + self.use_daemon_threads = True def getcwd(self): if isinstance(self._cwd, Exception): From 655549440b31cf94564de5020947c1b210ff4107 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 15:54:33 +0400 Subject: [PATCH 0567/1472] Use daemon threads by default Note: current implementation runs `thread.join()` to ensure that thread is not shut down while updating, but this works only with regular shutdown. If shutdown is somehow done without triggering VimLeavePre (for vim) threads can be terminated at any state. Closes #368 Closes #371 --- powerline/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 55704e5b..e282a406 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -132,7 +132,7 @@ class Powerline(object): renderer_module=None, run_once=False, logger=None, - use_daemon_threads = False, + use_daemon_threads=True, environ=os.environ, getcwd=getattr(os, 'getcwdu', os.getcwd), home=None): From 34c775f628ad06fb4d334622e37864a85cb09d22 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 16:51:04 +0400 Subject: [PATCH 0568/1472] Fix problem with updating value: it was not updated in a thread --- powerline/lib/threaded.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 43e12bbb..4be4acf3 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -63,7 +63,7 @@ class ThreadedSegment(object): while not self.shutdown_event.is_set(): start_time = monotonic() try: - self.update(self.update_value) + self.update_value = self.update(self.update_value) except Exception as e: self.error('Exception while updating: {0}', str(e)) self.skip = True From bc1a3e4c1dd823556806d4a84834a9136f2791db Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 16:53:04 +0400 Subject: [PATCH 0569/1472] Allow overriding renderer module with any module Will be used in tests. Allows renderer modules (and extensions) that look like `foo.bar`: it will import `foo.bar` and not `powerline.renderers.foo.bar` like before. --- powerline/__init__.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index e282a406..0d17e2e8 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -111,7 +111,12 @@ class Powerline(object): colorschemes, render module (``powerline.renders.{ext}``). :param str renderer_module: Overrides renderer module (defaults to ``ext``). Should be the name of - the package imported like this: ``powerline.renders.{render_module}``. + the package imported like this: ``powerline.renders.{render_module}``. + If this parameter contains a dot, ``powerline.renderers.`` is not + prepended. There is also a special case for renderers defined in + toplevel modules: ``foo.`` (note: dot at the end) tries to get renderer + from module ``foo`` (because ``foo`` (without dot) tries to get renderer + from module ``powerline.renderers.foo``). :param bool run_once: Determines whether .renderer.render() method will be run only once during python session. @@ -146,6 +151,11 @@ class Powerline(object): self.home = home self.use_daemon_threads = use_daemon_threads + if '.' not in self.renderer_module: + self.renderer_module = 'powerline.renderers.' + self.renderer_module + elif self.renderer_module[-1] == '.': + self.renderer_module = self.renderer_module[:-1] + self.config_paths = self.get_config_paths() self.renderer_lock = Lock() @@ -250,9 +260,8 @@ class Powerline(object): self.theme_config = self.load_theme_config(self.ext_config.get('theme', 'default')) if create_renderer: - renderer_module_import = 'powerline.renderers.{0}'.format(self.renderer_module) try: - Renderer = __import__(renderer_module_import, fromlist=['renderer']).renderer + Renderer = __import__(self.renderer_module, fromlist=['renderer']).renderer except Exception as e: self.pl.exception('Failed to import renderer module: {0}', str(e)) sys.exit(1) From e2b13d9bba63f185aedc0c07b2454385eeeb980f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 16:54:43 +0400 Subject: [PATCH 0570/1472] Also catch keyboard interrupts while updating This will skip segments and issue a warning to log. --- powerline/lib/threaded.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 4be4acf3..63d79e83 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -65,7 +65,10 @@ class ThreadedSegment(object): try: self.update_value = self.update(self.update_value) except Exception as e: - self.error('Exception while updating: {0}', str(e)) + self.exception('Exception while updating: {0}', str(e)) + self.skip = True + except KeyboardInterrupt: + self.warn('Caught keyboard interrupt while updating') self.skip = True else: self.skip = False @@ -154,8 +157,12 @@ class KwThreadedSegment(ThreadedSegment): try: updates[key] = (last_query_time, self.compute_state(key)) except Exception as e: - self.exception('Exception while computing state for {0}: {1}', repr(key), str(e)) + self.exception('Exception while computing state for {0!r}: {1}', key, str(e)) crashed.add(key) + except KeyboardInterrupt: + self.warn('Interrupt while computing state for {0!r}', key) + crashed.add(key) + return update_value def set_state(self, interval=None, update_first=True, **kwargs): From 388cccb3cfbdb719d9e3dfb1f0555519350c3175 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 16:57:04 +0400 Subject: [PATCH 0571/1472] Add write_lock to KwThreadedSegment Otherwise it is not safe to use. ThreadedSegment does not need write locks. New variant spends much less time locked in both threads --- powerline/lib/threaded.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 63d79e83..e7f10085 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -126,6 +126,8 @@ class KwThreadedSegment(ThreadedSegment): super(KwThreadedSegment, self).__init__() self.updated = True self.update_value = ({}, set()) + self.write_lock = Lock() + self.new_queries = {} @staticmethod def key(**kwargs): @@ -144,7 +146,8 @@ class KwThreadedSegment(ThreadedSegment): # configuration or in subclasses. update_state = self.compute_state(key) if update_first and self.update_first or self.run_once else None - queries[key] = (monotonic(), update_state) + with self.write_lock: + self.new_queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) def update(self, old_update_value): @@ -152,6 +155,11 @@ class KwThreadedSegment(ThreadedSegment): crashed = set() update_value = (updates, crashed) queries = old_update_value[0] + with self.write_lock: + if self.new_queries: + queries.update(self.new_queries) + self.new_queries.clear() + for key, (last_query_time, state) in queries.items(): if last_query_time < monotonic() < last_query_time + self.drop_interval: try: From a31d6f00a74fbfaafec5c3d5427145878da65d58 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 16:58:18 +0400 Subject: [PATCH 0572/1472] Rename update_state to branch in powerline.segments.vim.BranchSegment --- powerline/segments/vim.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f61a98c6..cb05b74d 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -359,15 +359,15 @@ class BranchSegment(RepositorySegment): def process_repo(repo): return repo.branch() - def render_one(self, update_state, segment_info, status_colors=False, **kwargs): - if not update_state: + def render_one(self, branch, segment_info, status_colors=False, **kwargs): + if not branch: return None if status_colors: self.started_repository_status = True return [{ - 'contents': update_state, + 'contents': branch, 'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info, **kwargs) else 'branch_clean'] if status_colors else []) + ['branch'], 'divider_highlight_group': 'branch:divider', From 90c8020e09b7408159e91a00af3dabffef6df204 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 18:12:55 +0400 Subject: [PATCH 0573/1472] Use monotonic clock, not time.time --- powerline/lib/file_watcher.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index b17b4fcf..0be4be09 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -7,7 +7,8 @@ __docformat__ = 'restructuredtext en' import os import sys import errno -import time +from powerline.lib.time import monotonic +from time import sleep from threading import RLock @@ -121,7 +122,7 @@ class INotifyWatch(object): self.process_event(wd, mask, cookie) def expire_watches(self): - now = time.time() + now = monotonic() for path, last_query in tuple(self.last_query.items()): if last_query - now > self.expire_time: self.unwatch(path) @@ -168,7 +169,7 @@ class INotifyWatch(object): raise OSError if the path does not exist. ''' path = self.os.path.abspath(path) with self.lock: - self.last_query[path] = time.time() + self.last_query[path] = monotonic() self.expire_watches() if path not in self.watches: # Try to re-add the watch, it will fail if the file does not @@ -269,7 +270,7 @@ class StatWatch(object): def close(self): with self.lock: - self.watches = {} + self.watches.clear() def create_file_watcher(use_stat=False, expire_time=10): @@ -302,7 +303,7 @@ if __name__ == '__main__': while True: if watcher(sys.argv[-1]): print ('%s has changed' % sys.argv[-1]) - time.sleep(1) + sleep(1) except KeyboardInterrupt: pass watcher.close() From 452e7780eb55723377a9720b375e8abf28c1de83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 21:50:11 +0400 Subject: [PATCH 0574/1472] Add extension to prefix when logging --- powerline/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 0d17e2e8..dab5c8be 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -64,18 +64,20 @@ def load_json_config(config_file_path, load=json.load, open_file=open_file): class PowerlineState(object): - def __init__(self, use_daemon_threads, logger, environ, getcwd, home): + def __init__(self, use_daemon_threads, logger, ext, environ, getcwd, home): self.environ = environ self.getcwd = getcwd self.home = home or environ.get('HOME', None) self.logger = logger + self.ext = ext + self.use_daemon_threads = use_daemon_threads self.prefix = '' self.last_msgs = {} - self.use_daemon_threads = use_daemon_threads def _log(self, attr, msg, *args, **kwargs): prefix = kwargs.get('prefix') or self.prefix - msg = ((prefix + ':') if prefix else '') + msg.format(*args, **kwargs) + prefix = self.ext + ((':' + prefix) if prefix else '') + msg = prefix + ':' + msg.format(*args, **kwargs) key = attr + ':' + prefix if msg != self.last_msgs.get(key): getattr(self.logger, attr)(msg) @@ -217,7 +219,7 @@ class Powerline(object): self.logger.setLevel(level) self.logger.addHandler(handler) - self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.environ, self.getcwd, self.home) + self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext, self.environ, self.getcwd, self.home) self.renderer_options = { 'term_truecolor': self.common_config.get('term_truecolor', False), From 30617385064909f8e2a930aeba65ab7c6eb284d3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 21:51:30 +0400 Subject: [PATCH 0575/1472] Collect all renderer options into one dictionary --- powerline/__init__.py | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index dab5c8be..075ec802 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -166,6 +166,8 @@ class Powerline(object): self.configs = defaultdict(set) self.thread = None + self.renderer_options = {} + if not watcher: watcher = create_file_watcher() self.watcher = MultiClientWatcher() @@ -221,24 +223,24 @@ class Powerline(object): self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext, self.environ, self.getcwd, self.home) - self.renderer_options = { - 'term_truecolor': self.common_config.get('term_truecolor', False), - 'ambiwidth': self.common_config.get('ambiwidth', 1), - 'tmux_escape': self.common_config.get('additional_escapes') == 'tmux', - 'screen_escape': self.common_config.get('additional_escapes') == 'screen', - } - - self.theme_kwargs = { - 'ext': self.ext, - 'common_config': self.common_config, - 'run_once': self.run_once, - } + self.renderer_options.update( + pl=self.pl, + term_truecolor=self.common_config.get('term_truecolor', False), + ambiwidth=self.common_config.get('ambiwidth', 1), + tmux_escape=self.common_config.get('additional_escapes') == 'tmux', + screen_escape=self.common_config.get('additional_escapes') == 'screen', + theme_kwargs={ + 'ext': self.ext, + 'common_config': self.common_config, + 'run_once': self.run_once, + }, + ) self.ext_config = config['ext'][self.ext] if self.ext_config != self.prev_ext_config: ext_config_differs = True if not self.prev_ext_config or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes'): - self.local_themes = self.get_local_themes(self.ext_config.get('local_themes')) + self.renderer_options['local_themes'] = self.get_local_themes(self.ext_config.get('local_themes')) load_colorscheme = (load_colorscheme or not self.prev_ext_config or self.prev_ext_config['colorscheme'] != self.ext_config['colorscheme']) @@ -250,16 +252,16 @@ class Powerline(object): if load_colors: self._purge_configs('colors') - colors_config = self.load_colors_config() + self.colors_config = self.load_colors_config() if load_colorscheme or load_colors: self._purge_configs('colorscheme') colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) - self.colorscheme = Colorscheme(colorscheme_config, colors_config) + self.renderer_options['colorscheme'] = Colorscheme(colorscheme_config, self.colors_config) if load_theme: self._purge_configs('theme') - self.theme_config = self.load_theme_config(self.ext_config.get('theme', 'default')) + self.renderer_options['theme_config'] = self.load_theme_config(self.ext_config.get('theme', 'default')) if create_renderer: try: @@ -273,12 +275,7 @@ class Powerline(object): # but .render still uses old renderer. with self.renderer_lock: try: - renderer = Renderer(self.theme_config, - self.local_themes, - self.theme_kwargs, - self.colorscheme, - self.pl, - **self.renderer_options) + renderer = Renderer(**self.renderer_options) except Exception as e: self.pl.exception('Failed to construct renderer object: {0}', str(e)) if not hasattr(self, 'renderer'): From 854216810eeb3de041f34aea67ed235fd9bfb5da Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 21:52:05 +0400 Subject: [PATCH 0576/1472] Change spaces in vim renderer --- powerline/renderers/vim.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 63900eb4..772004a9 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -76,7 +76,7 @@ class VimRenderer(Renderer): 'window': vim.windows[winidx], 'mode': mode, 'window_id': window_id, - } + } segment_info['buffer'] = segment_info['window'].buffer segment_info['bufnr'] = segment_info['buffer'].number winwidth = segment_info['window'].width @@ -109,7 +109,7 @@ class VimRenderer(Renderer): 'guibg': None, 'attr': ['NONE'], 'name': '', - } + } if fg is not None and fg is not False: hl_group['ctermfg'] = fg[0] hl_group['guifg'] = fg[1] @@ -132,13 +132,13 @@ class VimRenderer(Renderer): ''.join(hl_group['attr']) self.hl_groups[(fg, bg, attr)] = hl_group 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'] is not None else 'NONE', - ctermbg=hl_group['ctermbg'], - guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE', - attr=','.join(hl_group['attr']), - )) + group=hl_group['name'], + ctermfg=hl_group['ctermfg'], + guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] is not None else 'NONE', + ctermbg=hl_group['ctermbg'], + guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE', + attr=','.join(hl_group['attr']), + )) return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' From 3ebc16a48c9bde49bb65be3d6de21ddd6c85cf68 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 21:55:00 +0400 Subject: [PATCH 0577/1472] Replace pl.environ/getcwd/home with segment_info --- powerline/__init__.py | 23 +------ powerline/bindings/zsh/__init__.py | 12 +++- powerline/renderer.py | 20 +++++- powerline/renderers/ipython.py | 5 ++ powerline/renderers/shell.py | 7 ++ powerline/renderers/vim.py | 8 ++- powerline/segments/common.py | 33 ++++++---- powerline/segments/ipython.py | 2 +- powerline/segments/shell.py | 9 +-- scripts/powerline | 9 +-- tests/lib/__init__.py | 20 ++---- tests/test_configuration.py | 8 +-- tests/test_segments.py | 100 ++++++++++++++++++----------- 13 files changed, 151 insertions(+), 105 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 075ec802..6d4ba6f9 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -64,10 +64,7 @@ def load_json_config(config_file_path, load=json.load, open_file=open_file): class PowerlineState(object): - def __init__(self, use_daemon_threads, logger, ext, environ, getcwd, home): - self.environ = environ - self.getcwd = getcwd - self.home = home or environ.get('HOME', None) + def __init__(self, use_daemon_threads, logger, ext): self.logger = logger self.ext = ext self.use_daemon_threads = use_daemon_threads @@ -124,14 +121,6 @@ class Powerline(object): during python session. :param Logger logger: If present, no new logger will be created and this logger will be used. - :param dict environ: - Object with ``.__getitem__`` and ``.get`` methods used to obtain - environment variables. Defaults to ``os.environ``. - :param func getcwd: - Function used to get current working directory. Defaults to - ``os.getcwdu`` or ``os.getcwd``. - :param str home: - Home directory. Defaults to ``environ.get('HOME')``. ''' def __init__(self, @@ -139,18 +128,12 @@ class Powerline(object): renderer_module=None, run_once=False, logger=None, - use_daemon_threads=True, - environ=os.environ, - getcwd=getattr(os, 'getcwdu', os.getcwd), - home=None): + use_daemon_threads=True): global watcher self.ext = ext self.renderer_module = renderer_module or ext self.run_once = run_once self.logger = logger - self.environ = environ - self.getcwd = getcwd - self.home = home self.use_daemon_threads = use_daemon_threads if '.' not in self.renderer_module: @@ -221,7 +204,7 @@ class Powerline(object): self.logger.setLevel(level) self.logger.addHandler(handler) - self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext, self.environ, self.getcwd, self.home) + self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext) self.renderer_options.update( pl=self.pl, diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 4f00129a..1634794e 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -77,6 +77,9 @@ class Environment(object): return default +environ = Environment() + + class Prompt(object): __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args') @@ -88,7 +91,11 @@ class Prompt(object): self.args = powerline.args def __str__(self): - r = self.powerline.render(width=zsh.columns(), side=self.side, segment_info=self.args) + r = self.powerline.render( + width=zsh.columns(), + side=self.side, + segment_info={'args': self.args, 'environ': environ} + ) if type(r) is not str: if type(r) is bytes: return r.decode('utf-8') @@ -113,8 +120,7 @@ def set_prompt(powerline, psvar, side): def setup(): - environ = Environment() - powerline = ShellPowerline(Args(), environ=environ, getcwd=lambda: environ['PWD']) + powerline = ShellPowerline(Args()) used_powerlines.append(powerline) used_powerlines.append(powerline) set_prompt(powerline, 'PS1', 'left') diff --git a/powerline/renderer.py b/powerline/renderer.py index 7e6433fe..6a2a62c4 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -2,6 +2,7 @@ from powerline.theme import Theme from unicodedata import east_asian_width, combining +import os try: @@ -18,7 +19,19 @@ def construct_returned_value(rendered_highlighted, segments, output_raw): class Renderer(object): - def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, pl, **options): + segment_info = { + 'environ': os.environ, + 'getcwd': getattr(os, 'getcwdu', os.getcwd), + 'home': os.environ.get('HOME'), + } + + def __init__(self, + theme_config, + local_themes, + theme_kwargs, + colorscheme, + pl, + **options): self.__dict__.update(options) self.theme_config = theme_config theme_kwargs['pl'] = pl @@ -53,6 +66,9 @@ class Renderer(object): segment['divider_highlight'] = None return segment + def get_segment_info(self, segment_info): + return segment_info or self.segment_info + def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. @@ -63,7 +79,7 @@ class Renderer(object): reached. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side, segment_info) + segments = theme.get_segments(side, self.get_segment_info(segment_info)) # Handle excluded/included segments for the current mode segments = [self.get_highlighting(segment, mode) for segment in segments diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index f2d936c2..5695a228 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -9,6 +9,11 @@ class IpythonRenderer(ShellRenderer): escape_hl_start = '\x01' escape_hl_end = '\x02' + def get_segment_info(self, segment_info): + r = self.segment_info.copy() + r['ipython'] = segment_info + return r + def get_theme(self, matcher_info): if matcher_info == 'in': return self.theme diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 945dc765..da920acb 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -19,6 +19,13 @@ class ShellRenderer(Renderer): tmux_escape = False screen_escape = False + def get_segment_info(self, segment_info): + r = self.segment_info.copy() + r.update(segment_info) + if 'PWD' in r['environ']: + r['getcwd'] = lambda: r['environ']['PWD'] + return r + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 772004a9..4116bdae 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -79,8 +79,14 @@ class VimRenderer(Renderer): } segment_info['buffer'] = segment_info['window'].buffer segment_info['bufnr'] = segment_info['buffer'].number + segment_info.update(self.segment_info) winwidth = segment_info['window'].width - statusline = super(VimRenderer, self).render(mode, winwidth, segment_info=segment_info, matcher_info=segment_info) + statusline = super(VimRenderer, self).render( + mode=mode, + width=winwidth, + segment_info=segment_info, + matcher_info=segment_info, + ) return statusline def reset_highlight(self): diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 4c3e8739..0c05fb07 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -15,29 +15,32 @@ from powerline.lib.vcs import guess from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring from powerline.lib.time import monotonic from powerline.lib.humanize_bytes import humanize_bytes +from powerline.theme import requires_segment_info from collections import namedtuple from time import sleep -def hostname(pl, only_if_ssh=False): +@requires_segment_info +def hostname(pl, segment_info, only_if_ssh=False): '''Return the current hostname. :param bool only_if_ssh: only return the hostname if currently in an SSH session ''' - if only_if_ssh and not pl.environ.get('SSH_CLIENT'): + if only_if_ssh and not segment_info['environ'].get('SSH_CLIENT'): return None return socket.gethostname() +@requires_segment_info class RepositorySegment(KwThreadedSegment): def __init__(self): super(RepositorySegment, self).__init__() self.directories = {} @staticmethod - def key(pl, **kwargs): - return os.path.abspath(pl.getcwd()) + def key(segment_info, **kwargs): + return os.path.abspath(segment_info['getcwd']()) def update(self, *args): # .compute_state() is running only in this method, and only in one @@ -110,7 +113,8 @@ Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. ''') -def cwd(pl, dir_shorten_len=None, dir_limit_depth=None): +@requires_segment_info +def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None): '''Return the current working directory. Returns a segment list to create a breadcrumb-like effect. @@ -127,7 +131,7 @@ def cwd(pl, dir_shorten_len=None, dir_limit_depth=None): ''' import re try: - cwd = pl.getcwd() + cwd = segment_info['getcwd']() except OSError as e: if e.errno == 2: # user most probably deleted the directory @@ -136,7 +140,7 @@ def cwd(pl, dir_shorten_len=None, dir_limit_depth=None): cwd = "[not found]" else: raise - home = pl.home + home = segment_info['home'] if home: cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) @@ -540,7 +544,7 @@ try: if data: yield interface, data.bytes_recv, data.bytes_sent - def _get_user(pl): + def _get_user(segment_info): return psutil.Process(os.getpid()).username def cpu_load_percent(pl, measure_interval=.5): @@ -567,8 +571,8 @@ except ImportError: if x is not None: yield interface, x[0], x[1] - def _get_user(pl): # NOQA - return pl.environ.get('USER', None) + def _get_user(segment_info): # NOQA + return segment_info['environ'].get('USER', None) def cpu_load_percent(pl, measure_interval=.5): # NOQA '''Return the average CPU load as a percentage. @@ -587,7 +591,7 @@ username = False _geteuid = getattr(os, 'geteuid', lambda: 1) -def user(pl): +def user(pl, segment_info=None): '''Return the current user. Highlights the user with the ``superuser`` if the effective user ID is 0. @@ -605,6 +609,8 @@ def user(pl): 'contents': username, 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], }] +if 'psutil' in globals(): + user = requires_segment_info(user) if os.path.exists('/proc/uptime'): @@ -765,9 +771,10 @@ Highlight groups used: ``network_load_sent_gradient`` (gradient) or ``network_lo ''') -def virtualenv(pl): +@requires_segment_info +def virtualenv(pl, segment_info): '''Return the name of the current Python virtualenv.''' - return os.path.basename(pl.environ.get('VIRTUAL_ENV', '')) or None + return os.path.basename(segment_info['environ'].get('VIRTUAL_ENV', '')) or None _IMAPKey = namedtuple('Key', 'username password server port folder') diff --git a/powerline/segments/ipython.py b/powerline/segments/ipython.py index b9fa8fba..9a29ea8e 100644 --- a/powerline/segments/ipython.py +++ b/powerline/segments/ipython.py @@ -5,4 +5,4 @@ from powerline.theme import requires_segment_info @requires_segment_info def prompt_count(pl, segment_info): - return str(segment_info.prompt_count) + return str(segment_info['ipython'].prompt_count) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index acad81a7..66699ac3 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -9,9 +9,9 @@ def last_status(pl, segment_info): Highlight groups used: ``exit_fail`` ''' - if not segment_info.last_exit_code: + if not segment_info['args'].last_exit_code: return None - return [{'contents': str(segment_info.last_exit_code), 'highlight_group': 'exit_fail'}] + return [{'contents': str(segment_info['args'].last_exit_code), 'highlight_group': 'exit_fail'}] @requires_segment_info @@ -20,8 +20,9 @@ def last_pipe_status(pl, segment_info): Highlight groups used: ``exit_fail``, ``exit_success`` ''' - if any(segment_info.last_pipe_status): + last_pipe_status = segment_info['args'].last_pipe_status + if any(last_pipe_status): return [{"contents": str(status), "highlight_group": "exit_fail" if status else "exit_success"} - for status in segment_info.last_pipe_status] + for status in last_pipe_status] else: return None diff --git a/scripts/powerline b/scripts/powerline index dcc23fa5..1341861e 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -13,11 +13,12 @@ except ImportError: if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() - kwargs = {} - if 'PWD' in os.environ: - kwargs['getcwd'] = lambda: os.environ['PWD'] powerline = ShellPowerline(args, run_once=True, **kwargs) - rendered = powerline.render(width=args.width, side=args.side, segment_info=args) + rendered = powerline.render( + width=args.width, + side=args.side, + segment_info={'args': args, 'environ': os.environ}, + ) try: sys.stdout.write(rendered) except UnicodeEncodeError: diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 69cc7b7a..fd99c4ab 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet import imp import sys +import os class Pl(object): @@ -8,18 +9,9 @@ class Pl(object): self.errors = [] self.warns = [] self.debugs = [] - self._cwd = None self.prefix = None - self.environ = {} - self.home = None self.use_daemon_threads = True - def getcwd(self): - if isinstance(self._cwd, Exception): - raise self._cwd - else: - return self._cwd - for meth in ('error', 'warn', 'debug'): exec (('def {0}(self, msg, *args, **kwargs):\n' ' self.{0}s.append((kwargs.get("prefix") or self.prefix, msg, args, kwargs))\n').format(meth)) @@ -133,9 +125,7 @@ class ItemReplace(object): self.d[self.key] = self.old -def replace_env(key, new, d=None): - r = None - if not d: - r = Pl() - d = r.environ - return ItemReplace(d, key, new, r) +def replace_env(key, new, environ=None, **kwargs): + r = kwargs.copy() + r['environ'] = environ or {} + return ItemReplace(r['environ'], key, new, r) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index f5d766c5..81c9b624 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -65,17 +65,17 @@ class TestConfig(TestCase): from powerline.shell import ShellPowerline args = Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt') with ShellPowerline(args, run_once=False) as powerline: - powerline.render(segment_info=args) + powerline.render(segment_info={'args': args}) with ShellPowerline(args, run_once=False) as powerline: - powerline.render(segment_info=args) + powerline.render(segment_info={'args': args}) def test_bash(self): from powerline.shell import ShellPowerline args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]) with ShellPowerline(args, run_once=False) as powerline: - powerline.render(segment_info=args) + powerline.render(segment_info={'args': args}) with ShellPowerline(args, run_once=False) as powerline: - powerline.render(segment_info=args) + powerline.render(segment_info={'args': args}) def test_ipython(self): from powerline.ipython import IpythonPowerline diff --git a/tests/test_segments.py b/tests/test_segments.py index da9bc6a8..a2fcf44b 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -14,15 +14,22 @@ vim = None class TestShell(TestCase): def test_last_status(self): pl = Pl() - self.assertEqual(shell.last_status(pl=pl, segment_info=Args(last_exit_code=10)), + segment_info = {'args': Args(last_exit_code=10)} + self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), [{'contents': '10', 'highlight_group': 'exit_fail'}]) - self.assertEqual(shell.last_status(pl=pl, segment_info=Args(last_exit_code=None)), None) + segment_info['args'].last_exit_code=0 + self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), None) + segment_info['args'].last_exit_code=None + self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), None) def test_last_pipe_status(self): pl = Pl() - self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[])), None) - self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[0, 0, 0])), None) - self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[0, 2, 0])), + segment_info = {'args': Args(last_pipe_status=[])} + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), None) + segment_info['args'].last_pipe_status=[0, 0, 0] + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), None) + segment_info['args'].last_pipe_status=[0, 2, 0] + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), [{'contents': '0', 'highlight_group': 'exit_success'}, {'contents': '2', 'highlight_group': 'exit_fail'}, {'contents': '0', 'highlight_group': 'exit_success'}]) @@ -30,77 +37,93 @@ class TestShell(TestCase): class TestCommon(TestCase): def test_hostname(self): - with replace_env('SSH_CLIENT', '192.168.0.12 40921 22') as pl: + pl = Pl() + with replace_env('SSH_CLIENT', '192.168.0.12 40921 22') as segment_info: with replace_module_module(common, 'socket', gethostname=lambda: 'abc'): - self.assertEqual(common.hostname(pl=pl), 'abc') - self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), 'abc') - pl.environ.pop('SSH_CLIENT') - self.assertEqual(common.hostname(pl=pl), 'abc') - self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), None) + self.assertEqual(common.hostname(pl=pl, segment_info=segment_info), 'abc') + self.assertEqual(common.hostname(pl=pl, segment_info=segment_info, only_if_ssh=True), 'abc') + segment_info['environ'].pop('SSH_CLIENT') + self.assertEqual(common.hostname(pl=pl, segment_info=segment_info), 'abc') + self.assertEqual(common.hostname(pl=pl, segment_info=segment_info, only_if_ssh=True), None) def test_user(self): new_os = new_module('os', getpid=lambda: 1) new_psutil = new_module('psutil', Process=lambda pid: Args(username='def')) - with replace_env('USER', 'def') as pl: + pl = Pl() + with replace_env('USER', 'def') as segment_info: with replace_attr(common, 'os', new_os): with replace_attr(common, 'psutil', new_psutil): with replace_attr(common, '_geteuid', lambda: 5): - self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}]) + self.assertEqual(common.user(pl=pl, segment_info=segment_info), [ + {'contents': 'def', 'highlight_group': 'user'} + ]) with replace_attr(common, '_geteuid', lambda: 0): - self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}]) + self.assertEqual(common.user(pl=pl, segment_info=segment_info), [ + {'contents': 'def', 'highlight_group': ['superuser', 'user']} + ]) def test_branch(self): pl = Pl() - pl._cwd = os.getcwd() + segment_info = {'getcwd': os.getcwd} with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): - self.assertEqual(common.branch(pl=pl, status_colors=False), 'tests') - self.assertEqual(common.branch(pl=pl, status_colors=True), + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), 'tests') + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): - self.assertEqual(common.branch(pl=pl, status_colors=False), 'tests') - self.assertEqual(common.branch(pl=pl, status_colors=True), + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), 'tests') + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) - self.assertEqual(common.branch(pl=pl), 'tests') + self.assertEqual(common.branch(pl=pl, segment_info=segment_info), 'tests') with replace_attr(common, 'guess', lambda path: None): - self.assertEqual(common.branch(pl=pl), None) + self.assertEqual(common.branch(pl=pl, segment_info=segment_info), None) def test_cwd(self): new_os = new_module('os', path=os.path, sep='/') pl = Pl() + cwd = [None] + + def getcwd(): + wd = cwd[0] + if isinstance(wd, Exception): + raise wd + else: + return wd + + segment_info = {'getcwd': getcwd, 'home': None} with replace_attr(common, 'os', new_os): - pl._cwd = '/abc/def/ghi/foo/bar' - self.assertEqual(common.cwd(pl=pl), + cwd[0] = '/abc/def/ghi/foo/bar' + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'def', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - pl.home = '/abc/def/ghi' - self.assertEqual(common.cwd(pl=pl), + segment_info['home'] = '/abc/def/ghi' + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(pl=pl, dir_limit_depth=3), + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3), [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(pl=pl, dir_limit_depth=1), + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), [{'contents': '⋯', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(pl=pl, dir_limit_depth=2, dir_shorten_len=2), + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'fo', 'divider_highlight_group': 'cwd:divider'}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) ose = OSError() ose.errno = 2 - pl._cwd = ose - self.assertEqual(common.cwd(pl=pl, dir_limit_depth=2, dir_shorten_len=2), + cwd[0] = ose + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - pl._cwd = OSError() - self.assertRaises(OSError, common.cwd, pl=pl, dir_limit_depth=2, dir_shorten_len=2) - pl._cwd = ValueError() - self.assertRaises(ValueError, common.cwd, pl=pl, dir_limit_depth=2, dir_shorten_len=2) + cwd[0] = OSError() + self.assertRaises(OSError, common.cwd, pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2) + cwd[0] = ValueError() + self.assertRaises(ValueError, common.cwd, pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2) def test_date(self): pl = Pl() @@ -257,10 +280,11 @@ class TestCommon(TestCase): common.network_load.shutdown() def test_virtualenv(self): - with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as pl: - self.assertEqual(common.virtualenv(pl=pl), 'ghi') - pl.environ.pop('VIRTUAL_ENV') - self.assertEqual(common.virtualenv(pl=pl), None) + pl = Pl() + with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as segment_info: + self.assertEqual(common.virtualenv(pl=pl, segment_info=segment_info), 'ghi') + segment_info['environ'].pop('VIRTUAL_ENV') + self.assertEqual(common.virtualenv(pl=pl, segment_info=segment_info), None) def test_email_imap_alert(self): # TODO From 26412c3aff944a5682be55ca3cc97689d184653b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Mar 2013 23:57:21 +0400 Subject: [PATCH 0578/1472] Add watching interval customization, some fixes Custom interval (zero) will be used in tests. Fixes: - wrong check for subscribers receiving all events - missing prev_ext_config setting that meant theme and colorscheme always reloaded if main configuration was reloaded --- powerline/__init__.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 6d4ba6f9..cc290f1c 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -28,7 +28,7 @@ class MultiClientWatcher(object): def __call__(self, file): if file in self.received_events and self not in self.received_events[file]: self.received_events[file].add(self) - if self.received_events >= self.subscribers: + if self.received_events[file] >= self.subscribers: self.received_events.pop(file) return True @@ -121,6 +121,9 @@ class Powerline(object): during python session. :param Logger logger: If present, no new logger will be created and this logger will be used. + :param float interval: + When reloading configuration wait for this amount of seconds. Set it to + None if you don’t want to reload configuration automatically. ''' def __init__(self, @@ -128,13 +131,15 @@ class Powerline(object): renderer_module=None, run_once=False, logger=None, - use_daemon_threads=True): + use_daemon_threads=True, + interval=10): global watcher self.ext = ext self.renderer_module = renderer_module or ext self.run_once = run_once self.logger = logger self.use_daemon_threads = use_daemon_threads + self.interval = interval if '.' not in self.renderer_module: self.renderer_module = 'powerline.renderers.' + self.renderer_module @@ -230,6 +235,7 @@ class Powerline(object): load_theme = (load_theme or not self.prev_ext_config or self.prev_ext_config['theme'] != self.ext_config['theme']) + self.prev_ext_config = self.ext_config create_renderer = load_colors or load_colorscheme or load_theme or common_config_differs or ext_config_differs @@ -266,7 +272,7 @@ class Powerline(object): else: self.renderer = renderer - if not self.run_once and not self.is_alive(): + if not self.run_once and not self.is_alive() and self.interval is not None: self.start() def get_log_handler(self): @@ -408,7 +414,7 @@ class Powerline(object): self.create_renderer(**kwargs) except Exception as e: self.pl.exception('Failed to create renderer: {0}', str(e)) - self.shutdown_event.wait(10) + self.shutdown_event.wait(self.interval) def __enter__(self): return self From c2ceac093f356166ef0d1bb1fd4be8c28c8b263e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 00:02:53 +0400 Subject: [PATCH 0579/1472] Add replace_item with function, remove os import --- tests/lib/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index fd99c4ab..85f196fd 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,7 +1,6 @@ # vim:fileencoding=utf-8:noet import imp import sys -import os class Pl(object): @@ -125,6 +124,10 @@ class ItemReplace(object): self.d[self.key] = self.old +def replace_item(d, key, new): + return ItemReplace(d, key, new, d) + + def replace_env(key, new, environ=None, **kwargs): r = kwargs.copy() r['environ'] = environ or {} From c6be4426d3bd9660971b713d8e9545b87dc11a2a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 00:03:35 +0400 Subject: [PATCH 0580/1472] Fix whitespace errors --- tests/test_segments.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index a2fcf44b..73cf2fa6 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -17,18 +17,18 @@ class TestShell(TestCase): segment_info = {'args': Args(last_exit_code=10)} self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), [{'contents': '10', 'highlight_group': 'exit_fail'}]) - segment_info['args'].last_exit_code=0 + segment_info['args'].last_exit_code = 0 self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), None) - segment_info['args'].last_exit_code=None + segment_info['args'].last_exit_code = None self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), None) def test_last_pipe_status(self): pl = Pl() segment_info = {'args': Args(last_pipe_status=[])} self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), None) - segment_info['args'].last_pipe_status=[0, 0, 0] + segment_info['args'].last_pipe_status = [0, 0, 0] self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), None) - segment_info['args'].last_pipe_status=[0, 2, 0] + segment_info['args'].last_pipe_status = [0, 2, 0] self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), [{'contents': '0', 'highlight_group': 'exit_success'}, {'contents': '2', 'highlight_group': 'exit_fail'}, From 5a50acf16b0b85c5aae7e509f391a484c6f2cb38 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 00:06:49 +0400 Subject: [PATCH 0581/1472] Add initial tests for configuration reloading Currently only: - Test for not reloading configuration if run_once is not True - Test for reloading main configuration and other configurations triggered by changes in main configuration file TODO: tests for all other configuration files reloading (colors, colorscheme, theme). --- tests/test_config_reload.py | 212 ++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 tests/test_config_reload.py diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py new file mode 100644 index 00000000..47903142 --- /dev/null +++ b/tests/test_config_reload.py @@ -0,0 +1,212 @@ +# vim:fileencoding=utf-8:noet + +import powerline as powerline_module +import time +from powerline.renderer import Renderer +from tests import TestCase +from tests.lib import replace_item +from copy import deepcopy +from threading import Lock + + +class Watcher(object): + events = set() + + def watch(self, file): + pass + + def __call__(self, file): + if file in self.events: + self.events.remove(file) + return True + return False + + def _add(self, file): + self.events.add(file) + + +class Logger(object): + def __init__(self): + self.messages = [] + self.lock = Lock() + + def _add_msg(self, msg): + with self.lock: + self.messages.append(msg) + + def _pop_msgs(self): + with self.lock: + r = self.messages + self.messages = [] + return r + + def __getattr__(self, attr): + return self._add_msg + + +config = { + 'config': { + 'common': { + 'dividers': { + }, + 'spaces': 0, + }, + 'ext': { + 'test': { + 'theme': 'default', + 'colorscheme': 'default', + }, + }, + }, + 'colors': { + 'colors': { + }, + 'gradients': { + }, + }, + 'colorschemes/test/default': { + 'groups': { + }, + }, + 'themes/test/default': { + 'segments': { + }, + }, +} + + +access_log = [] +access_lock = Lock() + + +def load_json_config(config_file_path, *args, **kwargs): + global access_log + with access_lock: + access_log.append(config_file_path) + try: + return deepcopy(config[config_file_path]) + except KeyError: + raise IOError(config_file_path) + + +def find_config_file(search_paths, config_file): + return config_file + + +class SimpleRenderer(Renderer): + def hlstyle(self, fg=None, bg=None, attr=None): + return '<{fg} {bg} {attr}>'.format(fg=fg, bg=bg, attr=attr) + + +class TestPowerline(powerline_module.Powerline): + @staticmethod + def get_local_themes(local_themes): + return local_themes + + +renderer = SimpleRenderer + + +def get_powerline(**kwargs): + return TestPowerline(ext='test', renderer_module='tests.test_config_reload', interval=0, logger=Logger(), **kwargs) + + +# Minimum sleep time on my system is 0.000001, otherwise it fails to update +# config +# This sleep time is 100 times bigger +def sleep(interval=0.0001): + time.sleep(interval) + + +def add_watcher_events(*args): + Watcher.events.update(args) + sleep() + + +class TestConfigReload(TestCase): + def assertAccessEvents(self, *args): + global access_log + with access_lock: + self.assertEqual(set(access_log), set(args)) + access_log = [] + + def test_noreload(self): + with get_powerline(run_once=True) as p: + with replace_item(globals(), 'config', deepcopy(config)): + self.assertEqual(p.render(), '') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + config['config']['common']['spaces'] = 1 + add_watcher_events('config') + # When running once thread should not start + self.assertAccessEvents() + self.assertEqual(p.render(), '') + self.assertEqual(p.logger._pop_msgs(), []) + + def test_reload_main(self): + with get_powerline(run_once=False) as p: + with replace_item(globals(), 'config', deepcopy(config)): + self.assertEqual(p.render(), '') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + + config['config']['common']['spaces'] = 1 + add_watcher_events('config') + self.assertAccessEvents('config') + self.assertEqual(p.render(), '') + self.assertEqual(p.logger._pop_msgs(), []) + + config['config']['ext']['test']['theme'] = 'new' + add_watcher_events('config') + self.assertAccessEvents('config', 'themes/test/new') + self.assertEqual(p.render(), '') + # It should normally handle file missing error + self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: themes/test/new']) + + config['config']['ext']['test']['theme'] = 'default' + add_watcher_events('config') + self.assertAccessEvents('config', 'themes/test/default') + self.assertEqual(p.render(), '') + self.assertEqual(p.logger._pop_msgs(), []) + + config['config']['ext']['test']['colorscheme'] = 'new' + add_watcher_events('config') + self.assertAccessEvents('config', 'colorschemes/test/new') + self.assertEqual(p.render(), '') + # It should normally handle file missing error + self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: colorschemes/test/new']) + + config['config']['ext']['test']['colorscheme'] = 'default' + add_watcher_events('config') + self.assertAccessEvents('config', 'colorschemes/test/default') + self.assertEqual(p.render(), '') + self.assertEqual(p.logger._pop_msgs(), []) + + self.assertEqual(p.renderer.local_themes, None) + config['config']['ext']['test']['local_themes'] = 'something' + add_watcher_events('config') + self.assertAccessEvents('config') + self.assertEqual(p.render(), '') + self.assertEqual(p.logger._pop_msgs(), []) + self.assertEqual(p.renderer.local_themes, 'something') + + +replaces = { + 'watcher': Watcher(), + 'load_json_config': load_json_config, + 'find_config_file': find_config_file, +} + + +def swap_attributes(): + global replaces + for attr, val in replaces.items(): + old_val = getattr(powerline_module, attr) + setattr(powerline_module, attr, val) + replaces[attr] = old_val + + +tearDownModule = setUpModule = swap_attributes + + +if __name__ == '__main__': + from tests import main + main() From e03e864f6991b185fcb6c77d729f95be199049b1 Mon Sep 17 00:00:00 2001 From: Matt Spaulding Date: Sat, 30 Mar 2013 15:09:01 -0700 Subject: [PATCH 0582/1472] Add exclude_domain option to hostname segment Provides an option to return only the hostname if an fqdn is returned by socket.gethostname() --- powerline/segments/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 3537b687..0f514710 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -18,14 +18,18 @@ from powerline.lib.humanize_bytes import humanize_bytes from collections import namedtuple -def hostname(pl, only_if_ssh=False): +def hostname(pl, only_if_ssh=False, exclude_domain=False): '''Return the current hostname. :param bool only_if_ssh: only return the hostname if currently in an SSH session + :param bool exclude_domain: + return the hostname without domain if there is one ''' if only_if_ssh and not pl.environ.get('SSH_CLIENT'): return None + if exclude_domain: + return socket.gethostname().split('.')[0] return socket.gethostname() From 3a6c0ab8fdbfea9fe0634aa1a1f9d28b7dd4ff1e Mon Sep 17 00:00:00 2001 From: Matt Spaulding Date: Sat, 30 Mar 2013 15:32:29 -0700 Subject: [PATCH 0583/1472] Add test for hostname exclude_domain option --- tests/test_segments.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index 87c460f1..be7828de 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -37,6 +37,16 @@ class TestCommon(TestCase): pl.environ.pop('SSH_CLIENT') self.assertEqual(common.hostname(pl=pl), 'abc') self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), None) + with replace_env('SSH_CLIENT', '192.168.0.12 40921 22') as pl: + with replace_module_module(common, 'socket', gethostname=lambda: 'abc.mydomain'): + self.assertEqual(common.hostname(pl=pl), 'abc.mydomain') + self.assertEqual(common.hostname(pl=pl, exclude_domain=True), 'abc') + self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), 'abc.mydomain') + self.assertEqual(common.hostname(pl=pl, only_if_ssh=True, exclude_domain=True), 'abc') + pl.environ.pop('SSH_CLIENT') + self.assertEqual(common.hostname(pl=pl), 'abc.mydomain') + self.assertEqual(common.hostname(pl=pl, exclude_domain=True), 'abc') + self.assertEqual(common.hostname(pl=pl, only_if_ssh=True, exclude_domain=True), None) def test_user(self): new_os = new_module('os', getpid=lambda: 1) From e35b1541e8b5cfa637bef332fe7f9cb3f1bf8350 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 13:36:41 +0400 Subject: [PATCH 0584/1472] Make it possible to supply different watcher Used in tests --- powerline/__init__.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index cc290f1c..e601a690 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -22,10 +22,19 @@ class MultiClientWatcher(object): subscribers = set() received_events = {} + def __init__(self): + global watcher + self.subscribers.add(self) + if not watcher: + watcher = create_file_watcher() + def watch(self, file): watcher.watch(file) def __call__(self, file): + if self not in self.subscribers: + return False + if file in self.received_events and self not in self.received_events[file]: self.received_events[file].add(self) if self.received_events[file] >= self.subscribers: @@ -38,12 +47,14 @@ class MultiClientWatcher(object): return False - def __del__(self): + def unsubscribe(self): try: self.subscribers.remove(self) except KeyError: pass + __del__ = unsubscribe + def open_file(path): return open(path, 'r') @@ -132,8 +143,8 @@ class Powerline(object): run_once=False, logger=None, use_daemon_threads=True, - interval=10): - global watcher + interval=10, + watcher=None): self.ext = ext self.renderer_module = renderer_module or ext self.run_once = run_once @@ -156,9 +167,7 @@ class Powerline(object): self.renderer_options = {} - if not watcher: - watcher = create_file_watcher() - self.watcher = MultiClientWatcher() + self.watcher = watcher or MultiClientWatcher() self.prev_common_config = None self.prev_ext_config = None @@ -245,8 +254,9 @@ class Powerline(object): if load_colorscheme or load_colors: self._purge_configs('colorscheme') - colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) - self.renderer_options['colorscheme'] = Colorscheme(colorscheme_config, self.colors_config) + if load_colorscheme: + self.colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) + self.renderer_options['colorscheme'] = Colorscheme(self.colorscheme_config, self.colors_config) if load_theme: self._purge_configs('theme') @@ -311,7 +321,6 @@ class Powerline(object): def _load_config(self, cfg_path, type): '''Load configuration and setup watcher.''' - global watcher path = find_config_file(self.config_paths, cfg_path) with self.configs_lock: self.configs[type].add(path) @@ -386,10 +395,11 @@ class Powerline(object): '''Lock renderer from modifications and run its ``.shutdown()`` method. ''' self.shutdown_event.set() - with self.renderer_lock: - self.renderer.shutdown() if self.use_daemon_threads and self.is_alive(): self.thread.join() + with self.renderer_lock: + self.renderer.shutdown() + self.watcher.unsubscribe() def is_alive(self): return self.thread and self.thread.is_alive() @@ -401,7 +411,6 @@ class Powerline(object): self.thread.start() def run(self): - global watcher while not self.shutdown_event.is_set(): kwargs = {} with self.configs_lock: From 8c3be65bbab0893bd007073ffa4a1478a561c4b9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 13:37:07 +0400 Subject: [PATCH 0585/1472] Added test for colors config reloading --- tests/test_config_reload.py | 131 ++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 29 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 47903142..57c47c63 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet - +from __future__ import unicode_literals import powerline as powerline_module import time from powerline.renderer import Renderer @@ -11,18 +11,25 @@ from threading import Lock class Watcher(object): events = set() + lock = Lock() def watch(self, file): pass def __call__(self, file): if file in self.events: - self.events.remove(file) + with self.lock: + self.events.remove(file) return True return False - def _add(self, file): - self.events.add(file) + def _reset(self, files): + with self.lock: + self.events.clear() + self.events.update(files) + + def unsubscribe(self): + pass class Logger(object): @@ -48,6 +55,14 @@ config = { 'config': { 'common': { 'dividers': { + "left": { + "hard": ">>", + "soft": ">", + }, + "right": { + "hard": "<<", + "soft": "<", + }, }, 'spaces': 0, }, @@ -60,16 +75,36 @@ config = { }, 'colors': { 'colors': { + "col1": 1, + "col2": 2, + "col3": 3, + "col4": 4, }, 'gradients': { }, }, 'colorschemes/test/default': { 'groups': { + "str1": {"fg": "col1", "bg": "col2", "attr": ["bold"]}, + "str2": {"fg": "col3", "bg": "col4", "attr": ["underline"]}, }, }, 'themes/test/default': { 'segments': { + "left": [ + { + "type": "string", + "contents": "s", + "highlight_group": ["str1"], + }, + { + "type": "string", + "contents": "g", + "highlight_group": ["str2"], + }, + ], + "right": [ + ], }, }, } @@ -95,32 +130,55 @@ def find_config_file(search_paths, config_file): class SimpleRenderer(Renderer): def hlstyle(self, fg=None, bg=None, attr=None): - return '<{fg} {bg} {attr}>'.format(fg=fg, bg=bg, attr=attr) + return '<{fg} {bg} {attr}>'.format(fg=fg and fg[0], bg=bg and bg[0], attr=attr) class TestPowerline(powerline_module.Powerline): + _created = False + @staticmethod def get_local_themes(local_themes): return local_themes + def create_renderer(self, *args, **kwargs): + try: + r = super(TestPowerline, self).create_renderer(*args, **kwargs) + finally: + self._created = True + return r + + def _created_renderer(self): + if self._created: + self._created = False + return True + return False + renderer = SimpleRenderer def get_powerline(**kwargs): - return TestPowerline(ext='test', renderer_module='tests.test_config_reload', interval=0, logger=Logger(), **kwargs) + return TestPowerline( + ext='test', + renderer_module='tests.test_config_reload', + interval=0, + logger=Logger(), + watcher=Watcher(), + **kwargs + ) -# Minimum sleep time on my system is 0.000001, otherwise it fails to update -# config -# This sleep time is 100 times bigger -def sleep(interval=0.0001): +def sleep(interval): time.sleep(interval) -def add_watcher_events(*args): - Watcher.events.update(args) - sleep() +def add_watcher_events(p, *args, **kwargs): + p._created_renderer() + p.watcher._reset(args) + while not p._created_renderer(): + sleep(kwargs.get('interval', 0.000001)) + if not kwargs.get('wait', True): + return class TestConfigReload(TestCase): @@ -133,61 +191,76 @@ class TestConfigReload(TestCase): def test_noreload(self): with get_powerline(run_once=True) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '') self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') config['config']['common']['spaces'] = 1 - add_watcher_events('config') + add_watcher_events(p, 'config', wait=False, interval=0.05) # When running once thread should not start self.assertAccessEvents() - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) + # Without the following assertion test_reload_colors may fail + # for unknown reason (with AssertionError telling about “config” + # accessed one more time then needed) + self.assertAccessEvents() def test_reload_main(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '') self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') config['config']['common']['spaces'] = 1 - add_watcher_events('config') + add_watcher_events(p, 'config') self.assertAccessEvents('config') - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['theme'] = 'new' - add_watcher_events('config') + add_watcher_events(p, 'config') self.assertAccessEvents('config', 'themes/test/new') - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') # It should normally handle file missing error self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: themes/test/new']) config['config']['ext']['test']['theme'] = 'default' - add_watcher_events('config') + add_watcher_events(p, 'config') self.assertAccessEvents('config', 'themes/test/default') - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['colorscheme'] = 'new' - add_watcher_events('config') + add_watcher_events(p, 'config') self.assertAccessEvents('config', 'colorschemes/test/new') - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') # It should normally handle file missing error self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: colorschemes/test/new']) config['config']['ext']['test']['colorscheme'] = 'default' - add_watcher_events('config') + add_watcher_events(p, 'config') self.assertAccessEvents('config', 'colorschemes/test/default') - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, None) config['config']['ext']['test']['local_themes'] = 'something' - add_watcher_events('config') + add_watcher_events(p, 'config') self.assertAccessEvents('config') - self.assertEqual(p.render(), '') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, 'something') + def test_reload_colors(self): + with get_powerline(run_once=False) as p: + with replace_item(globals(), 'config', deepcopy(config)): + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + + config['colors']['colors']['col1'] = 5 + add_watcher_events(p, 'colors') + self.assertAccessEvents('colors') + self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + replaces = { 'watcher': Watcher(), From 54471569ab287677ed731b728801bfd4c50fd32b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 14:53:21 +0400 Subject: [PATCH 0586/1472] Check whether it switches configuration fine --- tests/test_config_reload.py | 50 +++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 57c47c63..8db2ff70 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -89,6 +89,12 @@ config = { "str2": {"fg": "col3", "bg": "col4", "attr": ["underline"]}, }, }, + 'colorschemes/test/2': { + 'groups': { + "str1": {"fg": "col2", "bg": "col3", "attr": ["bold"]}, + "str2": {"fg": "col1", "bg": "col4", "attr": ["underline"]}, + }, + }, 'themes/test/default': { 'segments': { "left": [ @@ -107,6 +113,24 @@ config = { ], }, }, + 'themes/test/2': { + 'segments': { + "left": [ + { + "type": "string", + "contents": "t", + "highlight_group": ["str1"], + }, + { + "type": "string", + "contents": "b", + "highlight_group": ["str2"], + }, + ], + "right": [ + ], + }, + }, } @@ -216,12 +240,12 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - config['config']['ext']['test']['theme'] = 'new' + config['config']['ext']['test']['theme'] = 'nonexistent' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'themes/test/new') + self.assertAccessEvents('config', 'themes/test/nonexistent') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: themes/test/new']) + self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: themes/test/nonexistent']) config['config']['ext']['test']['theme'] = 'default' add_watcher_events(p, 'config') @@ -229,24 +253,30 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - config['config']['ext']['test']['colorscheme'] = 'new' + config['config']['ext']['test']['colorscheme'] = 'nonexistent' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'colorschemes/test/new') + self.assertAccessEvents('config', 'colorschemes/test/nonexistent') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: colorschemes/test/new']) + self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: colorschemes/test/nonexistent']) - config['config']['ext']['test']['colorscheme'] = 'default' + config['config']['ext']['test']['colorscheme'] = '2' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'colorschemes/test/default') - self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents('config', 'colorschemes/test/2') + self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>>') + self.assertEqual(p.logger._pop_msgs(), []) + + config['config']['ext']['test']['theme'] = '2' + add_watcher_events(p, 'config') + self.assertAccessEvents('config', 'themes/test/2') + self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, None) config['config']['ext']['test']['local_themes'] = 'something' add_watcher_events(p, 'config') self.assertAccessEvents('config') - self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, 'something') From d1e79000b56294d99d65fc88e02e3cb2a0ce85ba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 14:57:43 +0400 Subject: [PATCH 0587/1472] Add tests for reloading theme and colorscheme --- tests/test_config_reload.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 8db2ff70..a36d625f 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -291,6 +291,28 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colors') self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + def test_reload_colorscheme(self): + with get_powerline(run_once=False) as p: + with replace_item(globals(), 'config', deepcopy(config)): + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + + config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' + add_watcher_events(p, 'colorschemes/test/default') + self.assertAccessEvents('colorschemes/test/default') + self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') + + def test_reload_theme(self): + with get_powerline(run_once=False) as p: + with replace_item(globals(), 'config', deepcopy(config)): + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + + config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' + add_watcher_events(p, 'themes/test/default') + self.assertAccessEvents('themes/test/default') + self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') + replaces = { 'watcher': Watcher(), From 46b5063ea84ef1e6184239f9c3ce345f56f069da Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 15:01:06 +0400 Subject: [PATCH 0588/1472] Remove unused import --- powerline/segments/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 0c05fb07..de8efd37 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -17,7 +17,6 @@ from powerline.lib.time import monotonic from powerline.lib.humanize_bytes import humanize_bytes from powerline.theme import requires_segment_info from collections import namedtuple -from time import sleep @requires_segment_info From 4d1dc0658b07e9621d8b157a0f06d8a0d1e75d11 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 1 Apr 2013 00:01:51 +0400 Subject: [PATCH 0589/1472] Remove unexistent kwargs --- scripts/powerline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/powerline b/scripts/powerline index 1341861e..925ee438 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -13,7 +13,7 @@ except ImportError: if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() - powerline = ShellPowerline(args, run_once=True, **kwargs) + powerline = ShellPowerline(args, run_once=True) rendered = powerline.render( width=args.width, side=args.side, From b7c61f8bf682f08b343e77c6889d128939a6fa4e Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 00:27:54 +0400 Subject: [PATCH 0590/1472] Handle removed and then added files Note: if user configuration was removed, but global configuration was not it will start tracking global configuration file for changes. --- powerline/__init__.py | 21 +++++++++++++++++++-- tests/test_config_reload.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index e601a690..4935747f 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -163,6 +163,8 @@ class Powerline(object): self.configs_lock = Lock() self.shutdown_event = Event() self.configs = defaultdict(set) + self.missing = defaultdict(set) + self.thread = None self.renderer_options = {} @@ -171,6 +173,7 @@ class Powerline(object): self.prev_common_config = None self.prev_ext_config = None + self.pl = None self.create_renderer(load_main=True, load_colors=True, load_colorscheme=True, load_theme=True) @@ -218,7 +221,8 @@ class Powerline(object): self.logger.setLevel(level) self.logger.addHandler(handler) - self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext) + if not self.pl: + self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext) self.renderer_options.update( pl=self.pl, @@ -321,7 +325,12 @@ class Powerline(object): def _load_config(self, cfg_path, type): '''Load configuration and setup watcher.''' - path = find_config_file(self.config_paths, cfg_path) + try: + path = find_config_file(self.config_paths, cfg_path) + except IOError: + with self.configs_lock: + self.missing[type].add(cfg_path) + raise with self.configs_lock: self.configs[type].add(path) self.watcher.watch(path) @@ -418,6 +427,14 @@ class Powerline(object): for path in paths: if self.watcher(path): kwargs['load_' + type] = True + for type, cfg_paths in self.missing.items(): + for cfg_path in cfg_paths: + try: + find_config_file(self.config_paths, cfg_path) + except IOError: + pass + else: + kwargs['load_' + type] = True if kwargs: try: self.create_renderer(**kwargs) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index a36d625f..d67eeeaf 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -149,6 +149,8 @@ def load_json_config(config_file_path, *args, **kwargs): def find_config_file(search_paths, config_file): + if config_file.endswith('raise') and config_file not in config: + raise IOError('fcf:' + config_file) return config_file @@ -280,6 +282,30 @@ class TestConfigReload(TestCase): self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, 'something') + def test_reload_unexistent(self): + with get_powerline(run_once=False) as p: + with replace_item(globals(), 'config', deepcopy(config)): + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + + config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' + add_watcher_events(p, 'config') + self.assertAccessEvents('config') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise']) + + config['colorschemes/test/nonexistentraise'] = { + 'groups': { + "str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]}, + "str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]}, + }, + } + while not p._created_renderer(): + sleep(0.000001) + self.assertAccessEvents('colorschemes/test/nonexistentraise') + self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') + self.assertEqual(p.logger._pop_msgs(), []) + def test_reload_colors(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): @@ -290,6 +316,7 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'colors') self.assertAccessEvents('colors') self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertEqual(p.logger._pop_msgs(), []) def test_reload_colorscheme(self): with get_powerline(run_once=False) as p: @@ -301,6 +328,7 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'colorschemes/test/default') self.assertAccessEvents('colorschemes/test/default') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') + self.assertEqual(p.logger._pop_msgs(), []) def test_reload_theme(self): with get_powerline(run_once=False) as p: @@ -312,6 +340,7 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'themes/test/default') self.assertAccessEvents('themes/test/default') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertEqual(p.logger._pop_msgs(), []) replaces = { From f15cdd9413ac8ed8387d1e9216837d64024286c1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 23:13:51 +0400 Subject: [PATCH 0591/1472] Replace draw_divider setting with draw_(soft|hard)_divider Previous variant was bad because 1. draw_divider only applied to soft dividers. Hard dividers were always drawn 2. But there was a hack with width=auto segments: for this segments draw_divider setting applied always. Now there are no additional dependencies: draw_*_divider applies no matter what other properties of the segment are. --- docs/source/configuration.rst | 9 ++++-- powerline/config_files/themes/ipython/in.json | 4 +-- .../config_files/themes/ipython/out.json | 4 +-- .../config_files/themes/ipython/rewrite.json | 4 +-- powerline/config_files/themes/vim/cmdwin.json | 3 +- .../config_files/themes/vim/default.json | 17 +++++------ powerline/config_files/themes/vim/help.json | 7 +++-- powerline/lint/__init__.py | 5 ++-- powerline/renderer.py | 5 ++-- powerline/segment.py | 3 +- powerline/segments/common.py | 6 ++-- tests/test_segments.py | 28 +++++++++---------- 12 files changed, 52 insertions(+), 43 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 4651e5ac..0d75df51 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -379,10 +379,13 @@ Themes Segments are removed according to their priority, with low priority segments being removed first. - ``draw_divider`` + ``draw_hard_divider``, ``draw_soft_divider`` Whether to draw a divider between this and the adjacent segment. The - adjacent segment is to the *right* for segments on the *left* side, - and vice versa. + adjacent segment is to the *right* for segments on the *left* side, and + vice versa. Hard dividers are used between segments with different + background colors, soft ones are used between segments with same + background. These options may be overridden by return value of functions + segments. ``exclude_modes`` A list of modes where this segment will be excluded: The segment is diff --git a/powerline/config_files/themes/ipython/in.json b/powerline/config_files/themes/ipython/in.json index bf6be670..ac979c5f 100644 --- a/powerline/config_files/themes/ipython/in.json +++ b/powerline/config_files/themes/ipython/in.json @@ -8,13 +8,13 @@ { "type": "string", "contents": "In[", - "draw_divider": false, + "draw_soft_divider": false, "highlight_group": ["prompt"] }, { "name": "prompt_count", "module": "powerline.segments.ipython", - "draw_divider": false + "draw_soft_divider": false }, { "type": "string", diff --git a/powerline/config_files/themes/ipython/out.json b/powerline/config_files/themes/ipython/out.json index 6d4eac53..11a63234 100644 --- a/powerline/config_files/themes/ipython/out.json +++ b/powerline/config_files/themes/ipython/out.json @@ -5,7 +5,7 @@ { "type": "string", "contents": "Out[", - "draw_divider": false, + "draw_soft_divider": false, "width": "auto", "align": "r", "highlight_group": ["prompt"] @@ -13,7 +13,7 @@ { "name": "prompt_count", "module": "powerline.segments.ipython", - "draw_divider": false + "draw_soft_divider": false }, { "type": "string", diff --git a/powerline/config_files/themes/ipython/rewrite.json b/powerline/config_files/themes/ipython/rewrite.json index 4616769a..47d8de0d 100644 --- a/powerline/config_files/themes/ipython/rewrite.json +++ b/powerline/config_files/themes/ipython/rewrite.json @@ -4,14 +4,14 @@ { "type": "string", "contents": "", - "draw_divider": false, + "draw_soft_divider": false, "width": "auto", "highlight_group": ["prompt"] }, { "name": "prompt_count", "module": "powerline.segments.ipython", - "draw_divider": false + "draw_soft_divider": false }, { "type": "string", diff --git a/powerline/config_files/themes/vim/cmdwin.json b/powerline/config_files/themes/vim/cmdwin.json index cffd9e1c..c300d948 100644 --- a/powerline/config_files/themes/vim/cmdwin.json +++ b/powerline/config_files/themes/vim/cmdwin.json @@ -9,7 +9,8 @@ { "type": "string", "highlight_group": ["background"], - "draw_divider": false, + "draw_soft_divider": false, + "draw_hard_divider": false, "width": "auto" } ] diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 3eeae293..40268a3f 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -32,22 +32,22 @@ }, { "name": "readonly_indicator", - "draw_divider": false, + "draw_soft_divider": false, "after": " " }, { "name": "file_directory", "priority": 40, - "draw_divider": false + "draw_soft_divider": false }, { "name": "file_name", - "draw_divider": false + "draw_soft_divider": false }, { "name": "file_vcs_status", "before": " ", - "draw_divider": false + "draw_soft_divider": false }, { "name": "modified_indicator", @@ -56,14 +56,15 @@ { "type": "string", "highlight_group": ["background"], - "draw_divider": false, + "draw_soft_divider": false, + "draw_hard_divider": false, "width": "auto" } ], "right": [ { "name": "file_format", - "draw_divider": false, + "draw_soft_divider": false, "exclude_modes": ["nc"], "priority": 50 }, @@ -90,13 +91,13 @@ }, { "name": "line_current", - "draw_divider": false, + "draw_soft_divider": false, "width": 3, "align": "r" }, { "name": "virtcol_current", - "draw_divider": false, + "draw_soft_divider": false, "priority": 30, "before": ":", "width": 3, diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index 9fb5320c..74071057 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -3,12 +3,13 @@ "left": [ { "name": "file_name", - "draw_divider": false + "draw_soft_divider": false }, { "type": "string", "highlight_group": ["background"], - "draw_divider": false, + "draw_soft_divider": false, + "draw_hard_divider": false, "width": "auto" } ], @@ -26,7 +27,7 @@ }, { "name": "line_current", - "draw_divider": false, + "draw_soft_divider": false, "width": 3, "align": "r" } diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 31d70798..67de84ce 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -505,7 +505,7 @@ vim_colorscheme_spec = (Spec( ).context_message('Error while loading vim colorscheme')) -generic_keys = set(('exclude_modes', 'include_modes', 'width', 'align', 'name', 'draw_divider', 'priority', 'after', 'before')) +generic_keys = set(('exclude_modes', 'include_modes', 'width', 'align', 'name', 'draw_soft_divider', 'draw_hard_divider', 'priority', 'after', 'before')) type_keys = { 'function': set(('args', 'module')), 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), @@ -797,7 +797,8 @@ segments_spec = Spec().optional().list( name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(), exclude_modes=Spec().list(vim_mode_spec()).optional(), include_modes=Spec().list(vim_mode_spec()).optional(), - draw_divider=Spec().type(bool).optional(), + draw_hard_divider=Spec().type(bool).optional(), + draw_soft_divider=Spec().type(bool).optional(), module=segment_module_spec(), priority=Spec().cmp('ge', -1).optional(), after=Spec().type(unicode).optional(), diff --git a/powerline/renderer.py b/powerline/renderer.py index 6a2a62c4..324aa982 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -144,9 +144,10 @@ class Renderer(object): divider_highlighted = '' contents_raw = segment['contents'] contents_highlighted = '' + draw_divider = segment['draw_' + divider_type + '_divider'] # Pad segments first - if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'): + if draw_divider: if segment['side'] == 'left': contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + ((divider_spaces + segment['_space_right']) * ' ') else: @@ -174,7 +175,7 @@ class Renderer(object): contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) # Append padded raw and highlighted segments to the rendered segment variables - if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'): + if draw_divider: if segment['side'] == 'left': segment['_rendered_raw'] += contents_raw + divider_raw segment['_rendered_hl'] += contents_highlighted + divider_highlighted diff --git a/powerline/segment.py b/powerline/segment.py index 8f166013..8d3c7623 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -74,7 +74,8 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): 'contents': contents, 'args': get_key(segment, module, 'args', {}) if segment_type == 'function' else {}, 'priority': segment.get('priority', -1), - 'draw_divider': segment.get('draw_divider', True), + 'draw_hard_divider': segment.get('draw_hard_divider', True), + 'draw_soft_divider': segment.get('draw_soft_divider', True), 'side': side, 'exclude_modes': segment.get('exclude_modes', []), 'include_modes': segment.get('include_modes', []), diff --git a/powerline/segments/common.py b/powerline/segments/common.py index d1a30d9b..7b7d79d9 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -438,7 +438,7 @@ class WeatherSegment(ThreadedSegment): { 'contents': temp_format.format(temp=converted_temp), 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], - 'draw_divider': False, + 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': gradient_level, }, @@ -521,11 +521,11 @@ def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): ret.append({ 'contents': format.format(avg=avg), 'highlight_group': ['system_load_gradient', 'system_load'], - 'draw_divider': False, + 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': gradient_level, }) - ret[0]['draw_divider'] = True + ret[0]['draw_soft_divider'] = True ret[0]['contents'] += ' ' ret[1]['contents'] += ' ' return ret diff --git a/tests/test_segments.py b/tests/test_segments.py index 40cc7cd0..74e5e966 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -175,35 +175,35 @@ class TestCommon(TestCase): with replace_attr(common, 'urllib_read', urllib_read): self.assertEqual(common.weather(pl=pl), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, temp_coldest=0, temp_hottest=100), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} ]) self.assertEqual(common.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} ]) self.assertEqual(common.weather(pl=pl, icons={'cloudy': 'o'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, icons={'partly_cloudy_day': 'x'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, unit='F'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, unit='K'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, temp_format='{temp:.1e}C'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} + {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} ]) def test_system_load(self): @@ -211,13 +211,13 @@ class TestCommon(TestCase): with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): with replace_attr(common, 'cpu_count', lambda: 2): self.assertEqual(common.system_load(pl=pl), - [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, - {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) + [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, + {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) self.assertEqual(common.system_load(pl=pl, format='{avg:.0f}', threshold_good=0, threshold_bad=1), - [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) + [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) def test_cpu_load_percent(self): pl = Pl() From 66beaaaa9d42f81bda42cd741f461c0c82ac9a8c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Mar 2013 23:47:55 +0400 Subject: [PATCH 0592/1472] Replace draw_soft_divider setting with draw_inner_divider If weather or system_load segments were moved to the left draw_soft_divider variant resulted in incorrect renderring. Thus it was replaced by draw_inner_divider. --- docs/source/configuration.rst | 8 ++++++-- powerline/lint/__init__.py | 3 ++- powerline/segment.py | 1 + powerline/segments/common.py | 3 --- powerline/theme.py | 34 ++++++++++++++++++++++++---------- tests/test_segments.py | 28 ++++++++++++++-------------- 6 files changed, 47 insertions(+), 30 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 0d75df51..c2427383 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -384,8 +384,12 @@ Themes adjacent segment is to the *right* for segments on the *left* side, and vice versa. Hard dividers are used between segments with different background colors, soft ones are used between segments with same - background. These options may be overridden by return value of functions - segments. + background. Both options default to ``True``. + + ``draw_inner_divider`` + Determines whether inner soft dividers are to be drawn for function + segments. Only applicable for functions returning multiple segments. + Defaults to ``False``. ``exclude_modes`` A list of modes where this segment will be excluded: The segment is diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 67de84ce..6b2636af 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -507,7 +507,7 @@ vim_colorscheme_spec = (Spec( generic_keys = set(('exclude_modes', 'include_modes', 'width', 'align', 'name', 'draw_soft_divider', 'draw_hard_divider', 'priority', 'after', 'before')) type_keys = { - 'function': set(('args', 'module')), + 'function': set(('args', 'module', 'draw_inner_divider')), 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), } @@ -799,6 +799,7 @@ segments_spec = Spec().optional().list( include_modes=Spec().list(vim_mode_spec()).optional(), draw_hard_divider=Spec().type(bool).optional(), draw_soft_divider=Spec().type(bool).optional(), + draw_inner_divider=Spec().type(bool).optional(), module=segment_module_spec(), priority=Spec().cmp('ge', -1).optional(), after=Spec().type(unicode).optional(), diff --git a/powerline/segment.py b/powerline/segment.py index 8d3c7623..8a2483ff 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -76,6 +76,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): 'priority': segment.get('priority', -1), 'draw_hard_divider': segment.get('draw_hard_divider', True), 'draw_soft_divider': segment.get('draw_soft_divider', True), + 'draw_inner_divider': segment.get('draw_inner_divider', False), 'side': side, 'exclude_modes': segment.get('exclude_modes', []), 'include_modes': segment.get('include_modes', []), diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 7b7d79d9..c85f6019 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -438,7 +438,6 @@ class WeatherSegment(ThreadedSegment): { 'contents': temp_format.format(temp=converted_temp), 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], - 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': gradient_level, }, @@ -521,11 +520,9 @@ def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): ret.append({ 'contents': format.format(avg=avg), 'highlight_group': ['system_load_gradient', 'system_load'], - 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': gradient_level, }) - ret[0]['draw_soft_divider'] = True ret[0]['contents'] += ' ' ret[1]['contents'] += ' ' return ret diff --git a/powerline/theme.py b/powerline/theme.py index ed5e5788..0b7cdd7a 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,7 +1,5 @@ # vim:fileencoding=utf-8:noet -from copy import copy - from .segment import gen_segment_getter @@ -91,19 +89,35 @@ class Theme(object): if contents is None: continue if isinstance(contents, list): - segment_base = copy(segment) + segment_base = segment.copy() if contents: - for key in ('before', 'after'): + draw_divider_position = -1 if side == 'left' else 0 + for key, i, newval in ( + ('before', 0, ''), + ('after', -1, ''), + ('draw_soft_divider', draw_divider_position, True), + ('draw_hard_divider', draw_divider_position, True), + ): try: - contents[0][key] = segment_base.pop(key) - segment_base[key] = '' + contents[i][key] = segment_base.pop(key) + segment_base[key] = newval except KeyError: pass - for subsegment in contents: - segment_copy = copy(segment_base) + draw_inner_divider = None + if side == 'right': + append = parsed_segments.append + else: + pslen = len(parsed_segments) + append = lambda item: parsed_segments.insert(pslen, item) + + for subsegment in (contents if side == 'right' else reversed(contents)): + segment_copy = segment_base.copy() segment_copy.update(subsegment) - parsed_segments.append(segment_copy) + if draw_inner_divider is not None: + segment_copy['draw_soft_divider'] = draw_inner_divider + draw_inner_divider = segment_copy.pop('draw_inner_divider', None) + append(segment_copy) else: segment['contents'] = contents parsed_segments.append(segment) @@ -124,4 +138,4 @@ class Theme(object): # We need to yield a copy of the segment, or else mode-dependent # segment contents can't be cached correctly e.g. when caching # non-current window contents for vim statuslines - yield copy(segment) + yield segment.copy() diff --git a/tests/test_segments.py b/tests/test_segments.py index 74e5e966..b4c52b31 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -175,35 +175,35 @@ class TestCommon(TestCase): with replace_attr(common, 'urllib_read', urllib_read): self.assertEqual(common.weather(pl=pl), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, temp_coldest=0, temp_hottest=100), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} ]) self.assertEqual(common.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} ]) self.assertEqual(common.weather(pl=pl, icons={'cloudy': 'o'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, icons={'partly_cloudy_day': 'x'}), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, unit='F'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, unit='K'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, temp_format='{temp:.1e}C'), [ {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, - {'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} ]) def test_system_load(self): @@ -211,13 +211,13 @@ class TestCommon(TestCase): with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): with replace_attr(common, 'cpu_count', lambda: 2): self.assertEqual(common.system_load(pl=pl), - [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, - {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) + [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, + {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) self.assertEqual(common.system_load(pl=pl, format='{avg:.0f}', threshold_good=0, threshold_bad=1), - [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_soft_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) + [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) def test_cpu_load_percent(self): pl = Pl() From 992e6151eb8399f57e5703f9e1bc19fc5bed8c1d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 17:37:03 +0400 Subject: [PATCH 0593/1472] Remove update_first set from set_state It is already handled correctly in .render() method --- powerline/lib/threaded.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index e7f10085..5c0cfb3c 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -144,7 +144,7 @@ class KwThreadedSegment(ThreadedSegment): except KeyError: # Allow only to forbid to compute missing values: in either user # configuration or in subclasses. - update_state = self.compute_state(key) if update_first and self.update_first or self.run_once else None + update_state = self.compute_state(key) if ((update_first and self.update_first) or self.run_once) else None with self.write_lock: self.new_queries[key] = (monotonic(), update_state) @@ -173,12 +173,9 @@ class KwThreadedSegment(ThreadedSegment): return update_value - def set_state(self, interval=None, update_first=True, **kwargs): + def set_state(self, interval=None, **kwargs): self.set_interval(interval) - if self.update_first: - self.update_first = update_first - @staticmethod def render_one(update_state, **kwargs): return update_state From b47c2fae156de5750e5800bf62faa543a05d593e Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 18:40:30 +0400 Subject: [PATCH 0594/1472] Pass the correct value to _get_user Fixes #379 --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index d1a30d9b..94bf3b43 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -603,7 +603,7 @@ def user(pl, segment_info=None): ''' global username if username is False: - username = _get_user(pl) + username = _get_user(segment_info) if username is None: pl.warn('Failed to get username') return None From 4e5bd54f79db6a98a33044d4d39dbb1f5fd4eced Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 19:01:03 +0400 Subject: [PATCH 0595/1472] Fix cwd segment, add use_path_separator option cwd segment was not drawing inner separators because of new default for multisegment functions. use_path_separator was added because current variant looks bad for my taste. --- powerline/segments/common.py | 11 +++++-- tests/test_segments.py | 60 ++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c85f6019..87ac2c28 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -117,7 +117,7 @@ Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. @requires_segment_info -def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None): +def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_separator=False): '''Return the current working directory. Returns a segment list to create a breadcrumb-like effect. @@ -126,7 +126,8 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None): shorten parent directory names to this length (e.g. :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) :param int dir_limit_depth: limit directory depth to this number (e.g. :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) - + :param bool use_path_separator: + Use path separator in place of soft divider. Divider highlight group used: ``cwd:divider``. @@ -155,14 +156,20 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None): ret = [] if not cwd[0]: cwd[0] = '/' + draw_inner_divider = not use_path_separator for part in cwd: if not part: continue + if use_path_separator: + part += os.sep ret.append({ 'contents': part, 'divider_highlight_group': 'cwd:divider', + 'draw_inner_divider': draw_inner_divider, }) ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] + if use_path_separator: + ret[-1]['contents'] = ret[-1]['contents'][:-1] return ret diff --git a/tests/test_segments.py b/tests/test_segments.py index b4c52b31..6e2737c5 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -102,34 +102,48 @@ class TestCommon(TestCase): segment_info = {'getcwd': getcwd, 'home': None} with replace_attr(common, 'os', new_os): cwd[0] = '/abc/def/ghi/foo/bar' - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), - [{'contents': '/', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'abc', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'def', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'abc', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'def', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) segment_info['home'] = '/abc/def/ghi' - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), - [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3), - [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'foo', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), - [{'contents': '⋯', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), - [{'contents': '~', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'fo', 'divider_highlight_group': 'cwd:divider'}, - {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), [ + {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True), [ + {'contents': '⋯/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'fo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2, use_path_separator=True), [ + {'contents': '~/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'fo/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) ose = OSError() ose.errno = 2 cwd[0] = ose self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), - [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}]) + [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd'], 'draw_inner_divider': True}]) cwd[0] = OSError() self.assertRaises(OSError, common.cwd, pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2) cwd[0] = ValueError() From 691efbfc9b95fe6af9e942951a2b91a1d23c759a Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 19:06:05 +0400 Subject: [PATCH 0596/1472] Also do the same for `last_pipe_status` --- powerline/segments/shell.py | 2 +- tests/test_segments.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 66699ac3..e870048a 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -22,7 +22,7 @@ def last_pipe_status(pl, segment_info): ''' last_pipe_status = segment_info['args'].last_pipe_status if any(last_pipe_status): - return [{"contents": str(status), "highlight_group": "exit_fail" if status else "exit_success"} + return [{'contents': str(status), 'highlight_group': 'exit_fail' if status else 'exit_success', 'draw_inner_divider': True} for status in last_pipe_status] else: return None diff --git a/tests/test_segments.py b/tests/test_segments.py index 6e2737c5..d8c7bb3d 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -29,10 +29,11 @@ class TestShell(TestCase): segment_info['args'].last_pipe_status = [0, 0, 0] self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), None) segment_info['args'].last_pipe_status = [0, 2, 0] - self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), - [{'contents': '0', 'highlight_group': 'exit_success'}, - {'contents': '2', 'highlight_group': 'exit_fail'}, - {'contents': '0', 'highlight_group': 'exit_success'}]) + self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), [ + {'contents': '0', 'highlight_group': 'exit_success', 'draw_inner_divider': True}, + {'contents': '2', 'highlight_group': 'exit_fail', 'draw_inner_divider': True}, + {'contents': '0', 'highlight_group': 'exit_success', 'draw_inner_divider': True} + ]) class TestCommon(TestCase): From de47b76a06c02e13ffbd0d630095d77ff79485b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 19:23:12 +0400 Subject: [PATCH 0597/1472] Add more assertAccessEvents May fix travis build --- tests/test_config_reload.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index d67eeeaf..aaab4ca1 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -225,10 +225,10 @@ class TestConfigReload(TestCase): self.assertAccessEvents() self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - # Without the following assertion test_reload_colors may fail - # for unknown reason (with AssertionError telling about “config” - # accessed one more time then needed) - self.assertAccessEvents() + # Without the following assertion test_reload_colors may fail for + # unknown reason (with AssertionError telling about “config” accessed + # one more time then needed) + self.assertAccessEvents() def test_reload_main(self): with get_powerline(run_once=False) as p: @@ -281,6 +281,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, 'something') + self.assertAccessEvents() def test_reload_unexistent(self): with get_powerline(run_once=False) as p: @@ -305,6 +306,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colorschemes/test/nonexistentraise') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) + self.assertAccessEvents() def test_reload_colors(self): with get_powerline(run_once=False) as p: @@ -317,6 +319,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colors') self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) + self.assertAccessEvents() def test_reload_colorscheme(self): with get_powerline(run_once=False) as p: @@ -329,6 +332,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colorschemes/test/default') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) + self.assertAccessEvents() def test_reload_theme(self): with get_powerline(run_once=False) as p: @@ -341,6 +345,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('themes/test/default') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) + self.assertAccessEvents() replaces = { From b990c920e999ccda56718edfa27e913db4c96d22 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 06:02:14 +0400 Subject: [PATCH 0598/1472] Use clear_events, do not assert. --- tests/test_config_reload.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index aaab4ca1..67d25877 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -207,12 +207,17 @@ def add_watcher_events(p, *args, **kwargs): return +def clear_events(): + global access_log + with access_lock: + access_log = [] + + class TestConfigReload(TestCase): def assertAccessEvents(self, *args): - global access_log with access_lock: self.assertEqual(set(access_log), set(args)) - access_log = [] + clear_events() def test_noreload(self): with get_powerline(run_once=True) as p: @@ -228,7 +233,7 @@ class TestConfigReload(TestCase): # Without the following assertion test_reload_colors may fail for # unknown reason (with AssertionError telling about “config” accessed # one more time then needed) - self.assertAccessEvents() + clear_events() def test_reload_main(self): with get_powerline(run_once=False) as p: @@ -281,7 +286,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, 'something') - self.assertAccessEvents() + clear_events() def test_reload_unexistent(self): with get_powerline(run_once=False) as p: @@ -306,7 +311,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colorschemes/test/nonexistentraise') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - self.assertAccessEvents() + clear_events() def test_reload_colors(self): with get_powerline(run_once=False) as p: @@ -319,7 +324,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colors') self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - self.assertAccessEvents() + clear_events() def test_reload_colorscheme(self): with get_powerline(run_once=False) as p: @@ -332,7 +337,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('colorschemes/test/default') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - self.assertAccessEvents() + clear_events() def test_reload_theme(self): with get_powerline(run_once=False) as p: @@ -345,7 +350,7 @@ class TestConfigReload(TestCase): self.assertAccessEvents('themes/test/default') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') self.assertEqual(p.logger._pop_msgs(), []) - self.assertAccessEvents() + clear_events() replaces = { From 33d32498b9684cc18b7709c78af929ec3e862289 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 06:33:56 +0400 Subject: [PATCH 0599/1472] Do not use create_renderer from a separate thread Also moves functions from tests.test_config_reload to tests.lib.config_mock Using create_renderer for vim results in vim access from a separate thread. --- powerline/__init__.py | 40 ++++---- tests/lib/config_mock.py | 119 +++++++++++++++++++++++ tests/test_config_reload.py | 188 +++++++----------------------------- 3 files changed, 175 insertions(+), 172 deletions(-) create mode 100644 tests/lib/config_mock.py diff --git a/powerline/__init__.py b/powerline/__init__.py index 4935747f..d459a6f5 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -159,8 +159,8 @@ class Powerline(object): self.config_paths = self.get_config_paths() - self.renderer_lock = Lock() self.configs_lock = Lock() + self.create_renderer_kwargs = {} self.shutdown_event = Event() self.configs = defaultdict(set) self.missing = defaultdict(set) @@ -276,15 +276,14 @@ class Powerline(object): # Renderer updates configuration file via segments’ .startup thus it # should be locked to prevent state when configuration was updated, # but .render still uses old renderer. - with self.renderer_lock: - try: - renderer = Renderer(**self.renderer_options) - except Exception as e: - self.pl.exception('Failed to construct renderer object: {0}', str(e)) - if not hasattr(self, 'renderer'): - raise - else: - self.renderer = renderer + try: + renderer = Renderer(**self.renderer_options) + except Exception as e: + self.pl.exception('Failed to construct renderer object: {0}', str(e)) + if not hasattr(self, 'renderer'): + raise + else: + self.renderer = renderer if not self.run_once and not self.is_alive() and self.interval is not None: self.start() @@ -397,8 +396,15 @@ class Powerline(object): '''Lock renderer from modifications and pass all arguments further to ``self.renderer.render()``. ''' - with self.renderer_lock: - return self.renderer.render(*args, **kwargs) + if self.create_renderer_kwargs: + try: + with self.configs_lock: + cr_kwargs = self.create_renderer_kwargs.copy() + self.create_renderer_kwargs.clear() + self.create_renderer(**cr_kwargs) + except Exception as e: + self.pl.exception('Failed to create renderer: {0}', str(e)) + return self.renderer.render(*args, **kwargs) def shutdown(self): '''Lock renderer from modifications and run its ``.shutdown()`` method. @@ -406,8 +412,7 @@ class Powerline(object): self.shutdown_event.set() if self.use_daemon_threads and self.is_alive(): self.thread.join() - with self.renderer_lock: - self.renderer.shutdown() + self.renderer.shutdown() self.watcher.unsubscribe() def is_alive(self): @@ -435,11 +440,8 @@ class Powerline(object): pass else: kwargs['load_' + type] = True - if kwargs: - try: - self.create_renderer(**kwargs) - except Exception as e: - self.pl.exception('Failed to create renderer: {0}', str(e)) + if kwargs: + self.create_renderer_kwargs.update(kwargs) self.shutdown_event.wait(self.interval) def __enter__(self): diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py new file mode 100644 index 00000000..83a27f75 --- /dev/null +++ b/tests/lib/config_mock.py @@ -0,0 +1,119 @@ +# vim:fileencoding=utf-8:noet +from threading import Lock +from powerline.renderer import Renderer +from powerline import Powerline +from copy import deepcopy + + +access_log = [] +access_lock = Lock() + + +def load_json_config(config, config_file_path, *args, **kwargs): + global access_log + with access_lock: + access_log.append(config_file_path) + try: + return deepcopy(config[config_file_path]) + except KeyError: + raise IOError(config_file_path) + + +def find_config_file(config, search_paths, config_file): + if config_file.endswith('raise') and config_file not in config: + raise IOError('fcf:' + config_file) + return config_file + + +def pop_events(): + global access_log + with access_lock: + r = access_log[:] + access_log = [] + return r + + +class Watcher(object): + events = set() + lock = Lock() + + def watch(self, file): + pass + + def __call__(self, file): + if file in self.events: + with self.lock: + self.events.remove(file) + return True + return False + + def _reset(self, files): + with self.lock: + self.events.clear() + self.events.update(files) + + def unsubscribe(self): + pass + + +class Logger(object): + def __init__(self): + self.messages = [] + self.lock = Lock() + + def _add_msg(self, attr, msg): + with self.lock: + self.messages.append(attr + ':' + msg) + + def _pop_msgs(self): + with self.lock: + r = self.messages + self.messages = [] + return r + + def __getattr__(self, attr): + return lambda *args, **kwargs: self._add_msg(attr, *args, **kwargs) + + +class SimpleRenderer(Renderer): + def hlstyle(self, fg=None, bg=None, attr=None): + return '<{fg} {bg} {attr}>'.format(fg=fg and fg[0], bg=bg and bg[0], attr=attr) + + +class TestPowerline(Powerline): + _created = False + + @staticmethod + def get_local_themes(local_themes): + return local_themes + + def _will_create_renderer(self): + return self.create_renderer_kwargs + + +renderer = SimpleRenderer + + +def get_powerline(**kwargs): + return TestPowerline( + ext='test', + renderer_module='tests.lib.config_mock', + interval=0, + logger=Logger(), + watcher=Watcher(), + **kwargs + ) + + +def swap_attributes(config_container, powerline_module, replaces): + if not replaces: + replaces = { + 'watcher': Watcher(), + 'load_json_config': lambda *args: load_json_config(config_container['config'], *args), + 'find_config_file': lambda *args: find_config_file(config_container['config'], *args), + } + for attr, val in replaces.items(): + old_val = getattr(powerline_module, attr) + setattr(powerline_module, attr, val) + replaces[attr] = old_val + return replaces diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 67d25877..d74f2cbe 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -2,55 +2,13 @@ from __future__ import unicode_literals import powerline as powerline_module import time -from powerline.renderer import Renderer from tests import TestCase from tests.lib import replace_item +from tests.lib.config_mock import swap_attributes, get_powerline, pop_events from copy import deepcopy from threading import Lock -class Watcher(object): - events = set() - lock = Lock() - - def watch(self, file): - pass - - def __call__(self, file): - if file in self.events: - with self.lock: - self.events.remove(file) - return True - return False - - def _reset(self, files): - with self.lock: - self.events.clear() - self.events.update(files) - - def unsubscribe(self): - pass - - -class Logger(object): - def __init__(self): - self.messages = [] - self.lock = Lock() - - def _add_msg(self, msg): - with self.lock: - self.messages.append(msg) - - def _pop_msgs(self): - with self.lock: - r = self.messages - self.messages = [] - return r - - def __getattr__(self, attr): - return self._add_msg - - config = { 'config': { 'common': { @@ -134,106 +92,37 @@ config = { } -access_log = [] -access_lock = Lock() - - -def load_json_config(config_file_path, *args, **kwargs): - global access_log - with access_lock: - access_log.append(config_file_path) - try: - return deepcopy(config[config_file_path]) - except KeyError: - raise IOError(config_file_path) - - -def find_config_file(search_paths, config_file): - if config_file.endswith('raise') and config_file not in config: - raise IOError('fcf:' + config_file) - return config_file - - -class SimpleRenderer(Renderer): - def hlstyle(self, fg=None, bg=None, attr=None): - return '<{fg} {bg} {attr}>'.format(fg=fg and fg[0], bg=bg and bg[0], attr=attr) - - -class TestPowerline(powerline_module.Powerline): - _created = False - - @staticmethod - def get_local_themes(local_themes): - return local_themes - - def create_renderer(self, *args, **kwargs): - try: - r = super(TestPowerline, self).create_renderer(*args, **kwargs) - finally: - self._created = True - return r - - def _created_renderer(self): - if self._created: - self._created = False - return True - return False - - -renderer = SimpleRenderer - - -def get_powerline(**kwargs): - return TestPowerline( - ext='test', - renderer_module='tests.test_config_reload', - interval=0, - logger=Logger(), - watcher=Watcher(), - **kwargs - ) - - def sleep(interval): time.sleep(interval) def add_watcher_events(p, *args, **kwargs): - p._created_renderer() p.watcher._reset(args) - while not p._created_renderer(): + while not p._will_create_renderer(): sleep(kwargs.get('interval', 0.000001)) if not kwargs.get('wait', True): return -def clear_events(): - global access_log - with access_lock: - access_log = [] - - class TestConfigReload(TestCase): def assertAccessEvents(self, *args): - with access_lock: - self.assertEqual(set(access_log), set(args)) - clear_events() + self.assertEqual(set(pop_events()), set(args)) def test_noreload(self): with get_powerline(run_once=True) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') config['config']['common']['spaces'] = 1 add_watcher_events(p, 'config', wait=False, interval=0.05) # When running once thread should not start - self.assertAccessEvents() self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents() self.assertEqual(p.logger._pop_msgs(), []) # Without the following assertion test_reload_colors may fail for # unknown reason (with AssertionError telling about “config” accessed # one more time then needed) - clear_events() + pop_events() def test_reload_main(self): with get_powerline(run_once=False) as p: @@ -243,62 +132,62 @@ class TestConfigReload(TestCase): config['config']['common']['spaces'] = 1 add_watcher_events(p, 'config') - self.assertAccessEvents('config') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents('config') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['theme'] = 'nonexistent' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'themes/test/nonexistent') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents('config', 'themes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: themes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: themes/test/nonexistent']) config['config']['ext']['test']['theme'] = 'default' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents('config', 'themes/test/default') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['colorscheme'] = 'nonexistent' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'colorschemes/test/nonexistent') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents('config', 'colorschemes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: colorschemes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: colorschemes/test/nonexistent']) config['config']['ext']['test']['colorscheme'] = '2' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'colorschemes/test/2') self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>>') + self.assertAccessEvents('config', 'colorschemes/test/2') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['theme'] = '2' add_watcher_events(p, 'config') - self.assertAccessEvents('config', 'themes/test/2') self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') + self.assertAccessEvents('config', 'themes/test/2') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, None) config['config']['ext']['test']['local_themes'] = 'something' add_watcher_events(p, 'config') - self.assertAccessEvents('config') self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') + self.assertAccessEvents('config') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, 'something') - clear_events() + pop_events() def test_reload_unexistent(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' add_watcher_events(p, 'config') - self.assertAccessEvents('config') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertEqual(p.logger._pop_msgs(), ['test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise']) + self.assertAccessEvents('config') + self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise']) config['colorschemes/test/nonexistentraise'] = { 'groups': { @@ -306,69 +195,62 @@ class TestConfigReload(TestCase): "str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]}, }, } - while not p._created_renderer(): + while not p._will_create_renderer(): sleep(0.000001) - self.assertAccessEvents('colorschemes/test/nonexistentraise') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') + self.assertAccessEvents('colorschemes/test/nonexistentraise') self.assertEqual(p.logger._pop_msgs(), []) - clear_events() + pop_events() def test_reload_colors(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') config['colors']['colors']['col1'] = 5 add_watcher_events(p, 'colors') - self.assertAccessEvents('colors') self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('colors') self.assertEqual(p.logger._pop_msgs(), []) - clear_events() + pop_events() def test_reload_colorscheme(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' add_watcher_events(p, 'colorschemes/test/default') - self.assertAccessEvents('colorschemes/test/default') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('colorschemes/test/default') self.assertEqual(p.logger._pop_msgs(), []) - clear_events() + pop_events() def test_reload_theme(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default') - self.assertAccessEvents('themes/test/default') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('themes/test/default') self.assertEqual(p.logger._pop_msgs(), []) - clear_events() + pop_events() -replaces = { - 'watcher': Watcher(), - 'load_json_config': load_json_config, - 'find_config_file': find_config_file, -} +replaces = {} -def swap_attributes(): +def setUpModule(): global replaces - for attr, val in replaces.items(): - old_val = getattr(powerline_module, attr) - setattr(powerline_module, attr, val) - replaces[attr] = old_val + replaces = swap_attributes(globals(), powerline_module, replaces) -tearDownModule = setUpModule = swap_attributes +tearDownModule = setUpModule if __name__ == '__main__': From 2a0e6019958d5c4679a560edfde9c15c9d6255d6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 07:38:53 +0400 Subject: [PATCH 0600/1472] Lock create_renderer_kwargs separately from configs --- powerline/__init__.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index d459a6f5..89a1b682 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -160,6 +160,7 @@ class Powerline(object): self.config_paths = self.get_config_paths() self.configs_lock = Lock() + self.cr_kwargs_lock = Lock() self.create_renderer_kwargs = {} self.shutdown_event = Event() self.configs = defaultdict(set) @@ -396,14 +397,13 @@ class Powerline(object): '''Lock renderer from modifications and pass all arguments further to ``self.renderer.render()``. ''' - if self.create_renderer_kwargs: - try: - with self.configs_lock: - cr_kwargs = self.create_renderer_kwargs.copy() + with self.cr_kwargs_lock: + if self.create_renderer_kwargs: + try: + cr_kwargs = self.create_renderer(**self.create_renderer_kwargs) self.create_renderer_kwargs.clear() - self.create_renderer(**cr_kwargs) - except Exception as e: - self.pl.exception('Failed to create renderer: {0}', str(e)) + except Exception as e: + self.pl.exception('Failed to create renderer: {0}', str(e)) return self.renderer.render(*args, **kwargs) def shutdown(self): @@ -440,6 +440,7 @@ class Powerline(object): pass else: kwargs['load_' + type] = True + with self.cr_kwargs_lock: if kwargs: self.create_renderer_kwargs.update(kwargs) self.shutdown_event.wait(self.interval) From 577a1c0c790e497f6f98df341846d725e7dc65a5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 07:41:48 +0400 Subject: [PATCH 0601/1472] Exchange if and with cr_kwargs_lock --- powerline/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 89a1b682..00a02f3b 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -440,8 +440,8 @@ class Powerline(object): pass else: kwargs['load_' + type] = True - with self.cr_kwargs_lock: - if kwargs: + if kwargs: + with self.cr_kwargs_lock: self.create_renderer_kwargs.update(kwargs) self.shutdown_event.wait(self.interval) From ea1e45def47ef45ef1d80b24b93261733e973883 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 08:51:44 +0400 Subject: [PATCH 0602/1472] Fix typo Fixes #387 --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 87ac2c28..e0b750c2 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -616,7 +616,7 @@ def user(pl, segment_info=None): 'contents': username, 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], }] -if 'psutil' in globals(): +if 'psutil' not in globals(): user = requires_segment_info(user) From 9bd5b3ac37bfefe3540fe1725359bb405b0904a4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 09:11:43 +0400 Subject: [PATCH 0603/1472] Fix problems with tests --- powerline/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 00a02f3b..f564bec6 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -400,10 +400,11 @@ class Powerline(object): with self.cr_kwargs_lock: if self.create_renderer_kwargs: try: - cr_kwargs = self.create_renderer(**self.create_renderer_kwargs) - self.create_renderer_kwargs.clear() + self.create_renderer(**self.create_renderer_kwargs) except Exception as e: self.pl.exception('Failed to create renderer: {0}', str(e)) + finally: + self.create_renderer_kwargs.clear() return self.renderer.render(*args, **kwargs) def shutdown(self): @@ -427,6 +428,7 @@ class Powerline(object): def run(self): while not self.shutdown_event.is_set(): kwargs = {} + removes = [] with self.configs_lock: for type, paths in self.configs.items(): for path in paths: @@ -440,6 +442,9 @@ class Powerline(object): pass else: kwargs['load_' + type] = True + removes.append((type, cfg_path)) + for type, cfg_path in removes: + self.missing[type].remove(cfg_path) if kwargs: with self.cr_kwargs_lock: self.create_renderer_kwargs.update(kwargs) From cde7669251eca9b252a0a7b644483741330f85bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 3 Apr 2013 14:09:52 +0200 Subject: [PATCH 0604/1472] Add parameters to shorten uptime display Ref #316 --- powerline/segments/common.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 558d807f..7be327a6 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -637,12 +637,19 @@ else: @add_divider_highlight_group('background:divider') -def uptime(pl, format='{days}d {hours:02d}h {minutes:02d}m'): +def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_format=' {minutes:d}m', seconds_format=' {seconds:d}s', shorten_len=3): '''Return system uptime. - :param str format: - format string, will be passed ``days``, ``hours``, ``minutes`` and - seconds as arguments + :param str days_format: + day format string, will be passed ``days`` as the argument + :param str hours_format: + hour format string, will be passed ``hours`` as the argument + :param str minutes_format: + minute format string, will be passed ``minutes`` as the argument + :param str seconds_format: + second format string, will be passed ``seconds`` as the argument + :param int shorten_len: + shorten the amount of units (days, hours, etc.) displayed Divider highlight group used: ``background:divider``. ''' @@ -654,7 +661,13 @@ def uptime(pl, format='{days}d {hours:02d}h {minutes:02d}m'): minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) - return format.format(days=int(days), hours=hours, minutes=minutes, seconds=seconds) + time_formatted = list(filter(None, [ + days_format.format(days=days) if days and days_format else None, + hours_format.format(hours=hours) if hours and hours_format else None, + minutes_format.format(minutes=minutes) if minutes and minutes_format else None, + seconds_format.format(seconds=seconds) if seconds and seconds_format else None, + ]))[0:shorten_len] + return ''.join(time_formatted).strip() class NetworkLoadSegment(KwThreadedSegment): From 2d9110b5f1ccdeb24aef07d9a69fb7172e944e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 3 Apr 2013 14:21:32 +0200 Subject: [PATCH 0605/1472] Update tests --- tests/test_segments.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index d8c7bb3d..b95bad62 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -176,8 +176,15 @@ class TestCommon(TestCase): def test_uptime(self): pl = Pl() + with replace_attr(common, '_get_uptime', lambda: 259200): + self.assertEqual(common.uptime(pl=pl), [{'contents': '3d', 'divider_highlight_group': 'background:divider'}]) + with replace_attr(common, '_get_uptime', lambda: 93784): + self.assertEqual(common.uptime(pl=pl), [{'contents': '1d 2h 3m', 'divider_highlight_group': 'background:divider'}]) + self.assertEqual(common.uptime(pl=pl, shorten_len=4), [{'contents': '1d 2h 3m 4s', 'divider_highlight_group': 'background:divider'}]) with replace_attr(common, '_get_uptime', lambda: 65536): - self.assertEqual(common.uptime(pl=pl), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}]) + self.assertEqual(common.uptime(pl=pl), [{'contents': '18h 12m 16s', 'divider_highlight_group': 'background:divider'}]) + self.assertEqual(common.uptime(pl=pl, shorten_len=2), [{'contents': '18h 12m', 'divider_highlight_group': 'background:divider'}]) + self.assertEqual(common.uptime(pl=pl, shorten_len=1), [{'contents': '18h', 'divider_highlight_group': 'background:divider'}]) def _get_uptime(): raise NotImplementedError From 878255aff070c90aa3c05fbf728c51996830200f Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 23:09:40 +0400 Subject: [PATCH 0606/1472] Make Tmux and PangoMarkup Renderers merge arguments like shell one For PangoMarkup it is pretty useless currently (segment_info does not contain anything useful). Note: this does its job by replacing default behavior. Source of issue: previous default used either segment_info argument (containing {"args": args, "environ": os.environ}) or default segment_info, shell renderer merged default segment_info with .render() argument. Now segment_info is merged by default and old behavior moved to vim renderer which is the only one that is designed to use this. Fixes #391 --- powerline/renderer.py | 6 +++++- powerline/renderers/shell.py | 7 ------- powerline/renderers/vim.py | 3 +++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 324aa982..b33840e9 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -67,7 +67,11 @@ class Renderer(object): return segment def get_segment_info(self, segment_info): - return segment_info or self.segment_info + r = self.segment_info.copy() + r.update(segment_info) + if 'PWD' in r['environ']: + r['getcwd'] = lambda: r['environ']['PWD'] + return r def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index da920acb..945dc765 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -19,13 +19,6 @@ class ShellRenderer(Renderer): tmux_escape = False screen_escape = False - def get_segment_info(self, segment_info): - r = self.segment_info.copy() - r.update(segment_info) - if 'PWD' in r['environ']: - r['getcwd'] = lambda: r['environ']['PWD'] - return r - def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 4116bdae..43d1d07d 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -65,6 +65,9 @@ class VimRenderer(Renderer): def strwidth(string): return vim.strwidth(string) + def get_segment_info(self, segment_info): + return segment_info or self.segment_info + def render(self, window_id, winidx, current): '''Render all segments.''' if current: From e599691c52c7bd105f6decf4b007dccc073bbc09 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 23:19:35 +0400 Subject: [PATCH 0607/1472] Update only if segment_info is not None --- powerline/renderer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index b33840e9..206be29e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -68,7 +68,8 @@ class Renderer(object): def get_segment_info(self, segment_info): r = self.segment_info.copy() - r.update(segment_info) + if segment_info: + r.update(segment_info) if 'PWD' in r['environ']: r['getcwd'] = lambda: r['environ']['PWD'] return r From 17065b4c7e48b470fee1822c918cd124ce87aa83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 5 Apr 2013 15:42:56 +0200 Subject: [PATCH 0608/1472] Add timeout to thread join calls to avoid deadlocks --- powerline/__init__.py | 3 ++- powerline/lib/threaded.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index f564bec6..615c5433 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -412,7 +412,8 @@ class Powerline(object): ''' self.shutdown_event.set() if self.use_daemon_threads and self.is_alive(): - self.thread.join() + # Give the worker thread a chance to shutdown, but don't block for too long + self.thread.join(.01) self.renderer.shutdown() self.watcher.unsubscribe() diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 5c0cfb3c..43398542 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -77,7 +77,8 @@ class ThreadedSegment(object): def shutdown(self): self.shutdown_event.set() if self.daemon and self.is_alive(): - self.thread.join() + # Give the worker thread a chance to shutdown, but don't block for too long + self.thread.join(.01) def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. From 0d3559c869dfc1df59cab15ef92f95d91578a168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 5 Apr 2013 15:44:38 +0200 Subject: [PATCH 0609/1472] Increase external IP polling interval to 5 minutes --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 558d807f..df4c1a58 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -245,7 +245,7 @@ def _external_ip(query_url='http://ipv4.icanhazip.com/'): class ExternalIpSegment(ThreadedSegment): - interval = 10 + interval = 300 def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs): self.query_url = query_url From 80be4071c97b391b77684066d596503e18dfb892 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 5 Apr 2013 15:59:51 +0200 Subject: [PATCH 0610/1472] Disable continuous polling of CPU count in load segment --- powerline/segments/common.py | 10 +++++++--- tests/test_segments.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index df4c1a58..611c154e 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -7,7 +7,7 @@ import sys from datetime import datetime import socket -from multiprocessing import cpu_count +from multiprocessing import cpu_count as _cpu_count from powerline.lib import add_divider_highlight_group from powerline.lib.url import urllib_read, urllib_urlencode @@ -18,6 +18,7 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.theme import requires_segment_info from collections import namedtuple +cpu_count = None @requires_segment_info def hostname(pl, segment_info, only_if_ssh=False, exclude_domain=False): @@ -485,7 +486,7 @@ Also uses ``weather_conditions_{condition}`` for all weather conditions supporte ''') -def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): +def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2, track_cpu_count=False): '''Return system load average. Highlights using ``system_load_good``, ``system_load_bad`` and @@ -504,6 +505,9 @@ def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): indicates relative position in this interval: (``100 * (cur-good) / (bad-good)``). Note: both parameters are checked against normalized load averages. + :param bool track_cpu_count: + if True powerline will continuously poll the system to detect changes + in the number of CPUs. Divider highlight group used: ``background:divider``. @@ -511,7 +515,7 @@ def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2): ''' global cpu_count try: - cpu_num = cpu_count() + cpu_num = cpu_count = _cpu_count() if cpu_count is None or track_cpu_count else cpu_count except NotImplementedError: pl.warn('Unable to get CPU count: method is not implemented') return None diff --git a/tests/test_segments.py b/tests/test_segments.py index d8c7bb3d..43067de0 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -224,7 +224,7 @@ class TestCommon(TestCase): def test_system_load(self): pl = Pl() with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): - with replace_attr(common, 'cpu_count', lambda: 2): + with replace_attr(common, '_cpu_count', lambda: 2): self.assertEqual(common.system_load(pl=pl), [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, From f1286a7bc1f89d20b93e6360cc842e6d495b7d54 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 5 Apr 2013 16:04:26 +0200 Subject: [PATCH 0611/1472] Rename time.py to monotonic.py This makes it possible to run modules in the lib/ directory standalone. --- powerline/lib/file_watcher.py | 2 +- powerline/lib/memoize.py | 2 +- powerline/lib/{time.py => monotonic.py} | 0 powerline/lib/threaded.py | 2 +- powerline/segments/common.py | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename powerline/lib/{time.py => monotonic.py} (100%) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 0be4be09..83d679ee 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -7,10 +7,10 @@ __docformat__ = 'restructuredtext en' import os import sys import errno -from powerline.lib.time import monotonic from time import sleep from threading import RLock +from powerline.lib.monotonic import monotonic class INotifyError(Exception): pass diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 623f1d32..e180f300 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8:noet from functools import wraps -from powerline.lib.time import monotonic +from powerline.lib.monotonic import monotonic def default_cache_key(**kwargs): diff --git a/powerline/lib/time.py b/powerline/lib/monotonic.py similarity index 100% rename from powerline/lib/time.py rename to powerline/lib/monotonic.py diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 43398542..2e9153f6 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -2,7 +2,7 @@ from __future__ import absolute_import -from powerline.lib.time import monotonic +from powerline.lib.monotonic import monotonic from threading import Thread, Lock, Event diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 611c154e..04cb2e77 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -13,7 +13,7 @@ from powerline.lib import add_divider_highlight_group from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring -from powerline.lib.time import monotonic +from powerline.lib.monotonic import monotonic from powerline.lib.humanize_bytes import humanize_bytes from powerline.theme import requires_segment_info from collections import namedtuple From 47eef9860f0e6f89d66e1737a42726056b6b5182 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Apr 2013 17:47:39 +0400 Subject: [PATCH 0612/1472] Use different emulate call Ref #389 --- powerline/bindings/zsh/powerline.zsh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 0b62aae6..a45a6657 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -14,14 +14,13 @@ _powerline_tmux_set_columns() { } _powerline_install_precmd() { - emulate -L zsh + emulate zsh for f in "${precmd_functions[@]}"; do if [[ "$f" = "_powerline_precmd" ]]; then return fi done chpwd_functions+=( _powerline_tmux_set_pwd ) - setopt nolocaloptions setopt promptpercent setopt promptsubst if zmodload zsh/zpython &>/dev/null ; then From 63e29429292200b1a8dcfb9c6cbee00329dc62e0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Apr 2013 17:51:42 +0400 Subject: [PATCH 0613/1472] Also take tests from @kovidgoyal develop branch --- tests/test_lib.py | 53 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 6355e25f..1983e6c8 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -36,6 +36,53 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=True), '1.00 GB') self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') + def do_test_for_change(self, watcher, path): + import time + st = time.time() + while time.time() - st < 1: + if watcher(path): + return + time.sleep(0.1) + self.fail('The change to %s was not detected'%path) + + def test_file_watcher(self): + from powerline.lib.file_watcher import create_file_watcher + w = create_file_watcher(use_stat=False) + if w.is_stat_based: + # The granularity of mtime (1 second) means that we cannot use the + # same tests for inotify and StatWatch. + return + f1, f2 = os.path.join(INOTIFY_DIR, 'file1'), os.path.join(INOTIFY_DIR, 'file2') + with open(f1, 'wb'): + with open(f2, 'wb'): + pass + ne = os.path.join(INOTIFY_DIR, 'notexists') + self.assertRaises(OSError, w, ne) + self.assertTrue(w(f1)) + self.assertTrue(w(f2)) + os.utime(f1, None), os.utime(f2, None) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Repeat once + os.utime(f1, None), os.utime(f2, None) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Check that no false changes are reported + self.assertFalse(w(f1), 'Spurious change detected') + self.assertFalse(w(f2), 'Spurious change detected') + # Check that open the file with 'w' triggers a change + with open(f1, 'wb'): + with open(f2, 'wb'): + pass + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Check that writing to a file with 'a' triggers a change + with open(f1, 'ab') as f: + f.write(b'1') + self.do_test_for_change(w, f1) + # Check that deleting a file registers as a change + os.unlink(f1) + self.do_test_for_change(w, f1) use_mercurial = use_bzr = sys.version_info < (3, 0) @@ -106,7 +153,7 @@ old_cwd = None GIT_REPO = 'git_repo' + os.environ.get('PYTHON', '') HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') - +INOTIFY_DIR = 'inotify' + os.environ.get('PYTHON', '') def setUpModule(): global old_cwd @@ -130,12 +177,14 @@ def setUpModule(): call(['bzr', 'config', 'email=Foo '], cwd=BZR_REPO) call(['bzr', 'config', 'nickname=test_powerline'], cwd=BZR_REPO) call(['bzr', 'config', 'create_signatures=0'], cwd=BZR_REPO) + os.mkdir(INOTIFY_DIR) + def tearDownModule(): global old_cwd global old_HGRCPATH - for repo_dir in [GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): + for repo_dir in [INOTIFY_DIR, GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): for root, dirs, files in list(os.walk(repo_dir, topdown=False)): for file in files: os.remove(os.path.join(root, file)) From 0a8e7c0e1e6e5112e65d11a646f170bd871229b9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Apr 2013 07:12:25 +0400 Subject: [PATCH 0614/1472] Add a note about vim-addon-manager --- docs/source/overview.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 494a14a0..19850793 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -100,8 +100,14 @@ absolute path to your Powerline installation directory: If you're using Vundle or Pathogen and don't want Powerline functionality in any other applications, simply add Powerline as a bundle and point the path -above to the Powerline bundle directory, e.g. -``~/.vim/bundle/powerline/powerline/bindings/vim``. +above to the Powerline bundle directory, e.g. +``~/.vim/bundle/powerline/powerline/bindings/vim``. For vim-addon-manager it is +even easier since you don’t need to write this big path or install anything by +hand: ``powerline`` is installed and run just like any other plugin using + +.. code-block:: vim + + call vam#ActivateAddons(['powerline']) Shell prompts ------------- From 5534b26bfe3480bfa03abaad68f508aafc53b096 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Apr 2013 01:44:28 +0400 Subject: [PATCH 0615/1472] Forbid querying vim from non-main threads in tests Currently tests nothing or almost nothing. --- tests/test_segments.py | 2 +- tests/vim.py | 158 ++++++++++++++++++++++++++++++++--------- 2 files changed, 124 insertions(+), 36 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index ca16eb93..f417f431 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -492,4 +492,4 @@ def tearDownModule(): if __name__ == '__main__': from tests import main - main() + main(verbosity=10) diff --git a/tests/vim.py b/tests/vim.py index 3b046590..aaf5e534 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -11,26 +11,114 @@ _options = { _last_bufnr = 0 _highlights = {} -buffers = {} -windows = [] +_thread_id = None -def _buffer(): - return windows[_window - 1].buffer.number +def _set_thread_id(): + global _thread_id + from threading import current_thread + _thread_id = current_thread().ident -def _logged(func): +# Assuming import is done from the main thread +_set_thread_id() + + +def _vim(func): from functools import wraps + from threading import current_thread @wraps(func) def f(*args, **kwargs): + global _thread_id + if _thread_id != current_thread().ident: + raise RuntimeError('Accessing vim from separate threads is not allowed') _log.append((func.__name__, args)) return func(*args, **kwargs) return f +class _Buffers(object): + @_vim + def __init__(self): + self.d = {} + + @_vim + def __getitem__(self, item): + return self.d[item] + + @_vim + def __setitem__(self, item, value): + self.d[item] = value + + @_vim + def __contains__(self, item): + return item in self.d + + @_vim + def __nonzero__(self): + return not not self.d + + @_vim + def keys(self): + return self.d.keys() + + @_vim + def pop(self, *args, **kwargs): + return self.d.pop(*args, **kwargs) + + +buffers = _Buffers() + + +class _Windows(object): + @_vim + def __init__(self): + self.l = [] + + @_vim + def __getitem__(self, item): + return self.l[item] + + @_vim + def __setitem__(self, item, value): + self.l[item] = value + + @_vim + def __len__(self): + return len(self.l) + + @_vim + def __iter__(self): + return iter(self.l) + + @_vim + def __nonzero__(self): + return not not self.l + + @_vim + def pop(self, *args, **kwargs): + return self.l.pop(*args, **kwargs) + + @_vim + def append(self, *args, **kwargs): + return self.l.append(*args, **kwargs) + + @_vim + def index(self, *args, **kwargs): + return self.l.index(*args, **kwargs) + + +windows = _Windows() + + +@_vim +def _buffer(): + return windows[_window - 1].buffer.number + + def _construct_result(r): import sys if sys.version_info < (3,): @@ -58,7 +146,7 @@ def _log_print(): sys.stdout.write(repr(entry) + '\n') -@_logged +@_vim def command(cmd): if cmd.startswith('let g:'): import re @@ -71,7 +159,7 @@ def command(cmd): raise NotImplementedError -@_logged +@_vim def eval(expr): if expr.startswith('g:'): return _g[expr[2:]] @@ -83,7 +171,7 @@ def eval(expr): raise NotImplementedError -@_logged +@_vim def bindeval(expr): if expr == 'g:': return _g @@ -95,7 +183,7 @@ def bindeval(expr): raise NotImplementedError -@_logged +@_vim @_str_func def _emul_mode(*args): if args and args[0]: @@ -104,11 +192,11 @@ def _emul_mode(*args): return _mode[0] -@_logged +@_vim @_str_func def _emul_getbufvar(bufnr, varname): if varname[0] == '&': - if bufnr not in _buf_options: + if bufnr not in buffers: return '' try: return _buf_options[bufnr][varname[1:]] @@ -120,25 +208,25 @@ def _emul_getbufvar(bufnr, varname): raise NotImplementedError -@_logged +@_vim @_str_func def _emul_getwinvar(winnr, varname): return _win_scopes[winnr][varname] -@_logged +@_vim def _emul_setwinvar(winnr, varname, value): _win_scopes[winnr][varname] = value -@_logged +@_vim def _emul_virtcol(expr): if expr == '.': return windows[_window - 1].cursor[1] + 1 raise NotImplementedError -@_logged +@_vim @_str_func def _emul_fnamemodify(path, modstring): import os @@ -154,7 +242,7 @@ def _emul_fnamemodify(path, modstring): return path -@_logged +@_vim @_str_func def _emul_expand(expr): if expr == '': @@ -162,21 +250,21 @@ def _emul_expand(expr): raise NotImplementedError -@_logged +@_vim def _emul_bufnr(expr): if expr == '$': return _last_bufnr raise NotImplementedError -@_logged +@_vim def _emul_exists(varname): if varname.startswith('g:'): return varname[2:] in _g raise NotImplementedError -@_logged +@_vim def _emul_line2byte(line): buflines = _buf_lines[_buffer()] if line == len(buflines) + 1: @@ -287,7 +375,7 @@ current = _Current() _dict = None -@_logged +@_vim def _init(): global _dict @@ -302,7 +390,7 @@ def _init(): return _dict -@_logged +@_vim def _get_segment_info(): mode_translations = { chr(ord('V') - 0x40): '^V', @@ -319,12 +407,12 @@ def _get_segment_info(): } -@_logged +@_vim def _launch_event(event): pass -@_logged +@_vim def _start_mode(mode): global _mode if mode == 'i': @@ -334,7 +422,7 @@ def _start_mode(mode): _mode = mode -@_logged +@_vim def _undo(): if len(_undostate[_buffer()]) == 1: return @@ -344,7 +432,7 @@ def _undo(): _buf_options[_buffer()]['modified'] = 0 -@_logged +@_vim def _edit(name=None): global _last_bufnr if _buffer() and buffers[_buffer()].name is None: @@ -355,14 +443,14 @@ def _edit(name=None): windows[_window - 1].buffer = buf -@_logged +@_vim def _new(name=None): global _window _Window(buffer={'name': name}) _window = len(windows) -@_logged +@_vim def _del_window(winnr): win = windows.pop(winnr - 1) _win_scopes.pop(winnr) @@ -371,7 +459,7 @@ def _del_window(winnr): return win -@_logged +@_vim def _close(winnr, wipe=True): global _window win = _del_window(winnr) @@ -387,7 +475,7 @@ def _close(winnr, wipe=True): _Window() -@_logged +@_vim def _bw(bufnr=None): bufnr = bufnr or _buffer() winnr = 1 @@ -401,12 +489,12 @@ def _bw(bufnr=None): _b(max(buffers.keys())) -@_logged +@_vim def _b(bufnr): windows[_window - 1].buffer = buffers[bufnr] -@_logged +@_vim def _set_cursor(line, col): windows[_window - 1].cursor = (line, col) if _mode == 'n': @@ -415,12 +503,12 @@ def _set_cursor(line, col): _launch_event('CursorMovedI') -@_logged +@_vim def _get_buffer(): return buffers[_buffer()] -@_logged +@_vim def _set_bufoption(option, value, bufnr=None): _buf_options[bufnr or _buffer()][option] = value if option == 'filetype': @@ -440,7 +528,7 @@ class _WithNewBuffer(object): _bw(self.bufnr) -@_logged +@_vim def _set_dict(d, new, setfunc=None): if not setfunc: def setfunc(k, v): @@ -496,7 +584,7 @@ class _WithDict(object): self.d.pop(k) -@_logged +@_vim def _with(key, *args, **kwargs): if key == 'buffer': return _WithNewBuffer(_edit, *args, **kwargs) From b188662844228ffc9160bc0bf0afcd5d34d6b51a Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 23:41:47 +0400 Subject: [PATCH 0616/1472] Move some thread functions to a separate class --- powerline/lib/threaded.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 2e9153f6..83b12f9c 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -7,7 +7,26 @@ from powerline.lib.monotonic import monotonic from threading import Thread, Lock, Event -class ThreadedSegment(object): +class MultiRunnedThread(object): + def __init__(self): + self.thread = None + + def is_alive(self): + return self.thread and self.thread.is_alive() + + def start(self): + self.shutdown_event.clear() + self.thread = Thread(target=self.run) + self.thread.daemon = self.daemon + self.thread.start() + + def join(self, *args, **kwargs): + if self.thread: + return self.thread.join(*args, **kwargs) + return None + + +class ThreadedSegment(MultiRunnedThread): min_sleep_time = 0.1 update_first = True interval = 1 @@ -17,7 +36,6 @@ class ThreadedSegment(object): super(ThreadedSegment, self).__init__() self.shutdown_event = Event() self.run_once = True - self.thread = None self.skip = False self.crashed_value = None self.update_value = None @@ -50,15 +68,6 @@ class ThreadedSegment(object): self.update_value = self.update(self.update_value) return self.update_value - def is_alive(self): - return self.thread and self.thread.is_alive() - - def start(self): - self.shutdown_event.clear() - self.thread = Thread(target=self.run) - self.thread.daemon = self.daemon - self.thread.start() - def run(self): while not self.shutdown_event.is_set(): start_time = monotonic() @@ -77,8 +86,9 @@ class ThreadedSegment(object): def shutdown(self): self.shutdown_event.set() if self.daemon and self.is_alive(): - # Give the worker thread a chance to shutdown, but don't block for too long - self.thread.join(.01) + # Give the worker thread a chance to shutdown, but don't block for + # too long + self.join(0.01) def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. From f45084057a54fcb2f858e8a919c9795e594c4528 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 23:43:50 +0400 Subject: [PATCH 0617/1472] Remove debugging function --- powerline/lib/threaded.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 83b12f9c..a107230c 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -122,13 +122,6 @@ class ThreadedSegment(MultiRunnedThread): self.pl.debug(prefix=self.__class__.__name__, *args, **kwargs) -def printed(func): - def f(*args, **kwargs): - print(func.__name__) - return func(*args, **kwargs) - return f - - class KwThreadedSegment(ThreadedSegment): drop_interval = 10 * 60 update_first = True From f0e5f43d48dadf0e4d921b55fa360f006ad5e451 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Apr 2013 23:48:01 +0400 Subject: [PATCH 0618/1472] Make it use existing shutdown events Also adds check to powerline-lint that "args" dictionary does not set shutdown_event, segment_info or pl keyword arguments, this will lead to problems --- powerline/__init__.py | 1 + powerline/lib/threaded.py | 7 ++++--- powerline/lint/__init__.py | 8 ++++++++ powerline/theme.py | 11 +++++++++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 615c5433..6b1d09c0 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -235,6 +235,7 @@ class Powerline(object): 'ext': self.ext, 'common_config': self.common_config, 'run_once': self.run_once, + 'shutdown_event': self.shutdown_event, }, ) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index a107230c..50892b45 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -34,7 +34,6 @@ class ThreadedSegment(MultiRunnedThread): def __init__(self): super(ThreadedSegment, self).__init__() - self.shutdown_event = Event() self.run_once = True self.skip = False self.crashed_value = None @@ -98,8 +97,9 @@ class ThreadedSegment(MultiRunnedThread): interval = interval or getattr(self, 'interval') self.interval = interval - def set_state(self, interval=None, update_first=True, **kwargs): + def set_state(self, interval=None, update_first=True, shutdown_event=None, **kwargs): self.set_interval(interval) + self.shutdown_event = shutdown_event or Event() self.updated = not (update_first and self.update_first) def startup(self, pl, **kwargs): @@ -177,8 +177,9 @@ class KwThreadedSegment(ThreadedSegment): return update_value - def set_state(self, interval=None, **kwargs): + def set_state(self, interval=None, shutdown_event=None, **kwargs): self.set_interval(interval) + self.shutdown_event = shutdown_event or Event() @staticmethod def render_one(update_state, **kwargs): diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6b2636af..6cbddc7d 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -242,6 +242,11 @@ class Spec(object): msg_func)) return self + def error(self, msg): + self.checks.append(('check_func', lambda *args: (True, True, True), + lambda value: msg.format(value))) + return self + def either(self, *specs): start = len(self.specs) self.specs.extend(specs) @@ -788,6 +793,9 @@ def check_segment_data_key(key, data, context, echoerr): args_spec = Spec( interval=Spec().either(Spec().type(float), Spec().type(int)).optional(), update_first=Spec().type(bool).optional(), + shutdown_event=Spec().error('Shutdown event must be set by powerline').optional(), + pl=Spec().error('pl object must be set by powerline').optional(), + segment_info=Spec().error('Segment info dictionary must be set by powerline').optional(), ).unknown_spec(Spec(), Spec()).optional().copy highlight_group_spec = Spec().type(unicode).copy segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy diff --git a/powerline/theme.py b/powerline/theme.py index 0b7cdd7a..2e3a3533 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -22,7 +22,14 @@ def requires_segment_info(func): class Theme(object): - def __init__(self, ext, theme_config, common_config, pl, top_theme_config=None, run_once=False): + def __init__(self, + ext, + theme_config, + common_config, + pl, + top_theme_config=None, + run_once=False, + shutdown_event=None): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = { @@ -44,7 +51,7 @@ class Theme(object): if not run_once: if segment['startup']: try: - segment['startup'](pl=pl, **segment['args']) + segment['startup'](pl=pl, shutdown_event=shutdown_event, **segment['args']) except Exception as e: pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) continue From af2f8f588bbc7107f1536c6005c9f85e11cb73b0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Apr 2013 22:57:14 +0400 Subject: [PATCH 0619/1472] Replace MultiClientWatcher and Powerline threads with ConfigLoader Also - move file opening and parsing to ConfigLoader - add interval configuration --- docs/source/configuration.rst | 4 + powerline/__init__.py | 173 +++++++++++----------------------- powerline/lib/config.py | 149 +++++++++++++++++++++++++++++ powerline/lib/threaded.py | 2 + powerline/lint/__init__.py | 20 ++-- tests/lib/config_mock.py | 23 +++-- tests/test_config_reload.py | 5 +- tests/test_segments.py | 2 +- 8 files changed, 239 insertions(+), 139 deletions(-) create mode 100644 powerline/lib/config.py diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index c2427383..50fbfc4e 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -166,6 +166,10 @@ Common configuration is a subdictionary that is a value of ``common`` key in String, determines format of the log messages. Defaults to ``'%(asctime)s:%(level)s:%(message)s'``. +``interval`` + Number, determines time (in seconds) between checks for changed + configuration. Use ``null`` to disable. Defaults to 10. + Extension-specific configuration -------------------------------- diff --git a/powerline/__init__.py b/powerline/__init__.py index 6b1d09c0..0f6373d1 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -7,58 +7,13 @@ import sys import logging from powerline.colorscheme import Colorscheme -from powerline.lib.file_watcher import create_file_watcher +from powerline.lib.config import ConfigLoader -from threading import Lock, Thread, Event -from collections import defaultdict +from threading import Lock, Event DEFAULT_SYSTEM_CONFIG_DIR = None -watcher = None - - -class MultiClientWatcher(object): - subscribers = set() - received_events = {} - - def __init__(self): - global watcher - self.subscribers.add(self) - if not watcher: - watcher = create_file_watcher() - - def watch(self, file): - watcher.watch(file) - - def __call__(self, file): - if self not in self.subscribers: - return False - - if file in self.received_events and self not in self.received_events[file]: - self.received_events[file].add(self) - if self.received_events[file] >= self.subscribers: - self.received_events.pop(file) - return True - - if watcher(file): - self.received_events[file] = set([self]) - return True - - return False - - def unsubscribe(self): - try: - self.subscribers.remove(self) - except KeyError: - pass - - __del__ = unsubscribe - - -def open_file(path): - return open(path, 'r') - def find_config_file(search_paths, config_file): config_file += '.json' @@ -69,11 +24,6 @@ def find_config_file(search_paths, config_file): raise IOError('Config file not found in search path: {0}'.format(config_file)) -def load_json_config(config_file_path, load=json.load, open_file=open_file): - with open_file(config_file_path) as config_file_fp: - return load(config_file_fp) - - class PowerlineState(object): def __init__(self, use_daemon_threads, logger, ext): self.logger = logger @@ -132,9 +82,12 @@ class Powerline(object): during python session. :param Logger logger: If present, no new logger will be created and this logger will be used. - :param float interval: - When reloading configuration wait for this amount of seconds. Set it to - None if you don’t want to reload configuration automatically. + :param bool use_daemon_threads: + Use daemon threads for. + :param Event shutdown_event: + Use this Event as shutdown_event. + :param ConfigLoader config_loader: + Class that manages (re)loading of configuration. ''' def __init__(self, @@ -143,35 +96,29 @@ class Powerline(object): run_once=False, logger=None, use_daemon_threads=True, - interval=10, - watcher=None): + shutdown_event=None, + config_loader=None): self.ext = ext self.renderer_module = renderer_module or ext self.run_once = run_once self.logger = logger self.use_daemon_threads = use_daemon_threads - self.interval = interval if '.' not in self.renderer_module: self.renderer_module = 'powerline.renderers.' + self.renderer_module elif self.renderer_module[-1] == '.': self.renderer_module = self.renderer_module[:-1] - self.config_paths = self.get_config_paths() + config_paths = self.get_config_paths() + self.find_config_file = lambda cfg_path: find_config_file(config_paths, cfg_path) - self.configs_lock = Lock() self.cr_kwargs_lock = Lock() self.create_renderer_kwargs = {} - self.shutdown_event = Event() - self.configs = defaultdict(set) - self.missing = defaultdict(set) - - self.thread = None + self.shutdown_event = shutdown_event or Event() + self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event) self.renderer_options = {} - self.watcher = watcher or MultiClientWatcher() - self.prev_common_config = None self.prev_ext_config = None self.pl = None @@ -224,6 +171,8 @@ class Powerline(object): if not self.pl: self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext) + if not self.config_loader.pl: + self.config_loader.pl = self.pl self.renderer_options.update( pl=self.pl, @@ -239,6 +188,12 @@ class Powerline(object): }, ) + if not self.run_once: + interval = self.common_config.get('interval', 10) + self.config_loader.set_interval(interval) + if interval is not None and not self.config_loader.is_alive(): + self.config_loader.start() + self.ext_config = config['ext'][self.ext] if self.ext_config != self.prev_ext_config: ext_config_differs = True @@ -287,9 +242,6 @@ class Powerline(object): else: self.renderer = renderer - if not self.run_once and not self.is_alive() and self.interval is not None: - self.start() - def get_log_handler(self): '''Get log handler. @@ -325,24 +277,20 @@ class Powerline(object): return config_paths def _load_config(self, cfg_path, type): - '''Load configuration and setup watcher.''' + '''Load configuration and setup watches.''' + function = getattr(self, 'on_' + type + '_change') try: - path = find_config_file(self.config_paths, cfg_path) + path = self.find_config_file(cfg_path) except IOError: - with self.configs_lock: - self.missing[type].add(cfg_path) + self.config_loader.register_missing(self.find_config_file, function, cfg_path) raise - with self.configs_lock: - self.configs[type].add(path) - self.watcher.watch(path) - return load_json_config(path) + self.config_loader.register(function, path) + return self.config_loader.load(path) def _purge_configs(self, type): - try: - with self.configs_lock: - self.configs.pop(type) - except KeyError: - pass + function = getattr(self, 'on_' + type + '_change') + self.config_loader.unregister_functions(set((function,))) + self.config_loader.unregister_missing(set(((self.find_config_file, function),))) def load_theme_config(self, name): '''Get theme configuration. @@ -409,48 +357,35 @@ class Powerline(object): return self.renderer.render(*args, **kwargs) def shutdown(self): - '''Lock renderer from modifications and run its ``.shutdown()`` method. + '''Shut down all background threads. Must be run only prior to exiting + current application. ''' self.shutdown_event.set() - if self.use_daemon_threads and self.is_alive(): - # Give the worker thread a chance to shutdown, but don't block for too long - self.thread.join(.01) self.renderer.shutdown() - self.watcher.unsubscribe() + functions = ( + self.on_main_change, + self.on_colors_change, + self.on_colorscheme_change, + self.on_theme_change, + ) + self.config_loader.unregister_functions(set(functions)) + self.config_loader.unregister_missing(set(((find_config_file, function) for function in functions))) - def is_alive(self): - return self.thread and self.thread.is_alive() + def on_main_change(self, path): + with self.cr_kwargs_lock: + self.create_renderer_kwargs['load_main'] = True - def start(self): - self.thread = Thread(target=self.run) - if self.use_daemon_threads: - self.thread.daemon = True - self.thread.start() + def on_colors_change(self, path): + with self.cr_kwargs_lock: + self.create_renderer_kwargs['load_colors'] = True - def run(self): - while not self.shutdown_event.is_set(): - kwargs = {} - removes = [] - with self.configs_lock: - for type, paths in self.configs.items(): - for path in paths: - if self.watcher(path): - kwargs['load_' + type] = True - for type, cfg_paths in self.missing.items(): - for cfg_path in cfg_paths: - try: - find_config_file(self.config_paths, cfg_path) - except IOError: - pass - else: - kwargs['load_' + type] = True - removes.append((type, cfg_path)) - for type, cfg_path in removes: - self.missing[type].remove(cfg_path) - if kwargs: - with self.cr_kwargs_lock: - self.create_renderer_kwargs.update(kwargs) - self.shutdown_event.wait(self.interval) + def on_colorscheme_change(self, path): + with self.cr_kwargs_lock: + self.create_renderer_kwargs['load_colorscheme'] = True + + def on_theme_change(self, path): + with self.cr_kwargs_lock: + self.create_renderer_kwargs['load_theme'] = True def __enter__(self): return self diff --git a/powerline/lib/config.py b/powerline/lib/config.py new file mode 100644 index 00000000..42f4ac92 --- /dev/null +++ b/powerline/lib/config.py @@ -0,0 +1,149 @@ +# vim:fileencoding=utf-8:noet + +from powerline.lib.threaded import MultiRunnedThread +from powerline.lib.file_watcher import create_file_watcher + +from threading import Event, Lock +from collections import defaultdict + +import json + + +def open_file(path): + return open(path, 'r') + + +def load_json_config(config_file_path, load=json.load, open_file=open_file): + with open_file(config_file_path) as config_file_fp: + return load(config_file_fp) + + +class ConfigLoader(MultiRunnedThread): + def __init__(self, shutdown_event=None, watcher=None, load=load_json_config): + super(ConfigLoader, self).__init__() + self.shutdown_event = shutdown_event or Event() + self.watcher = watcher or create_file_watcher() + self._load = load + + self.pl = None + self.interval = None + + self.lock = Lock() + + self.watched = defaultdict(set) + self.missing = defaultdict(set) + self.loaded = {} + + def set_pl(self, pl): + self.pl = pl + + def set_interval(self, interval): + self.interval = interval + + def register(self, function, path): + '''Register function that will be run when file changes. + + :param function function: + Function that will be called when file at the given path changes. + :param str path: + Path that will be watched for. + ''' + with self.lock: + self.watched[path].add(function) + self.watcher.watch(path) + + def register_missing(self, condition_function, function, key): + '''Register any function that will be called with given key each + interval seconds (interval is defined at __init__). Its result is then + passed to ``function``, but only if the result is true. + + :param function condition_function: + Function which will be called each ``interval`` seconds. All + exceptions from it will be ignored. + :param function function: + Function which will be called if condition_function returns + something that is true. Accepts result of condition_function as an + argument. + :param str key: + Any value, it will be passed to condition_function on each call. + + Note: registered functions will be automatically removed if + condition_function results in something true. + ''' + with self.lock: + self.missing[key].add((condition_function, function)) + + def unregister_functions(self, removed_functions): + '''Unregister files handled by these functions. + + :param set removed_functions: + Set of functions previously passed to ``.register()`` method. + ''' + removes = [] + with self.lock: + for path, functions in list(self.watched.items()): + functions -= removed_functions + if not functions: + self.watched.pop(path) + self.loaded.pop(path, None) + + def unregister_missing(self, removed_functions): + '''Unregister files handled by these functions. + + :param set removed_functions: + Set of pairs (2-tuples) representing ``(condition_function, + function)`` function pairs previously passed as an arguments to + ``.register_missing()`` method. + ''' + with self.lock: + for key, functions in list(self.missing.items()): + functions -= removed_functions + if not functions: + self.missing.pop(key) + + def load(self, path): + try: + # No locks: GIL does what we need + return self.loaded[path] + except KeyError: + r = self._load(path) + if self.interval is not None: + self.loaded[path] = r + return r + + def run(self): + while self.interval is not None and not self.shutdown_event.is_set(): + toload = [] + with self.lock: + for path, functions in self.watched.items(): + for function in functions: + if self.watcher(path): + function(path) + toload.append(path) + with self.lock: + for key, functions in list(self.missing.items()): + remove = False + for condition_function, function in list(functions): + try: + path = condition_function(key) + except Exception as e: + self.exception('Error while running condition function for key {0}: {1}', key, str(e)) + else: + if path: + toload.append(path) + function(path) + functions.remove((condition_function, function)) + if not functions: + self.missing.pop(key) + for path in toload: + try: + self.loaded[path] = self._load(path) + except Exception as e: + self.exception('Error while loading {0}: {1}', path, str(e)) + self.shutdown_event.wait(self.interval) + + def exception(self, msg, *args, **kwargs): + if self.pl: + self.pl.exception(msg, prefix='config_loader', *args, **kwargs) + else: + raise diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 50892b45..169495ad 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -8,6 +8,8 @@ from threading import Thread, Lock, Event class MultiRunnedThread(object): + daemon = True + def __init__(self): self.thread = None diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6cbddc7d..982062ec 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,5 +1,6 @@ from powerline.lint.markedjson import load -from powerline import load_json_config, find_config_file, Powerline +from powerline import find_config_file, Powerline +from powerline.lib.config import load_json_config from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes import itertools @@ -73,11 +74,15 @@ class Spec(object): spec.context_message(msg) return self - def check_type(self, value, context_mark, data, context, echoerr, t): - if type(value.value) is not t: + def check_type(self, value, context_mark, data, context, echoerr, types): + if type(value.value) not in types: echoerr(context=self.cmsg.format(key=context_key(context)), context_mark=context_mark, - problem='{0!r} must be a {1} instance, not {2}'.format(value, t.__name__, type(value.value).__name__), + problem='{0!r} must be a {1} instance, not {2}'.format( + value, + ', '.join((t.__name__ for t in types)), + type(value.value).__name__ + ), problem_mark=value.mark) return False, True return True, False @@ -141,8 +146,8 @@ class Spec(object): return False, hadproblem return True, hadproblem - def type(self, t): - self.checks.append(('check_type', t)) + def type(self, *args): + self.checks.append(('check_type', args)) return self cmp_funcs = { @@ -411,6 +416,7 @@ main_spec = (Spec( log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)), lambda value: 'unknown debugging level {0}'.format(value)).optional(), log_format=Spec().type(str).optional(), + interval=Spec().type(int, float, type(None)).optional(), ).context_message('Error while loading common configuration (key {key})'), ext=Spec( vim=Spec( @@ -791,7 +797,7 @@ def check_segment_data_key(key, data, context, echoerr): # FIXME More checks, limit existing to ThreadedSegment instances only args_spec = Spec( - interval=Spec().either(Spec().type(float), Spec().type(int)).optional(), + interval=Spec().type(int, float).optional(), update_first=Spec().type(bool).optional(), shutdown_event=Spec().error('Shutdown event must be set by powerline').optional(), pl=Spec().error('pl object must be set by powerline').optional(), diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 83a27f75..7243fcb3 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet from threading import Lock from powerline.renderer import Renderer +from powerline.lib.config import ConfigLoader from powerline import Powerline from copy import deepcopy @@ -9,12 +10,12 @@ access_log = [] access_lock = Lock() -def load_json_config(config, config_file_path, *args, **kwargs): +def load_json_config(config_file_path, *args, **kwargs): global access_log with access_lock: access_log.append(config_file_path) try: - return deepcopy(config[config_file_path]) + return deepcopy(config_container['config'][config_file_path]) except KeyError: raise IOError(config_file_path) @@ -41,10 +42,10 @@ class Watcher(object): pass def __call__(self, file): - if file in self.events: - with self.lock: + with self.lock: + if file in self.events: self.events.remove(file) - return True + return True return False def _reset(self, files): @@ -98,18 +99,20 @@ def get_powerline(**kwargs): return TestPowerline( ext='test', renderer_module='tests.lib.config_mock', - interval=0, logger=Logger(), - watcher=Watcher(), + config_loader=ConfigLoader(load=load_json_config, watcher=Watcher()), **kwargs ) -def swap_attributes(config_container, powerline_module, replaces): +config_container = None + + +def swap_attributes(cfg_container, powerline_module, replaces): + global config_container + config_container = cfg_container if not replaces: replaces = { - 'watcher': Watcher(), - 'load_json_config': lambda *args: load_json_config(config_container['config'], *args), 'find_config_file': lambda *args: find_config_file(config_container['config'], *args), } for attr, val in replaces.items(): diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index d74f2cbe..2dc34011 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -23,6 +23,7 @@ config = { }, }, 'spaces': 0, + 'interval': 0, }, 'ext': { 'test': { @@ -97,7 +98,7 @@ def sleep(interval): def add_watcher_events(p, *args, **kwargs): - p.watcher._reset(args) + p.config_loader.watcher._reset(args) while not p._will_create_renderer(): sleep(kwargs.get('interval', 0.000001)) if not kwargs.get('wait', True): @@ -187,7 +188,7 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'config') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents('config') - self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise']) + self.assertIn('exception:test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) config['colorschemes/test/nonexistentraise'] = { 'groups': { diff --git a/tests/test_segments.py b/tests/test_segments.py index f417f431..ca16eb93 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -492,4 +492,4 @@ def tearDownModule(): if __name__ == '__main__': from tests import main - main(verbosity=10) + main() From 88865b11455d629bd8c16f7e603c425bcd66b336 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 16:32:38 +0400 Subject: [PATCH 0620/1472] Defer create_renderer (and thus thread creation) until .render call May fix problem discussed in #397 Also fix documentation for Powerline.render(). --- powerline/__init__.py | 36 +++++++++++++++++++++++------------- powerline/vim.py | 1 + tests/test_config_reload.py | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 0f6373d1..4aa1eeaa 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -113,7 +113,12 @@ class Powerline(object): self.find_config_file = lambda cfg_path: find_config_file(config_paths, cfg_path) self.cr_kwargs_lock = Lock() - self.create_renderer_kwargs = {} + self.create_renderer_kwargs = { + 'load_main': True, + 'load_colors': True, + 'load_colorscheme': True, + 'load_theme': True, + } self.shutdown_event = shutdown_event or Event() self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event) @@ -123,8 +128,6 @@ class Powerline(object): self.prev_ext_config = None self.pl = None - self.create_renderer(load_main=True, load_colors=True, load_colorscheme=True, load_theme=True) - def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): '''(Re)create renderer object. Can be used after Powerline object was successfully initialized. If any of the below parameters except @@ -342,18 +345,25 @@ class Powerline(object): ''' return None - def render(self, *args, **kwargs): - '''Lock renderer from modifications and pass all arguments further to - ``self.renderer.render()``. - ''' + def update_renderer(self): + '''Updates/creates a renderer if needed.''' + create_renderer_kwargs = None with self.cr_kwargs_lock: if self.create_renderer_kwargs: - try: - self.create_renderer(**self.create_renderer_kwargs) - except Exception as e: - self.pl.exception('Failed to create renderer: {0}', str(e)) - finally: - self.create_renderer_kwargs.clear() + create_renderer_kwargs = self.create_renderer_kwargs.copy() + if create_renderer_kwargs: + try: + self.create_renderer(**create_renderer_kwargs) + except Exception as e: + self.pl.exception('Failed to create renderer: {0}', str(e)) + finally: + self.create_renderer_kwargs.clear() + + def render(self, *args, **kwargs): + '''Update/create renderer if needed and pass all arguments further to + ``self.renderer.render()``. + ''' + self.update_renderer() return self.renderer.render(*args, **kwargs) def shutdown(self): diff --git a/powerline/vim.py b/powerline/vim.py index b0610baf..df89b604 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -46,6 +46,7 @@ class VimPowerline(Powerline): ``True`` if theme was added successfully and ``False`` if theme with the same matcher already exists. ''' + self.update_renderer() key = self.get_matcher(key) try: self.renderer.add_local_theme(key, {'config': config}) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 2dc34011..7565c834 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -128,8 +128,8 @@ class TestConfigReload(TestCase): def test_reload_main(self): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') config['config']['common']['spaces'] = 1 add_watcher_events(p, 'config') From 296107d68e8a1fd5046d3d259e677091fab09494 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 18:07:30 +0400 Subject: [PATCH 0621/1472] Improve lint number (interval and priority) checks --- powerline/lint/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 982062ec..522f9d43 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -177,12 +177,14 @@ class Spec(object): def cmp(self, comparison, cint, msg_func=None): if type(cint) is str: self.type(unicode) + elif type(cint) is float: + self.type(int, float) else: self.type(type(cint)) cmp_func = self.cmp_funcs[comparison] msg_func = msg_func or (lambda value: '{0} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint)) self.checks.append(('check_func', - (lambda value, *args: (True, True, not cmp_func(value, cint))), + (lambda value, *args: (True, True, not cmp_func(value.value, cint))), msg_func)) return self @@ -416,7 +418,7 @@ main_spec = (Spec( log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)), lambda value: 'unknown debugging level {0}'.format(value)).optional(), log_format=Spec().type(str).optional(), - interval=Spec().type(int, float, type(None)).optional(), + interval=Spec().either(Spec().cmp('gt', 0.0), Spec().type(type(None))).optional(), ).context_message('Error while loading common configuration (key {key})'), ext=Spec( vim=Spec( @@ -797,7 +799,7 @@ def check_segment_data_key(key, data, context, echoerr): # FIXME More checks, limit existing to ThreadedSegment instances only args_spec = Spec( - interval=Spec().type(int, float).optional(), + interval=Spec().cmp('gt', 0.0).optional(), update_first=Spec().type(bool).optional(), shutdown_event=Spec().error('Shutdown event must be set by powerline').optional(), pl=Spec().error('pl object must be set by powerline').optional(), @@ -815,7 +817,7 @@ segments_spec = Spec().optional().list( draw_soft_divider=Spec().type(bool).optional(), draw_inner_divider=Spec().type(bool).optional(), module=segment_module_spec(), - priority=Spec().cmp('ge', -1).optional(), + priority=Spec().either(Spec().cmp('eq', -1), Spec().cmp('ge', 0.0)).optional(), after=Spec().type(unicode).optional(), before=Spec().type(unicode).optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), From d1d05c9dcf33a87f70f175a8c56c113dbaed326e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 18:09:45 +0400 Subject: [PATCH 0622/1472] Add support for reloading configuration in the main thread --- docs/source/configuration.rst | 8 ++++- powerline/__init__.py | 8 +++-- powerline/lib/config.py | 60 ++++++++++++++++++----------------- powerline/lint/__init__.py | 1 + tests/test_config_reload.py | 14 ++++++++ 5 files changed, 59 insertions(+), 32 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 50fbfc4e..949c77c8 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -168,7 +168,13 @@ Common configuration is a subdictionary that is a value of ``common`` key in ``interval`` Number, determines time (in seconds) between checks for changed - configuration. Use ``null`` to disable. Defaults to 10. + configuration. Checks are done in a seprate thread. Use ``null`` to check + for configuration changes on ``.render()`` call in main thread. + Defaults to ``None``. + +``reload_config`` + Boolean, determines whether configuration should be reloaded at all. + Defaults to ``True``. Extension-specific configuration -------------------------------- diff --git a/powerline/__init__.py b/powerline/__init__.py index 4aa1eeaa..d4542d2a 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -121,6 +121,7 @@ class Powerline(object): } self.shutdown_event = shutdown_event or Event() self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event) + self.run_loader_update = False self.renderer_options = {} @@ -191,9 +192,10 @@ class Powerline(object): }, ) - if not self.run_once: - interval = self.common_config.get('interval', 10) + if not self.run_once and self.common_config.get('reload_config', True): + interval = self.common_config.get('interval', None) self.config_loader.set_interval(interval) + self.run_loader_update = (interval is None) if interval is not None and not self.config_loader.is_alive(): self.config_loader.start() @@ -347,6 +349,8 @@ class Powerline(object): def update_renderer(self): '''Updates/creates a renderer if needed.''' + if self.run_loader_update: + self.config_loader.update() create_renderer_kwargs = None with self.cr_kwargs_lock: if self.create_renderer_kwargs: diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 42f4ac92..ebe2193d 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -107,39 +107,41 @@ class ConfigLoader(MultiRunnedThread): return self.loaded[path] except KeyError: r = self._load(path) - if self.interval is not None: - self.loaded[path] = r + self.loaded[path] = r return r + def update(self): + toload = [] + with self.lock: + for path, functions in self.watched.items(): + for function in functions: + if self.watcher(path): + function(path) + toload.append(path) + with self.lock: + for key, functions in list(self.missing.items()): + remove = False + for condition_function, function in list(functions): + try: + path = condition_function(key) + except Exception as e: + self.exception('Error while running condition function for key {0}: {1}', key, str(e)) + else: + if path: + toload.append(path) + function(path) + functions.remove((condition_function, function)) + if not functions: + self.missing.pop(key) + for path in toload: + try: + self.loaded[path] = self._load(path) + except Exception as e: + self.exception('Error while loading {0}: {1}', path, str(e)) + def run(self): while self.interval is not None and not self.shutdown_event.is_set(): - toload = [] - with self.lock: - for path, functions in self.watched.items(): - for function in functions: - if self.watcher(path): - function(path) - toload.append(path) - with self.lock: - for key, functions in list(self.missing.items()): - remove = False - for condition_function, function in list(functions): - try: - path = condition_function(key) - except Exception as e: - self.exception('Error while running condition function for key {0}: {1}', key, str(e)) - else: - if path: - toload.append(path) - function(path) - functions.remove((condition_function, function)) - if not functions: - self.missing.pop(key) - for path in toload: - try: - self.loaded[path] = self._load(path) - except Exception as e: - self.exception('Error while loading {0}: {1}', path, str(e)) + self.update() self.shutdown_event.wait(self.interval) def exception(self, msg, *args, **kwargs): diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 522f9d43..e5733a67 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -419,6 +419,7 @@ main_spec = (Spec( lambda value: 'unknown debugging level {0}'.format(value)).optional(), log_format=Spec().type(str).optional(), interval=Spec().either(Spec().cmp('gt', 0.0), Spec().type(type(None))).optional(), + reload_config=Spec().type(bool).optional(), ).context_message('Error while loading common configuration (key {key})'), ext=Spec( vim=Spec( diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 7565c834..a9a8a4f7 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -242,6 +242,20 @@ class TestConfigReload(TestCase): self.assertEqual(p.logger._pop_msgs(), []) pop_events() + def test_reload_theme_main(self): + with replace_item(globals(), 'config', deepcopy(config)): + config['config']['common']['interval'] = None + with get_powerline(run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + + config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' + add_watcher_events(p, 'themes/test/default', wait=False) + self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('themes/test/default') + self.assertEqual(p.logger._pop_msgs(), []) + pop_events() + replaces = {} From b17dab0cd4f4831a0fb37e4e66f870f3ac03fc7d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 18:30:33 +0400 Subject: [PATCH 0623/1472] Handle removed files --- powerline/lib/config.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index ebe2193d..9d5733f1 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -115,9 +115,16 @@ class ConfigLoader(MultiRunnedThread): with self.lock: for path, functions in self.watched.items(): for function in functions: - if self.watcher(path): + try: + modified = self.watcher(path) + except OSError as e: + modified = True + self.exception('Error while running watcher for path {0}: {1}', path, str(e)) + else: + if modified: + toload.append(path) + if modified: function(path) - toload.append(path) with self.lock: for key, functions in list(self.missing.items()): remove = False From 42ee82c1de84427689b842edbf9b87a65e3f06c0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 18:48:04 +0400 Subject: [PATCH 0624/1472] Update *watcher stuff from @kovidgoyal develop branch --- powerline/lib/file_watcher.py | 168 ++++------------------------- powerline/lib/inotify.py | 175 ++++++++++++++++++++++++++++++ powerline/lib/tree_watcher.py | 197 ++++++++++++++++++++++++++++++++++ tests/test_lib.py | 43 +++++++- 4 files changed, 432 insertions(+), 151 deletions(-) create mode 100644 powerline/lib/inotify.py create mode 100644 powerline/lib/tree_watcher.py diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 83d679ee..34616e90 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -6,120 +6,23 @@ __docformat__ = 'restructuredtext en' import os import sys -import errno from time import sleep from threading import RLock from powerline.lib.monotonic import monotonic +from powerline.lib.inotify import INotify, INotifyError -class INotifyError(Exception): - pass - - -class INotifyWatch(object): +class INotifyWatch(INotify): is_stat_based = False - # See for the flags defined below - - # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. - ACCESS = 0x00000001 # File was accessed. - MODIFY = 0x00000002 # File was modified. - ATTRIB = 0x00000004 # Metadata changed. - CLOSE_WRITE = 0x00000008 # Writtable file was closed. - CLOSE_NOWRITE = 0x00000010 # Unwrittable file closed. - OPEN = 0x00000020 # File was opened. - MOVED_FROM = 0x00000040 # File was moved from X. - MOVED_TO = 0x00000080 # File was moved to Y. - CREATE = 0x00000100 # Subfile was created. - DELETE = 0x00000200 # Subfile was deleted. - DELETE_SELF = 0x00000400 # Self was deleted. - MOVE_SELF = 0x00000800 # Self was moved. - - # Events sent by the kernel. - UNMOUNT = 0x00002000 # Backing fs was unmounted. - Q_OVERFLOW = 0x00004000 # Event queued overflowed. - IGNORED = 0x00008000 # File was ignored. - - # Helper events. - CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. - MOVE = (MOVED_FROM | MOVED_TO) # Moves. - - # Special flags. - ONLYDIR = 0x01000000 # Only watch the path if it is a directory. - DONT_FOLLOW = 0x02000000 # Do not follow a sym link. - EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. - MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. - ISDIR = 0x40000000 # Event occurred against dir. - ONESHOT = 0x80000000 # Only send event once. - - # All events which a program can wait on. - ALL_EVENTS = (ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | - OPEN | MOVED_FROM | MOVED_TO | CREATE | DELETE | - DELETE_SELF | MOVE_SELF) - - # See - CLOEXEC = 0x80000 - NONBLOCK = 0x800 - - def __init__(self, inotify_fd, add_watch, rm_watch, read, expire_time=10): - import ctypes - import struct - self._add_watch, self._rm_watch = add_watch, rm_watch - self._read = read - # We keep a reference to os to prevent it from being deleted - # during interpreter shutdown, which would lead to errors in the - # __del__ method - self.os = os + def __init__(self, expire_time=10): + super(INotifyWatch, self).__init__() self.watches = {} self.modified = {} self.last_query = {} - self._buf = ctypes.create_string_buffer(5000) - self.fenc = sys.getfilesystemencoding() or 'utf-8' - self.hdr = struct.Struct(b'iIII') - if self.fenc == 'ascii': - self.fenc = 'utf-8' self.lock = RLock() self.expire_time = expire_time * 60 - self._inotify_fd = inotify_fd - - def handle_error(self): - import ctypes - eno = ctypes.get_errno() - raise OSError(eno, self.os.strerror(eno)) - - def __del__(self): - # This method can be called during interpreter shutdown, which means we - # must do the absolute minimum here. Note that there could be running - # daemon threads that are trying to call other methods on this object. - try: - self.os.close(self._inotify_fd) - except (AttributeError, TypeError): - pass - - def read(self): - import ctypes - buf = [] - while True: - num = self._read(self._inotify_fd, self._buf, len(self._buf)) - if num == 0: - break - if num < 0: - en = ctypes.get_errno() - if en == errno.EAGAIN: - break # No more data - if en == errno.EINTR: - continue # Interrupted, try again - raise OSError(en, self.os.strerror(en)) - buf.append(self._buf.raw[:num]) - raw = b''.join(buf) - pos = 0 - lraw = len(raw) - while lraw - pos >= self.hdr.size: - wd, mask, cookie, name_len = self.hdr.unpack_from(raw, pos) - # We dont care about names as we only watch files - pos += self.hdr.size + name_len - self.process_event(wd, mask, cookie) def expire_watches(self): now = monotonic() @@ -127,7 +30,19 @@ class INotifyWatch(object): if last_query - now > self.expire_time: self.unwatch(path) - def process_event(self, wd, mask, cookie): + def process_event(self, wd, mask, cookie, name): + if wd == -1 and (mask & self.Q_OVERFLOW): + # We missed some INOTIFY events, so we dont + # know the state of any tracked files. + for path in tuple(self.modified): + if os.path.exists(path): + self.modified[path] = True + else: + self.watches.pop(path, None) + self.modified.pop(path, None) + self.last_query.pop(path, None) + return + for path, num in tuple(self.watches.items()): if num == wd: if mask & self.IGNORED: @@ -176,7 +91,7 @@ class INotifyWatch(object): # exist/you dont have permission self.watch(path) return True - self.read() + self.read(get_name=False) if path not in self.modified: # An ignored event was received which means the path has been # automatically unwatched @@ -193,50 +108,7 @@ class INotifyWatch(object): self.unwatch(path) except OSError: pass - if hasattr(self, '_inotify_fd'): - self.os.close(self._inotify_fd) - del self.os - del self._add_watch - del self._rm_watch - del self._inotify_fd - - -def get_inotify(expire_time=10): - ''' Initialize the inotify based file watcher ''' - import ctypes - if not hasattr(ctypes, 'c_ssize_t'): - raise INotifyError('You need python >= 2.7 to use inotify') - from ctypes.util import find_library - name = find_library('c') - if not name: - raise INotifyError('Cannot find C library') - libc = ctypes.CDLL(name, use_errno=True) - for function in ("inotify_add_watch", "inotify_init1", "inotify_rm_watch"): - if not hasattr(libc, function): - raise INotifyError('libc is too old') - # inotify_init1() - prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) - init1 = prototype(('inotify_init1', libc), ((1, "flags", 0),)) - - # inotify_add_watch() - prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) - add_watch = prototype(('inotify_add_watch', libc), ( - (1, "fd"), (1, "pathname"), (1, "mask")), use_errno=True) - - # inotify_rm_watch() - prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) - rm_watch = prototype(('inotify_rm_watch', libc), ( - (1, "fd"), (1, "wd")), use_errno=True) - - # read() - prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) - read = prototype(('read', libc), ( - (1, "fd"), (1, "buf"), (1, "count")), use_errno=True) - - inotify_fd = init1(INotifyWatch.CLOEXEC | INotifyWatch.NONBLOCK) - if inotify_fd == -1: - raise INotifyError(os.strerror(ctypes.get_errno())) - return INotifyWatch(inotify_fd, add_watch, rm_watch, read, expire_time=expire_time) + super(INotifyWatch, self).close() class StatWatch(object): @@ -289,7 +161,7 @@ def create_file_watcher(use_stat=False, expire_time=10): if use_stat: return StatWatch() try: - return get_inotify(expire_time=expire_time) + return INotifyWatch(expire_time=expire_time) except INotifyError: pass return StatWatch() diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py new file mode 100644 index 00000000..ecfdd1f8 --- /dev/null +++ b/powerline/lib/inotify.py @@ -0,0 +1,175 @@ +# vim:fileencoding=UTF-8:noet +from __future__ import unicode_literals, absolute_import + +__copyright__ = '2013, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import sys, os, errno + +class INotifyError(Exception): + pass + +_inotify = None + +def load_inotify(): + ''' Initialize the inotify library ''' + global _inotify + if _inotify is None: + if hasattr(sys, 'getwindowsversion'): + # On windows abort before loading the C library. Windows has + # multiple, incompatible C runtimes, and we have no way of knowing + # if the one chosen by ctypes is compatible with the currently + # loaded one. + raise INotifyError('INotify not available on windows') + import ctypes + if not hasattr(ctypes, 'c_ssize_t'): + raise INotifyError('You need python >= 2.7 to use inotify') + from ctypes.util import find_library + name = find_library('c') + if not name: + raise INotifyError('Cannot find C library') + libc = ctypes.CDLL(name, use_errno=True) + for function in ("inotify_add_watch", "inotify_init1", "inotify_rm_watch"): + if not hasattr(libc, function): + raise INotifyError('libc is too old') + # inotify_init1() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) + init1 = prototype(('inotify_init1', libc), ((1, "flags", 0),)) + + # inotify_add_watch() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) + add_watch = prototype(('inotify_add_watch', libc), ( + (1, "fd"), (1, "pathname"), (1, "mask")), use_errno=True) + + # inotify_rm_watch() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) + rm_watch = prototype(('inotify_rm_watch', libc), ( + (1, "fd"), (1, "wd")), use_errno=True) + + # read() + prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) + read = prototype(('read', libc), ( + (1, "fd"), (1, "buf"), (1, "count")), use_errno=True) + _inotify = (init1, add_watch, rm_watch, read) + return _inotify + +class INotify(object): + + # See for the flags defined below + + # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. + ACCESS = 0x00000001 # File was accessed. + MODIFY = 0x00000002 # File was modified. + ATTRIB = 0x00000004 # Metadata changed. + CLOSE_WRITE = 0x00000008 # Writtable file was closed. + CLOSE_NOWRITE = 0x00000010 # Unwrittable file closed. + OPEN = 0x00000020 # File was opened. + MOVED_FROM = 0x00000040 # File was moved from X. + MOVED_TO = 0x00000080 # File was moved to Y. + CREATE = 0x00000100 # Subfile was created. + DELETE = 0x00000200 # Subfile was deleted. + DELETE_SELF = 0x00000400 # Self was deleted. + MOVE_SELF = 0x00000800 # Self was moved. + + # Events sent by the kernel. + UNMOUNT = 0x00002000 # Backing fs was unmounted. + Q_OVERFLOW = 0x00004000 # Event queued overflowed. + IGNORED = 0x00008000 # File was ignored. + + # Helper events. + CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. + MOVE = (MOVED_FROM | MOVED_TO) # Moves. + + # Special flags. + ONLYDIR = 0x01000000 # Only watch the path if it is a directory. + DONT_FOLLOW = 0x02000000 # Do not follow a sym link. + EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. + MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. + ISDIR = 0x40000000 # Event occurred against dir. + ONESHOT = 0x80000000 # Only send event once. + + # All events which a program can wait on. + ALL_EVENTS = (ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | + OPEN | MOVED_FROM | MOVED_TO | CREATE | DELETE | + DELETE_SELF | MOVE_SELF) + + # See + CLOEXEC = 0x80000 + NONBLOCK = 0x800 + + def __init__(self, cloexec=True, nonblock=True): + import ctypes, struct + self._init1, self._add_watch, self._rm_watch, self._read = load_inotify() + flags = 0 + if cloexec: + flags |= self.CLOEXEC + if nonblock: + flags |= self.NONBLOCK + self._inotify_fd = self._init1(flags) + if self._inotify_fd == -1: + raise INotifyError(os.strerror(ctypes.get_errno())) + + self._buf = ctypes.create_string_buffer(5000) + self.fenc = sys.getfilesystemencoding() or 'utf-8' + self.hdr = struct.Struct(b'iIII') + if self.fenc == 'ascii': + self.fenc = 'utf-8' + # We keep a reference to os to prevent it from being deleted + # during interpreter shutdown, which would lead to errors in the + # __del__ method + self.os = os + + def handle_error(self): + import ctypes + eno = ctypes.get_errno() + raise OSError(eno, self.os.strerror(eno)) + + def __del__(self): + # This method can be called during interpreter shutdown, which means we + # must do the absolute minimum here. Note that there could be running + # daemon threads that are trying to call other methods on this object. + try: + self.os.close(self._inotify_fd) + except (AttributeError, TypeError): + pass + + def close(self): + if hasattr(self, '_inotify_fd'): + self.os.close(self._inotify_fd) + del self.os + del self._add_watch + del self._rm_watch + del self._inotify_fd + + def read(self, get_name=True): + import ctypes + buf = [] + while True: + num = self._read(self._inotify_fd, self._buf, len(self._buf)) + if num == 0: + break + if num < 0: + en = ctypes.get_errno() + if en == errno.EAGAIN: + break # No more data + if en == errno.EINTR: + continue # Interrupted, try again + raise OSError(en, self.os.strerror(en)) + buf.append(self._buf.raw[:num]) + raw = b''.join(buf) + pos = 0 + lraw = len(raw) + while lraw - pos >= self.hdr.size: + wd, mask, cookie, name_len = self.hdr.unpack_from(raw, pos) + pos += self.hdr.size + name = None + if get_name: + name = raw[pos:pos+name_len].rstrip(b'\0').decode(self.fenc) + pos += name_len + self.process_event(wd, mask, cookie, name) + + def process_event(self, *args): + raise NotImplementedError() + + + diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py new file mode 100644 index 00000000..1d2070c5 --- /dev/null +++ b/powerline/lib/tree_watcher.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:noet +from __future__ import (unicode_literals, absolute_import, print_function) + +__copyright__ = '2013, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import sys, os, errno +from time import sleep +from powerline.lib.monotonic import monotonic + +from powerline.lib.inotify import INotify, INotifyError + +class NoSuchDir(ValueError): + pass + +class DirTooLarge(ValueError): + + def __init__(self, bdir): + ValueError.__init__(self, 'The directory %s is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'%bdir) + +class INotifyTreeWatcher(INotify): + + is_dummy = False + + def __init__(self, basedir): + super(INotifyTreeWatcher, self).__init__() + self.basedir = os.path.abspath(basedir) + self.watch_tree() + self.modified = True + + def watch_tree(self): + self.watched_dirs = {} + self.watched_rmap = {} + try: + self.add_watches(self.basedir) + except OSError as e: + if e.errno == errno.ENOSPC: + raise DirTooLarge(self.basedir) + + def add_watches(self, base, top_level=True): + ''' Add watches for this directory and all its descendant directories, + recursively. ''' + base = os.path.abspath(base) + try: + is_dir = self.add_watch(base) + except OSError as e: + if e.errno == errno.ENOENT: + # The entry could have been deleted between listdir() and + # add_watch(). + if top_level: + raise NoSuchDir('The dir %s does not exist'%base) + return + if e.errno == errno.EACCES: + # We silently ignore entries for which we dont have permission, + # unless they are the top level dir + if top_level: + raise NoSuchDir('You do not have permission to monitor %s'%base) + return + raise + else: + if is_dir: + try: + files = os.listdir(base) + except OSError as e: + if e.errno in (errno.ENOTDIR, errno.ENOENT): + # The dir was deleted/replaced between the add_watch() + # and listdir() + if top_level: + raise NoSuchDir('The dir %s does not exist'%base) + return + raise + for x in files: + self.add_watches(os.path.join(base, x), top_level=False) + elif top_level: + # The top level dir is a file, not good. + raise NoSuchDir('The dir %s does not exist'%base) + + def add_watch(self, path): + import ctypes + bpath = path if isinstance(path, bytes) else path.encode(self.fenc) + wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), + self.DONT_FOLLOW | self.ONLYDIR | # Ignore symlinks and watch only directories + self.MODIFY | self.CREATE | self.DELETE | + self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | + self.ATTRIB | self.MOVE_SELF | self.DELETE_SELF) + if wd == -1: + eno = ctypes.get_errno() + if eno == errno.ENOTDIR: + return False + raise OSError(eno, 'Failed to add watch for: %s: %s'%(path, self.os.strerror(eno))) + self.watched_dirs[path] = wd + self.watched_rmap[wd] = path + return True + + def process_event(self, wd, mask, cookie, name): + if wd == -1 and (mask & self.Q_OVERFLOW): + # We missed some INOTIFY events, so we dont + # know the state of any tracked dirs. + self.watch_tree() + self.modified = True + return + path = self.watched_rmap.get(wd, None) + if path is not None: + self.modified = True + if mask & self.CREATE: + # A new sub-directory might have been created, monitor it. + try: + self.add_watch(os.path.join(path, name)) + except OSError as e: + if e.errno == errno.ENOENT: + # Deleted before add_watch() + pass + elif e.errno == errno.ENOSPC: + raise DirTooLarge(self.basedir) + else: + raise + + def __call__(self): + self.read() + ret = self.modified + self.modified = False + return ret + +class DummyTreeWatcher(object): + + is_dummy = True + + def __init__(self, basedir): + self.basedir = os.path.abspath(basedir) + + def __call__(self): + return False + +class TreeWatcher(object): + + def __init__(self, expire_time=10): + self.watches = {} + self.last_query_times = {} + self.expire_time = expire_time * 60 + + def watch(self, path, logger=None): + path = os.path.abspath(path) + try: + w = INotifyTreeWatcher(path) + except (INotifyError, DirTooLarge) as e: + if logger is not None: + logger.warn('Failed to watch path: %s with error: %s'%(path, e)) + w = DummyTreeWatcher(path) + self.watches[path] = w + return w + + def is_actually_watched(self, path): + w = self.watches.get(path, None) + return not getattr(w, 'is_dummy', True) + + def expire_old_queries(self): + pop = [] + now = monotonic() + for path, lt in self.last_query_times.items(): + if now - lt > self.expire_time: + pop.append(path) + for path in pop: + del self.last_query_times[path] + + def __call__(self, path, logger=None): + path = os.path.abspath(path) + self.expire_old_queries() + self.last_query_times[path] = monotonic() + w = self.watches.get(path, None) + if w is None: + try: + self.watch(path) + except NoSuchDir: + pass + return True + try: + return w() + except DirTooLarge as e: + if logger is not None: + logger.warn(str(e)) + self.watches[path] = DummyTreeWatcher(path) + return False + +if __name__ == '__main__': + w = INotifyTreeWatcher(sys.argv[-1]) + w() + print ('Monitoring', sys.argv[-1], 'press Ctrl-C to stop') + try: + while True: + if w(): + print (sys.argv[-1], 'changed') + sleep(1) + except KeyboardInterrupt: + raise SystemExit(0) + + diff --git a/tests/test_lib.py b/tests/test_lib.py index 1983e6c8..b51dac83 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -5,6 +5,8 @@ from powerline.lib.vcs import guess from subprocess import call, PIPE import os import sys +from unittest.case import SkipTest +from functools import partial from tests import TestCase @@ -36,6 +38,8 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=True), '1.00 GB') self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') +class TestFilesystemWatchers(TestCase): + def do_test_for_change(self, watcher, path): import time st = time.time() @@ -49,9 +53,7 @@ class TestLib(TestCase): from powerline.lib.file_watcher import create_file_watcher w = create_file_watcher(use_stat=False) if w.is_stat_based: - # The granularity of mtime (1 second) means that we cannot use the - # same tests for inotify and StatWatch. - return + raise SkipTest('This test is not suitable for a stat based file watcher') f1, f2 = os.path.join(INOTIFY_DIR, 'file1'), os.path.join(INOTIFY_DIR, 'file2') with open(f1, 'wb'): with open(f2, 'wb'): @@ -84,6 +86,41 @@ class TestLib(TestCase): os.unlink(f1) self.do_test_for_change(w, f1) + def test_tree_watcher(self): + from powerline.lib.tree_watcher import TreeWatcher + tw = TreeWatcher() + subdir = os.path.join(INOTIFY_DIR, 'subdir') + os.mkdir(subdir) + if tw.watch(INOTIFY_DIR).is_dummy: + raise SkipTest('No tree watcher available') + import shutil + self.assertTrue(tw(INOTIFY_DIR)) + self.assertFalse(tw(INOTIFY_DIR)) + changed = partial(self.do_test_for_change, tw, INOTIFY_DIR) + open(os.path.join(INOTIFY_DIR, 'tree1'), 'w').close() + changed() + open(os.path.join(subdir, 'tree1'), 'w').close() + changed() + os.unlink(os.path.join(subdir, 'tree1')) + changed() + os.rmdir(subdir) + changed() + os.mkdir(subdir) + changed() + os.rename(subdir, subdir+'1') + changed() + shutil.rmtree(subdir+'1') + changed() + os.mkdir(subdir) + f = os.path.join(subdir, 'f') + open(f, 'w').close() + changed() + with open(f, 'a') as s: + s.write(' ') + changed() + os.rename(f, f+'1') + changed() + use_mercurial = use_bzr = sys.version_info < (3, 0) From e68bae6409930f4abdc6248a6324a161e663bb08 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 18:45:57 +0400 Subject: [PATCH 0625/1472] Add fixes for flake8 to that stuff --- powerline/lib/file_watcher.py | 2 +- powerline/lib/inotify.py | 17 ++++++++++------- powerline/lib/tree_watcher.py | 34 ++++++++++++++++++---------------- tests/test_lib.py | 12 ++++++------ 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 34616e90..426e168d 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -12,8 +12,8 @@ from threading import RLock from powerline.lib.monotonic import monotonic from powerline.lib.inotify import INotify, INotifyError -class INotifyWatch(INotify): +class INotifyWatch(INotify): is_stat_based = False def __init__(self, expire_time=10): diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index ecfdd1f8..1b58d49a 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -4,13 +4,18 @@ from __future__ import unicode_literals, absolute_import __copyright__ = '2013, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import sys, os, errno +import sys +import os +import errno + class INotifyError(Exception): pass + _inotify = None + def load_inotify(): ''' Initialize the inotify library ''' global _inotify @@ -53,8 +58,8 @@ def load_inotify(): _inotify = (init1, add_watch, rm_watch, read) return _inotify -class INotify(object): +class INotify(object): # See for the flags defined below # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. @@ -98,7 +103,8 @@ class INotify(object): NONBLOCK = 0x800 def __init__(self, cloexec=True, nonblock=True): - import ctypes, struct + import ctypes + import struct self._init1, self._add_watch, self._rm_watch, self._read = load_inotify() flags = 0 if cloexec: @@ -164,12 +170,9 @@ class INotify(object): pos += self.hdr.size name = None if get_name: - name = raw[pos:pos+name_len].rstrip(b'\0').decode(self.fenc) + name = raw[pos:pos + name_len].rstrip(b'\0').decode(self.fenc) pos += name_len self.process_event(wd, mask, cookie, name) def process_event(self, *args): raise NotImplementedError() - - - diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index 1d2070c5..e345d286 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -1,26 +1,28 @@ -#!/usr/bin/env python # vim:fileencoding=UTF-8:noet from __future__ import (unicode_literals, absolute_import, print_function) __copyright__ = '2013, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import sys, os, errno +import sys +import os +import errno from time import sleep from powerline.lib.monotonic import monotonic from powerline.lib.inotify import INotify, INotifyError + class NoSuchDir(ValueError): pass -class DirTooLarge(ValueError): +class DirTooLarge(ValueError): def __init__(self, bdir): - ValueError.__init__(self, 'The directory %s is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'%bdir) + ValueError.__init__(self, 'The directory {0} is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'.format(bdir)) + class INotifyTreeWatcher(INotify): - is_dummy = False def __init__(self, basedir): @@ -49,13 +51,13 @@ class INotifyTreeWatcher(INotify): # The entry could have been deleted between listdir() and # add_watch(). if top_level: - raise NoSuchDir('The dir %s does not exist'%base) + raise NoSuchDir('The dir {0} does not exist'.format(base)) return if e.errno == errno.EACCES: # We silently ignore entries for which we dont have permission, # unless they are the top level dir if top_level: - raise NoSuchDir('You do not have permission to monitor %s'%base) + raise NoSuchDir('You do not have permission to monitor {0}'.format(base)) return raise else: @@ -67,20 +69,22 @@ class INotifyTreeWatcher(INotify): # The dir was deleted/replaced between the add_watch() # and listdir() if top_level: - raise NoSuchDir('The dir %s does not exist'%base) + raise NoSuchDir('The dir {0} does not exist'.format(base)) return raise for x in files: self.add_watches(os.path.join(base, x), top_level=False) elif top_level: # The top level dir is a file, not good. - raise NoSuchDir('The dir %s does not exist'%base) + raise NoSuchDir('The dir {0} does not exist'.format(base)) def add_watch(self, path): import ctypes bpath = path if isinstance(path, bytes) else path.encode(self.fenc) wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), - self.DONT_FOLLOW | self.ONLYDIR | # Ignore symlinks and watch only directories + # Ignore symlinks and watch only directories + self.DONT_FOLLOW | self.ONLYDIR | + self.MODIFY | self.CREATE | self.DELETE | self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | self.ATTRIB | self.MOVE_SELF | self.DELETE_SELF) @@ -88,7 +92,7 @@ class INotifyTreeWatcher(INotify): eno = ctypes.get_errno() if eno == errno.ENOTDIR: return False - raise OSError(eno, 'Failed to add watch for: %s: %s'%(path, self.os.strerror(eno))) + raise OSError(eno, 'Failed to add watch for: {0}: {1}'.format(path, self.os.strerror(eno))) self.watched_dirs[path] = wd self.watched_rmap[wd] = path return True @@ -122,8 +126,8 @@ class INotifyTreeWatcher(INotify): self.modified = False return ret -class DummyTreeWatcher(object): +class DummyTreeWatcher(object): is_dummy = True def __init__(self, basedir): @@ -132,8 +136,8 @@ class DummyTreeWatcher(object): def __call__(self): return False -class TreeWatcher(object): +class TreeWatcher(object): def __init__(self, expire_time=10): self.watches = {} self.last_query_times = {} @@ -145,7 +149,7 @@ class TreeWatcher(object): w = INotifyTreeWatcher(path) except (INotifyError, DirTooLarge) as e: if logger is not None: - logger.warn('Failed to watch path: %s with error: %s'%(path, e)) + logger.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) w = DummyTreeWatcher(path) self.watches[path] = w return w @@ -193,5 +197,3 @@ if __name__ == '__main__': sleep(1) except KeyboardInterrupt: raise SystemExit(0) - - diff --git a/tests/test_lib.py b/tests/test_lib.py index b51dac83..f903b323 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -38,8 +38,8 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=True), '1.00 GB') self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') -class TestFilesystemWatchers(TestCase): +class TestFilesystemWatchers(TestCase): def do_test_for_change(self, watcher, path): import time st = time.time() @@ -47,7 +47,7 @@ class TestFilesystemWatchers(TestCase): if watcher(path): return time.sleep(0.1) - self.fail('The change to %s was not detected'%path) + self.fail('The change to {0} was not detected'.format(path)) def test_file_watcher(self): from powerline.lib.file_watcher import create_file_watcher @@ -107,9 +107,9 @@ class TestFilesystemWatchers(TestCase): changed() os.mkdir(subdir) changed() - os.rename(subdir, subdir+'1') + os.rename(subdir, subdir + '1') changed() - shutil.rmtree(subdir+'1') + shutil.rmtree(subdir + '1') changed() os.mkdir(subdir) f = os.path.join(subdir, 'f') @@ -118,7 +118,7 @@ class TestFilesystemWatchers(TestCase): with open(f, 'a') as s: s.write(' ') changed() - os.rename(f, f+'1') + os.rename(f, f + '1') changed() use_mercurial = use_bzr = sys.version_info < (3, 0) @@ -192,6 +192,7 @@ HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') INOTIFY_DIR = 'inotify' + os.environ.get('PYTHON', '') + def setUpModule(): global old_cwd global old_HGRCPATH @@ -217,7 +218,6 @@ def setUpModule(): os.mkdir(INOTIFY_DIR) - def tearDownModule(): global old_cwd global old_HGRCPATH From b1fecebd53ad0952a06bf28fc2a74145089704b7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 18:55:41 +0400 Subject: [PATCH 0626/1472] Some fixes for flake8 --- powerline/__init__.py | 1 - powerline/lib/config.py | 4 +--- powerline/segments/common.py | 2 ++ tests/test_config_reload.py | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index d4542d2a..ed446918 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -1,7 +1,6 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import -import json import os import sys import logging diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 9d5733f1..b3ef57b5 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -56,7 +56,7 @@ class ConfigLoader(MultiRunnedThread): '''Register any function that will be called with given key each interval seconds (interval is defined at __init__). Its result is then passed to ``function``, but only if the result is true. - + :param function condition_function: Function which will be called each ``interval`` seconds. All exceptions from it will be ignored. @@ -79,7 +79,6 @@ class ConfigLoader(MultiRunnedThread): :param set removed_functions: Set of functions previously passed to ``.register()`` method. ''' - removes = [] with self.lock: for path, functions in list(self.watched.items()): functions -= removed_functions @@ -127,7 +126,6 @@ class ConfigLoader(MultiRunnedThread): function(path) with self.lock: for key, functions in list(self.missing.items()): - remove = False for condition_function, function in list(functions): try: path = condition_function(key) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e50615cc..c3e9864d 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -18,8 +18,10 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.theme import requires_segment_info from collections import namedtuple + cpu_count = None + @requires_segment_info def hostname(pl, segment_info, only_if_ssh=False, exclude_domain=False): '''Return the current hostname. diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index a9a8a4f7..b58eaff8 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -6,7 +6,6 @@ from tests import TestCase from tests.lib import replace_item from tests.lib.config_mock import swap_attributes, get_powerline, pop_events from copy import deepcopy -from threading import Lock config = { From 5b8b3599be702a05d381254bd6dbe6463913c0b0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 19:01:59 +0400 Subject: [PATCH 0627/1472] Fix tests on python-2.6 --- tests/__init__.py | 2 ++ tests/test_lib.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 13b41550..2492b045 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -2,5 +2,7 @@ import sys if sys.version_info < (2, 7): from unittest2 import TestCase, main # NOQA + from unittest2.case import SkipTest # NOQA else: from unittest import TestCase, main # NOQA + from unittest.case import SkipTest # NOQA diff --git a/tests/test_lib.py b/tests/test_lib.py index f903b323..654f4905 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -5,9 +5,8 @@ from powerline.lib.vcs import guess from subprocess import call, PIPE import os import sys -from unittest.case import SkipTest from functools import partial -from tests import TestCase +from tests import TestCase, SkipTest class TestLib(TestCase): From 62127112316816738769b9a3bfa6586a71953a71 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 21:21:52 +0400 Subject: [PATCH 0628/1472] Add forgotten self.updated set Ref #367 --- powerline/lib/threaded.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 169495ad..2b311189 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -40,6 +40,7 @@ class ThreadedSegment(MultiRunnedThread): self.skip = False self.crashed_value = None self.update_value = None + self.updated = False def __call__(self, pl, update_first=True, **kwargs): if self.run_once: @@ -56,6 +57,7 @@ class ThreadedSegment(MultiRunnedThread): self.start() elif not self.updated: update_value = self.get_update_value(True) + self.updated = True else: update_value = self.update_value @@ -102,7 +104,7 @@ class ThreadedSegment(MultiRunnedThread): def set_state(self, interval=None, update_first=True, shutdown_event=None, **kwargs): self.set_interval(interval) self.shutdown_event = shutdown_event or Event() - self.updated = not (update_first and self.update_first) + self.updated = self.updated or (not (update_first and self.update_first)) def startup(self, pl, **kwargs): self.run_once = False From 25f19596dd215978834e2b462248683cabab0fe3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 6 Apr 2013 23:14:18 +0400 Subject: [PATCH 0629/1472] Also ignore E225 like suggested in CONTRIBUTING.rst --- .local.vimrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.local.vimrc b/.local.vimrc index 766efbfd..ef30b09e 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -1,2 +1,2 @@ setlocal noexpandtab -let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128' +let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225' From 8cb1538e8ea13187461515077e05f5284521d70d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 02:32:29 +0400 Subject: [PATCH 0630/1472] Normalize modelines --- powerline/lib/file_watcher.py | 2 +- powerline/lib/inotify.py | 2 +- powerline/lib/tree_watcher.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 426e168d..d2874c88 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -1,4 +1,4 @@ -# vim:fileencoding=UTF-8:noet +# vim:fileencoding=utf-8:noet from __future__ import unicode_literals, absolute_import __copyright__ = '2013, Kovid Goyal ' diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index 1b58d49a..9f247bca 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -1,4 +1,4 @@ -# vim:fileencoding=UTF-8:noet +# vim:fileencoding=utf-8:noet from __future__ import unicode_literals, absolute_import __copyright__ = '2013, Kovid Goyal ' diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index e345d286..c8568891 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -1,4 +1,4 @@ -# vim:fileencoding=UTF-8:noet +# vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, absolute_import, print_function) __copyright__ = '2013, Kovid Goyal ' From 9ce63d1657bd15873ad4c4221a698b6e93155e89 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 03:24:39 +0400 Subject: [PATCH 0631/1472] Allow non-format messages --- powerline/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index ed446918..ebc50ac7 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -34,7 +34,9 @@ class PowerlineState(object): def _log(self, attr, msg, *args, **kwargs): prefix = kwargs.get('prefix') or self.prefix prefix = self.ext + ((':' + prefix) if prefix else '') - msg = prefix + ':' + msg.format(*args, **kwargs) + if args or kwargs: + msg = msg.format(*args, **kwargs) + msg = prefix + ':' + msg key = attr + ':' + prefix if msg != self.last_msgs.get(key): getattr(self.logger, attr)(msg) From 2c533789451b9f88b7a3c827c661b9a5c03574a2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 04:16:57 +0400 Subject: [PATCH 0632/1472] Also ignore trailing whitespaces --- .local.vimrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.local.vimrc b/.local.vimrc index ef30b09e..afd2178e 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -1,2 +1,2 @@ setlocal noexpandtab -let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225' +let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225,W291' From 3c613bc1b729904883bca77924d892012b93cdc3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 04:18:48 +0400 Subject: [PATCH 0633/1472] Use xmlrpclib.escape for escaping in PangoMarkupRenderer --- powerline/renderers/pango_markup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index f5e85b6a..0f2cb0ac 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -3,6 +3,8 @@ from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +from xmlrpclib import escape as _escape + class PangoMarkupRenderer(Renderer): '''Powerline Pango markup segment renderer.''' @@ -30,5 +32,7 @@ class PangoMarkupRenderer(Renderer): awesome_attr += ['underline="single"'] return '' + contents + '' + escape = staticmethod(_escape) + renderer = PangoMarkupRenderer From b5070297414dce26c7d84bd1f76e193f9d054027 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 04:19:45 +0400 Subject: [PATCH 0634/1472] Replace shell script with python one --- packages/gentoo/app-misc/powerline/Manifest | 2 +- .../app-misc/powerline/powerline-9999.ebuild | 4 +- .../bindings/awesome/powerline-awesome.py | 38 +++++++++++++++++++ powerline/bindings/awesome/powerline.lua | 4 +- powerline/bindings/awesome/powerline.sh | 10 ----- 5 files changed, 43 insertions(+), 15 deletions(-) create mode 100755 powerline/bindings/awesome/powerline-awesome.py delete mode 100755 powerline/bindings/awesome/powerline.sh diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest index 802c4188..cca6d4dd 100644 --- a/packages/gentoo/app-misc/powerline/Manifest +++ b/packages/gentoo/app-misc/powerline/Manifest @@ -1 +1 @@ -EBUILD powerline-9999.ebuild 4091 SHA256 d1d13b09e3ebefdaa1b90c50ccceb563bb444ca0f484e6448a993d2612dfbb78 SHA512 47f211249bb85608cb9c5b8e72daba5c36971e294e27843396cbf517bc792db64a4f80e843f5c883f8411ed4c9cef5618f9d617b305303a31a4590636879dcd3 WHIRLPOOL 2fc50e1da46d56d160140d3a87a4d9421bf63d67c792bfd88486e0dc72d379045ed79d152aa060a014e7f6bf4eb232642463014de9523909946bd8b2cbf83371 +EBUILD powerline-9999.ebuild 4107 SHA256 721f3360196aa4caa3656ff6e051add2c70cbfc37e965fce9830ecb668148b81 SHA512 27660689e5a0cf86d17268a18fdd917abfb8b2d88b33049fe66f3394c9a85211f24e50af8df1f39d93d11e8bfd442e4636ed92821441daddb56ae9d34162296a WHIRLPOOL 602a2d0d36b1e80da9d1f00c019872369010cd413c4761c1f92a8f53ee5bfb63122d8c13e896700406bf71d99db5994bcd3c7ae46c07218d173bd7084f1a13f1 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild index 8eadbabb..be065566 100644 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild @@ -105,11 +105,11 @@ src_install() { doins init.lua rm init.lua exeinto /usr/share/awesome/lib/powerline - doexe powerline/bindings/awesome/powerline.sh + doexe powerline/bindings/awesome/powerline-awesome.py else rm powerline/bindings/awesome/powerline.lua fi - rm powerline/bindings/awesome/powerline.sh + rm powerline/bindings/awesome/powerline-awesome.py # There are no standard location for this, thus using /usr/share/powerline if use tmux ; then elog "" diff --git a/powerline/bindings/awesome/powerline-awesome.py b/powerline/bindings/awesome/powerline-awesome.py new file mode 100755 index 00000000..c0aa7961 --- /dev/null +++ b/powerline/bindings/awesome/powerline-awesome.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet + +from powerline import Powerline +import sys +from time import sleep +from powerline.lib.monotonic import monotonic +from subprocess import Popen, PIPE +from threading import Thread +from select import select + +powerline = Powerline('wm', renderer_module='pango_markup') +powerline.update_renderer() + +try: + interval = float(sys.argv[1]) +except IndexError: + interval = 2 + +def read_to_log(pl, client): + for line in client.stdout: + if line: + pl.info(line, prefix='awesome-client') + for line in client.stderr: + if line: + pl.error(line, prefix='awesome-client') + if client.wait(): + pl.error('Client exited with {0}', client.returncode, prefix='awesome') + +while True: + start_time = monotonic() + s = powerline.render(side='right') + request = "powerline_widget:set_markup('" + s.replace('\\', '\\\\').replace("'", "\\'") + "')\n" + client = Popen(['awesome-client'], shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE) + client.stdin.write(request.encode('utf-8')) + client.stdin.close() + read_to_log(powerline.pl, client) + sleep(max(interval - (monotonic() - start_time), 0.1)) diff --git a/powerline/bindings/awesome/powerline.lua b/powerline/bindings/awesome/powerline.lua index 9424a2fc..659fade4 100644 --- a/powerline/bindings/awesome/powerline.lua +++ b/powerline/bindings/awesome/powerline.lua @@ -7,5 +7,5 @@ powerline_widget:set_align('right') function powerline(mode, widget) end bindings_path = string.gsub(debug.getinfo(1).source:match('@(.*)$'), '/[^/]+$', '') -powerline_cmd = bindings_path .. '/powerline.sh' -awful.util.spawn_with_shell('ps ax -u $USER | grep "' .. powerline_cmd .. '" | grep -v grep || (' .. powerline_cmd .. ')') +powerline_cmd = bindings_path .. '/powerline-awesome.py' +awful.util.spawn_with_shell('ps -C powerline-awesome.py || ' .. powerline_cmd) diff --git a/powerline/bindings/awesome/powerline.sh b/powerline/bindings/awesome/powerline.sh deleted file mode 100755 index d068b958..00000000 --- a/powerline/bindings/awesome/powerline.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -SLEEP=2 -[[ "$1" != "" ]] && SLEEP="$1" - -while true; do - PL_AWESOME_RIGHT=$(powerline wm right -r pango_markup) - echo "powerline_widget:set_markup('$PL_AWESOME_RIGHT')" | awesome-client - sleep $SLEEP -done From 6f57a3dd63788be8980709090ab9f7c8b372b969 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 04:30:51 +0400 Subject: [PATCH 0635/1472] Some fixes for flake8 --- powerline/bindings/awesome/powerline-awesome.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/awesome/powerline-awesome.py b/powerline/bindings/awesome/powerline-awesome.py index c0aa7961..3e4125dc 100755 --- a/powerline/bindings/awesome/powerline-awesome.py +++ b/powerline/bindings/awesome/powerline-awesome.py @@ -6,8 +6,6 @@ import sys from time import sleep from powerline.lib.monotonic import monotonic from subprocess import Popen, PIPE -from threading import Thread -from select import select powerline = Powerline('wm', renderer_module='pango_markup') powerline.update_renderer() @@ -17,6 +15,7 @@ try: except IndexError: interval = 2 + def read_to_log(pl, client): for line in client.stdout: if line: @@ -27,6 +26,7 @@ def read_to_log(pl, client): if client.wait(): pl.error('Client exited with {0}', client.returncode, prefix='awesome') + while True: start_time = monotonic() s = powerline.render(side='right') From 66cdb36231ff1192a8a2e6b15c4b8d524cfbff6d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 04:42:36 +0400 Subject: [PATCH 0636/1472] Use xml.sax.saxutils.escape in place of xmlrpclib.escape The latter is not available in python 3 --- powerline/renderers/pango_markup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index 0f2cb0ac..ea1fe15e 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -3,7 +3,7 @@ from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE -from xmlrpclib import escape as _escape +from xml.sax.saxutils import escape as _escape class PangoMarkupRenderer(Renderer): From 8dcbb031aa00afc35900243142d8f49814834d19 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 13:19:20 +0400 Subject: [PATCH 0637/1472] Make IPython renderer shutdown properly --- powerline/renderers/ipython.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 5695a228..f5c2b24b 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -25,5 +25,11 @@ class IpythonRenderer(ShellRenderer): match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) return match['theme'] + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() + renderer = IpythonRenderer From f3ce370566dedcbf5ba4da11591ddcd5777d0924 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 18:52:49 +0400 Subject: [PATCH 0638/1472] Make cpu_load_percent segment threaded interval=0.5 means that it will block for 0.5 seconds which is bad. With threading it blocks only the separate thread, and it does not hold GIL (uses regular time.sleep to wait) in this case which is fine. interval=0.05 means that it will report almost random value. interval=None means that (assuming psutil.cpu_percent is called only by this segment) it will report CPU load percent measured between two subsequent .cpu_load_percent calls or cpu_load_percent call and module import. It is used for update method to get immediate result in case update_first is True. --- powerline/segments/common.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e50615cc..4f8db199 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -558,16 +558,30 @@ try: def _get_user(segment_info): return psutil.Process(os.getpid()).username - def cpu_load_percent(pl, measure_interval=.5): - '''Return the average CPU load as a percentage. + class CPULoadPercentSegment(ThreadedSegment): + interval = 1 - Requires the ``psutil`` module. + def update(self, old_cpu): + return psutil.cpu_percent(interval=None) - :param float measure_interval: - interval used to measure CPU load (in seconds) - ''' - cpu_percent = int(psutil.cpu_percent(interval=measure_interval)) - return '{0}%'.format(cpu_percent) + def run(self): + while not self.shutdown_event.is_set(): + try: + self.update_value = psutil.cpu_percent(interval=self.interval) + except Exception as e: + self.exception('Exception while calculating cpu_percent: {0}', str(e)) + + def render(self, cpu_percent, format='{0:.0f}%', **kwargs): + return format.format(cpu_percent) + + cpu_load_percent = with_docstring(CPULoadPercentSegment(), + '''Return the average CPU load as a percentage. + + Requires the ``psutil`` module. + + :param str format: + Output format. Accepts measured CPU load as the first argument. + ''') except ImportError: def _get_bytes(interface): # NOQA with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: From 14b31eca357eec3e4f66191901a9d612937d6c18 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 19:01:54 +0400 Subject: [PATCH 0639/1472] Make dummy cpu_load_percent also ThreadedSegment It is the easiest way to make documentation identical when created on a system with and without psutil module and deduplicate docstrings --- powerline/segments/common.py | 44 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 4f8db199..bbc6fb11 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -573,15 +573,6 @@ try: def render(self, cpu_percent, format='{0:.0f}%', **kwargs): return format.format(cpu_percent) - - cpu_load_percent = with_docstring(CPULoadPercentSegment(), - '''Return the average CPU load as a percentage. - - Requires the ``psutil`` module. - - :param str format: - Output format. Accepts measured CPU load as the first argument. - ''') except ImportError: def _get_bytes(interface): # NOQA with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: @@ -599,16 +590,35 @@ except ImportError: def _get_user(segment_info): # NOQA return segment_info['environ'].get('USER', None) - def cpu_load_percent(pl, measure_interval=.5): # NOQA - '''Return the average CPU load as a percentage. + class CPULoadPercentSegment(ThreadedSegment): # NOQA + interval = 1 - Requires the ``psutil`` module. + @staticmethod + def startup(**kwargs): + pass - :param float measure_interval: - interval used to measure CPU load (in seconds) - ''' - pl.warn('psutil package is not installed, thus CPU load is not available') - return None + @staticmethod + def start(): + pass + + @staticmethod + def shutdown(): + pass + + @staticmethod + def render(cpu_percent, pl, format='{0:.0f}%', **kwargs): + pl.warn('psutil package is not installed, thus CPU load is not available') + return None + + +cpu_load_percent = with_docstring(CPULoadPercentSegment(), +'''Return the average CPU load as a percentage. + +Requires the ``psutil`` module. + +:param str format: + Output format. Accepts measured CPU load as the first argument. +''') username = False From 340bdc7a510c7699616e9a66f724a39921710125 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Apr 2013 19:42:16 +0400 Subject: [PATCH 0640/1472] Add other self.pl proxies --- powerline/lib/threaded.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 2b311189..7ceea797 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -116,6 +116,15 @@ class ThreadedSegment(MultiRunnedThread): if not self.is_alive(): self.start() + def critical(self, *args, **kwargs): + self.pl.critical(prefix=self.__class__.__name__, *args, **kwargs) + + def exception(self, *args, **kwargs): + self.pl.exception(prefix=self.__class__.__name__, *args, **kwargs) + + def info(self, *args, **kwargs): + self.pl.info(prefix=self.__class__.__name__, *args, **kwargs) + def error(self, *args, **kwargs): self.pl.error(prefix=self.__class__.__name__, *args, **kwargs) From b2cb5b1e94a0fa5c341f521db6d738688b6c1aa8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 8 Apr 2013 07:49:44 +0400 Subject: [PATCH 0641/1472] Fix gradient level in email_imap_alert segment Closes #407 --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c3e9864d..603ad12f 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -842,7 +842,7 @@ class EmailIMAPSegment(KwThreadedSegment): return [{ 'contents': str(unread_count), 'highlight_group': ['email_alert_gradient', 'email_alert'], - 'gradient_level': unread_count * 100.0 / max_msgs, + 'gradient_level': min(unread_count * 100.0 / max_msgs, 100), }] From 71329cdb5d45cd42b62629385a53dddac3f8520c Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 8 Apr 2013 08:04:22 +0400 Subject: [PATCH 0642/1472] Add gradient for cpu_load_percent Note: no changes to colorschemes: no cpu_load_percent in colorscheme --- powerline/segments/common.py | 10 +++++++++- tests/test_segments.py | 11 ++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index bbc6fb11..a40ec75c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -572,7 +572,13 @@ try: self.exception('Exception while calculating cpu_percent: {0}', str(e)) def render(self, cpu_percent, format='{0:.0f}%', **kwargs): - return format.format(cpu_percent) + if not cpu_percent: + return None + return [{ + 'contents': format.format(cpu_percent), + 'gradient_level': cpu_percent, + 'highlight_group': ['cpu_load_percent_gradient', 'cpu_load_percent'], + }] except ImportError: def _get_bytes(interface): # NOQA with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: @@ -618,6 +624,8 @@ Requires the ``psutil`` module. :param str format: Output format. Accepts measured CPU load as the first argument. + +Highlight groups used: ``cpu_load_percent_gradient`` (gradient) or ``cpu_load_percent``. ''') diff --git a/tests/test_segments.py b/tests/test_segments.py index ca16eb93..18cdc65e 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -244,7 +244,16 @@ class TestCommon(TestCase): def test_cpu_load_percent(self): pl = Pl() with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3): - self.assertEqual(common.cpu_load_percent(pl=pl), '52%') + self.assertEqual(common.cpu_load_percent(pl=pl), [{ + 'contents': '52%', + 'gradient_level': 52.3, + 'highlight_group': ['cpu_load_percent_gradient', 'cpu_load_percent'], + }]) + self.assertEqual(common.cpu_load_percent(pl=pl, format='{0:.1f}%'), [{ + 'contents': '52.3%', + 'gradient_level': 52.3, + 'highlight_group': ['cpu_load_percent_gradient', 'cpu_load_percent'], + }]) def test_network_load(self): from time import sleep From 24dde27b7e09b9a3222ac53d09459ff0b9c2cd83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 8 Apr 2013 08:59:42 +0400 Subject: [PATCH 0643/1472] Alter the priority of branch and line_percent segments Unlike file_type and file_directory branch cannot be deduced from the filename (you normally know directory structure of the project) and contents of the file. Unlike file_format and file_encoding which are normally configured once and then forgotten about knowing branch segment is necessary to separate commits. --- powerline/config_files/themes/vim/default.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 40268a3f..29d6a2ac 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -28,7 +28,7 @@ { "name": "branch", "exclude_modes": ["nc"], - "priority": 60 + "priority": 30 }, { "name": "readonly_indicator", @@ -66,21 +66,21 @@ "name": "file_format", "draw_soft_divider": false, "exclude_modes": ["nc"], - "priority": 50 + "priority": 60 }, { "name": "file_encoding", "exclude_modes": ["nc"], - "priority": 50 + "priority": 60 }, { "name": "file_type", "exclude_modes": ["nc"], - "priority": 50 + "priority": 60 }, { "name": "line_percent", - "priority": 30, + "priority": 50, "width": 4, "align": "r" }, @@ -98,7 +98,7 @@ { "name": "virtcol_current", "draw_soft_divider": false, - "priority": 30, + "priority": 20, "before": ":", "width": 3, "align": "l" From de678ed289a97dc2474e38ad24435f7e5dad6e29 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 9 Apr 2013 00:37:53 +0400 Subject: [PATCH 0644/1472] Create VimPowerline.reset_highlight proxy method Fixes #411 --- powerline/bindings/vim/plugin/powerline.vim | 2 +- powerline/vim.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 1ee4b00d..6ed0f039 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -82,7 +82,7 @@ function! PowerlineRegisterCachePurgerEvent(event) endfunction augroup Powerline - autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()' + autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.reset_highlight()' autocmd! VimEnter * :redrawstatus! autocmd! VimLeavePre * :exec s:powerline_pycmd 'powerline.shutdown()' augroup END diff --git a/powerline/vim.py b/powerline/vim.py index df89b604..b49bd45d 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -82,3 +82,14 @@ class VimPowerline(Powerline): @staticmethod def get_segment_info(): return {} + + def reset_highlight(self): + try: + self.renderer.reset_highlight() + except AttributeError: + # Renderer object appears only after first `.render()` call. Thus if + # ColorScheme event happens before statusline is drawn for the first + # time AttributeError will be thrown for the self.renderer. It is + # fine to ignore it: no renderer == no colors to reset == no need to + # do anything. + pass From 8230f542c6fe17c8a236b4d4b0d343bf3ff2a625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 11 Apr 2013 11:00:21 +0200 Subject: [PATCH 0645/1472] vim: Add quickfix window statusline Closes #419 --- powerline/config_files/config.json | 3 +- .../config_files/themes/vim/quickfix.json | 36 +++++++++++++++++++ powerline/matchers/vim.py | 4 +++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 powerline/config_files/themes/vim/quickfix.json diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index cf9cf771..a70922ff 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -36,7 +36,8 @@ "theme": "default", "local_themes": { "cmdwin": "cmdwin", - "help": "help" + "help": "help", + "quickfix": "quickfix" } }, "wm": { diff --git a/powerline/config_files/themes/vim/quickfix.json b/powerline/config_files/themes/vim/quickfix.json new file mode 100644 index 00000000..00f8a9fd --- /dev/null +++ b/powerline/config_files/themes/vim/quickfix.json @@ -0,0 +1,36 @@ +{ + "segment_data": { + "file_name": { + "contents": "Location List" + } + }, + "segments": { + "left": [ + { + "type": "string", + "name": "file_name", + "draw_soft_divider": false + }, + { + "type": "string", + "highlight_group": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "type": "string", + "name": "line_current_symbol", + "highlight_group": ["line_current_symbol", "line_current"] + }, + { + "name": "line_current", + "draw_soft_divider": false, + "width": 3, + "align": "r" + } + ] + } +} diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index e46b5ef2..9d8eec37 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -13,3 +13,7 @@ def help(matcher_info): def cmdwin(matcher_info): name = matcher_info['buffer'].name return name and os.path.basename(name) == '[Command Line]' + + +def quickfix(matcher_info): + return str(getbufvar(matcher_info['bufnr'], '&buftype')) == 'quickfix' From 7864acb70b5166c03e6731d0d24af8059ad4c307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 11 Apr 2013 11:52:54 +0200 Subject: [PATCH 0646/1472] Update tests --- tests/test_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 81c9b624..668c57b6 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -19,7 +19,7 @@ class TestConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') - buffers = ((('bufoptions',), {'buftype': 'help'}), (('buffer', '[Command Line]'), {})) + buffers = ((('bufoptions',), {'buftype': 'help'}), (('buffer', '[Command Line]'), {}), (('bufoptions',), {'buftype': 'quickfix'})) with open(os.path.join(cfg_path, 'config.json'), 'r') as f: self.assertEqual(len(buffers), len(json.load(f)['ext']['vim']['local_themes'])) outputs = {} From 79fcdc63bde6b21f4376d7496f3dab5bbda0de85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 12 Apr 2013 20:29:07 +0200 Subject: [PATCH 0647/1472] Update Arch Linux optdepends and conflicts --- packages/archlinux/python-powerline-git/PKGBUILD | 8 +++++--- packages/archlinux/python2-powerline-git/PKGBUILD | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 980964fb..8053c5dc 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=python-powerline-git -pkgver=20130313 +pkgver=20130412 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -11,8 +11,10 @@ depends=('python>=3.2') makedepends=('git' 'python-distribute') optdepends=('python-psutil: improved system information' 'python-pygit2: improved git support' - 'zsh: better shell prompt') -conflicts=('powerline-git') + 'zsh: better shell prompt' + 'gvim: vim compiled with Python support') +conflicts=('python2-powerline-git' + 'powerline-git') install='powerline.install' source=() diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 05c0e7aa..e6db1ffb 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Kim Silkebækken pkgname=python2-powerline-git -pkgver=20130313 +pkgver=20130412 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -12,7 +12,9 @@ makedepends=('git' 'python2-distribute') optdepends=('python2-psutil: improved system information' 'python2-pygit2: improved git support' 'mercurial: improved mercurial support' - 'zsh: better shell prompt') + 'zsh: better shell prompt' + 'gvim: vim compiled with Python support') +conflicts=('python-powerline-git') replaces=('powerline-git') install='powerline.install' source=() From d81d6f163f5d5d24136920a1cc2f69803b78b1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 12 Apr 2013 21:19:06 +0200 Subject: [PATCH 0648/1472] vim: Fix missing qf buffer highlighting --- powerline/config_files/themes/vim/quickfix.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/themes/vim/quickfix.json b/powerline/config_files/themes/vim/quickfix.json index 00f8a9fd..da77d631 100644 --- a/powerline/config_files/themes/vim/quickfix.json +++ b/powerline/config_files/themes/vim/quickfix.json @@ -1,6 +1,6 @@ { "segment_data": { - "file_name": { + "buffer_name": { "contents": "Location List" } }, @@ -8,7 +8,8 @@ "left": [ { "type": "string", - "name": "file_name", + "name": "buffer_name", + "highlight_group": ["file_name"], "draw_soft_divider": false }, { From 40973ea5305172dc90a71bf7f5103d32c615a797 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 12 Apr 2013 23:45:42 +0400 Subject: [PATCH 0649/1472] Alter indentation --- powerline/segment.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index 8a2483ff..d91ac028 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -39,17 +39,17 @@ def get_filler(data, segment): segment_getters = { - "function": get_function, - "string": get_string, - "filler": get_filler, - } + "function": get_function, + "string": get_string, + "filler": get_filler, +} def gen_segment_getter(ext, path, theme_configs, default_module=None): data = { - 'default_module': default_module or 'powerline.segments.' + ext, - 'path': path, - } + 'default_module': default_module or 'powerline.segments.' + ext, + 'path': path, + } def get_key(segment, module, key, default=None): return get_segment_key(segment, theme_configs, key, module, default) @@ -89,6 +89,6 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): '_len': 0, '_space_left': 0, '_space_right': 0, - } + } return get From 3ce068db9470e007825f63ea27f70a12b41cba13 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 20:18:03 +0400 Subject: [PATCH 0650/1472] Make vim and ipython ext configurations optional --- powerline/lint/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index e5733a67..d151bbd3 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -427,7 +427,7 @@ main_spec = (Spec( theme=theme_spec(), local_themes=Spec() .unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec()) - ), + ).optional(), ipython=Spec( colorscheme=colorscheme_spec(), theme=theme_spec(), @@ -436,7 +436,7 @@ main_spec = (Spec( out=theme_spec(), rewrite=theme_spec(), ), - ), + ).optional(), ).unknown_spec(check_ext, Spec( colorscheme=colorscheme_spec(), From ac88a0943613aa9afc61fd0f6acc5ddd87918f6a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 14:03:45 +0400 Subject: [PATCH 0651/1472] Add renderer docstrings. --- powerline/renderer.py | 129 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 9 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 206be29e..26b18f75 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -19,11 +19,51 @@ def construct_returned_value(rendered_highlighted, segments, output_raw): class Renderer(object): + '''Object that is responsible for generating the highlighted string. + + :param dict theme_config: + Main theme configuration. + :param local_themes: + Local themes. Is to be used by subclasses from ``.get_theme()`` method, + base class only records this parameter to a ``.local_themes`` attribute. + :param dict theme_kwargs: + Keyword arguments for ``Theme`` class constructor. + :param Colorscheme colorscheme: + Colorscheme object that holds colors configuration. + :param PowerlineState pl: + Object used for logging. + :param int ambiwidth: + Width of the characters with east asian width unicode attribute equal to + ``A`` (Ambigious). + :param dict options: + Various options. Are normally not used by base renderer, but all options + are recorded as attributes. + ''' + segment_info = { 'environ': os.environ, 'getcwd': getattr(os, 'getcwdu', os.getcwd), 'home': os.environ.get('HOME'), } + '''Basic segment info. Is merged with local segment information by + ``.get_segment_info()`` method. Keys: + + ``environ`` + Object containing environment variables. Must define at least the + following methods: ``.__getitem__(var)`` that raises ``KeyError`` in + case requested environment variable is not present, ``.get(var, + default=None)`` that works like ``dict.get`` and be able to be passed to + ``Popen``. + + ``getcwd`` + Function that returns current working directory. Will be called without + any arguments, should return ``unicode`` or (in python-2) regular + string. + + ``home`` + String containing path to home directory. Should be ``unicode`` or (in + python-2) regular string or ``None``. + ''' def __init__(self, theme_config, @@ -31,6 +71,7 @@ class Renderer(object): theme_kwargs, colorscheme, pl, + ambiwidth=1, **options): self.__dict__.update(options) self.theme_config = theme_config @@ -41,24 +82,48 @@ class Renderer(object): self.theme_kwargs = theme_kwargs self.colorscheme = colorscheme self.width_data = { - 'N': 1, # Neutral - 'Na': 1, # Narrow - 'A': getattr(self, 'ambiwidth', 1), # Ambigious - 'H': 1, # Half-width - 'W': 2, # Wide - 'F': 2, # Fullwidth - } + 'N': 1, # Neutral + 'Na': 1, # Narrow + 'A': ambiwidth, # Ambigious + 'H': 1, # Half-width + 'W': 2, # Wide + 'F': 2, # Fullwidth + } def strwidth(self, string): + '''Function that returns string width. + + Is used to calculate the place given string occupies when handling + ``width`` argument to ``.render()`` method. Must take east asian width + into account. + + :param unicode string: + String whose width will be calculated. + + :return: unsigned integer. + ''' return sum((0 if combining(symbol) else self.width_data[east_asian_width(symbol)] for symbol in string)) def get_theme(self, matcher_info): + '''Get Theme object. + + Is to be overridden by subclasses to support local themes, this variant + only returns ``.theme`` attribute. + + :param matcher_info: + Parameter ``matcher_info`` that ``.render()`` method received. + Unused. + ''' return self.theme def shutdown(self): + '''Prepare for interpreter shutdown. The only job it is supposed to do + is calling ``.shutdown()`` method for all theme objects. Should be + overridden by subclasses in case they support local themes. + ''' self.theme.shutdown() - def get_highlighting(self, segment, mode): + def _get_highlighting(self, segment, mode): segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) if segment['divider_highlight_group']: segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode) @@ -67,6 +132,21 @@ class Renderer(object): return segment def get_segment_info(self, segment_info): + '''Get segment information. + + Must return a dictionary containing at least ``home``, ``environ`` and + ``getcwd`` keys (see documentation for ``segment_info`` attribute). This + implementation merges ``segment_info`` dictionary passed to + ``.render()`` method with ``.segment_info`` attribute, preferring keys + from the former. It also replaces ``getcwd`` key with function returning + ``segment_info['environ']['PWD']`` in case ``PWD`` variable is + available. + + :param dict segment_info: + Segment information that was passed to ``.render()`` method. + + :return: dict with segment information. + ''' r = self.segment_info.copy() if segment_info: r.update(segment_info) @@ -82,12 +162,30 @@ class Renderer(object): 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. + + :param str mode: + Mode string. Affects contents (colors and the set of segments) of + rendered string. + :param int width: + Maximum width text can occupy. May be exceeded if there are too much + non-removable segments. + :param str side: + One of ``left``, ``right``. Determines which side will be rendered. + If not present all sides are rendered. + :param bool output_raw: + Changes the output: if this parameter is ``True`` then in place of + one string this method outputs a pair ``(colored_string, + colorless_string)``. + :param dict segment_info: + Segment information. See also ``.get_segment_info()`` method. + :param matcher_info: + Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) segments = theme.get_segments(side, self.get_segment_info(segment_info)) # Handle excluded/included segments for the current mode - segments = [self.get_highlighting(segment, mode) for segment in segments + segments = [self._get_highlighting(segment, mode) for segment in segments if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] segments = [segment for segment in self._render_segments(theme, segments)] @@ -199,10 +297,23 @@ class Renderer(object): @staticmethod def escape(string): + '''Method that escapes segment contents. + ''' return string def hlstyle(fg=None, bg=None, attr=None): + '''Output highlight style string. + + Assuming highlighted string looks like ``{style}{contents}`` this method + should output ``{style}``. If it is called without arguments this method + is supposed to reset style to its default. + ''' raise NotImplementedError def hl(self, contents, fg=None, bg=None, attr=None): + '''Output highlighted chunk. + + This implementation just outputs ``.hlstyle()`` joined with + ``contents``. + ''' return self.hlstyle(fg, bg, attr) + (contents or '') From 9250d794d7bd59952ac4b182959a098b1a9e9d4d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 14:32:20 +0400 Subject: [PATCH 0652/1472] Remove `pl` argument and first argument to `render*` from docs --- docs/source/powerline_autodoc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 38f0a4cb..613f9a85 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -44,7 +44,8 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): if (arg == 'self' or (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)) or - (method == 'render_one' and -i == len(argspec.args)) or + (arg == 'pl') or + (method.startswith('render') and 1-i == len(argspec.args)) or arg in args): continue if argspec.defaults and len(argspec.defaults) >= -i: @@ -64,7 +65,8 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): args = [] defaults = [] for i, arg in zip(count(-1, -1), reversed(argspec.args)): - if (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)): + if ((arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)) or + arg == 'pl'): continue if argspec.defaults and len(argspec.defaults) >= -i: default = argspec.defaults[i] From 587789ebda7be5fb09b798ac016d9f444ca0331a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Apr 2013 19:09:03 +0400 Subject: [PATCH 0653/1472] =?UTF-8?q?Fix=20documentation=20for=20ThreadedS?= =?UTF-8?q?egment=20classes=20with=20@staticmethod=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/powerline_autodoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 613f9a85..87412cfa 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -45,7 +45,7 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)) or (arg == 'pl') or - (method.startswith('render') and 1-i == len(argspec.args)) or + (method.startswith('render') and (1 if argspec.args[0] == 'self' else 0) - i == len(argspec.args)) or arg in args): continue if argspec.defaults and len(argspec.defaults) >= -i: From 49618fc4e34e303a3980bb4c111896a3bc7c07b1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 8 Apr 2013 22:59:14 +0400 Subject: [PATCH 0654/1472] Rename PowerlineState to PowerlineLogger --- powerline/__init__.py | 4 ++-- powerline/renderer.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index ebc50ac7..fdbc33f3 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -23,7 +23,7 @@ def find_config_file(search_paths, config_file): raise IOError('Config file not found in search path: {0}'.format(config_file)) -class PowerlineState(object): +class PowerlineLogger(object): def __init__(self, use_daemon_threads, logger, ext): self.logger = logger self.ext = ext @@ -175,7 +175,7 @@ class Powerline(object): self.logger.addHandler(handler) if not self.pl: - self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext) + self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext) if not self.config_loader.pl: self.config_loader.pl = self.pl diff --git a/powerline/renderer.py b/powerline/renderer.py index 26b18f75..81ae00ab 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -30,7 +30,7 @@ class Renderer(object): Keyword arguments for ``Theme`` class constructor. :param Colorscheme colorscheme: Colorscheme object that holds colors configuration. - :param PowerlineState pl: + :param PowerlineLogger pl: Object used for logging. :param int ambiwidth: Width of the characters with east asian width unicode attribute equal to From a8eb0a247168c00517dbaccde11656f8a6292489 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 9 Apr 2013 08:18:37 +0400 Subject: [PATCH 0655/1472] Special-case None priority in place of -1 This extends priorities in both directions, uses slightly faster `is None` check, makes it consistent with `interval` special-casing also to `None` and makes lint able to use one simple `.type()` check in place of `.either()` one. --- docs/source/configuration.rst | 11 +++++------ powerline/lint/__init__.py | 2 +- powerline/renderer.py | 2 +- powerline/segment.py | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 949c77c8..40adca33 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -377,14 +377,13 @@ Themes can be aligned with the ``align`` property. ``priority`` - Optional segment priority. Segments with priority ``-1`` (the - default priority) will always be included, regardless of the width - of the prompt/statusline. + Optional segment priority. Segments with priority ``None`` (the default + priority, represented by ``null`` in json) will always be included, + regardless of the width of the prompt/statusline. - If the priority is ``0`` or more, the segment may be removed if the + If the priority is any number, the segment may be removed if the prompt/statusline width is too small for all the segments to be - rendered. A lower number means that the segment has a higher - priority. + rendered. A lower number means that the segment has a higher priority. Segments are removed according to their priority, with low priority segments being removed first. diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index d151bbd3..85695caf 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -818,7 +818,7 @@ segments_spec = Spec().optional().list( draw_soft_divider=Spec().type(bool).optional(), draw_inner_divider=Spec().type(bool).optional(), module=segment_module_spec(), - priority=Spec().either(Spec().cmp('eq', -1), Spec().cmp('ge', 0.0)).optional(), + priority=Spec().type(int, float, type(None)).optional(), after=Spec().type(unicode).optional(), before=Spec().type(unicode).optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), diff --git a/powerline/renderer.py b/powerline/renderer.py index 81ae00ab..948d553c 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -195,7 +195,7 @@ class Renderer(object): return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped - segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] + segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] is not None] while sum([segment['_len'] for segment in segments]) > width and len(segments_priority): segments.remove(segments_priority[0]) segments_priority.pop(0) diff --git a/powerline/segment.py b/powerline/segment.py index d91ac028..9632206a 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -73,7 +73,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): 'contents_func': contents_func, 'contents': contents, 'args': get_key(segment, module, 'args', {}) if segment_type == 'function' else {}, - 'priority': segment.get('priority', -1), + 'priority': segment.get('priority', None), 'draw_hard_divider': segment.get('draw_hard_divider', True), 'draw_soft_divider': segment.get('draw_soft_divider', True), 'draw_inner_divider': segment.get('draw_inner_divider', False), From c7946cda3e3f3156aefd1e9f3a5c6bb6db7fbdb0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 9 Apr 2013 08:38:04 +0400 Subject: [PATCH 0656/1472] Use different default for functions This makes it look up `powerline.segments.vim.virtualenv` in addition to virtualenv in colorscheme like it does with `segment_data`. --- powerline/segment.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index 9632206a..497242e2 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -61,8 +61,18 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None): get_segment_info = segment_getters[segment_type] except KeyError: raise TypeError('Unknown segment type: {0}'.format(segment_type)) - contents, contents_func, module = get_segment_info(data, segment) - highlight_group = segment_type != 'function' and segment.get('highlight_group') or segment.get('name') + + try: + contents, contents_func, module = get_segment_info(data, segment) + except Exception as e: + pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') + return None + + if segment_type == 'function': + highlight_group = [module + '.' + segment['name'], segment['name']] + else: + highlight_group = segment.get('highlight_group') or segment.get('name') + return { 'name': segment.get('name'), 'type': segment_type, From 4dd9c5196e1f4898cd706e2219e3a9fa7eef6af9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 12 Apr 2013 23:38:44 +0400 Subject: [PATCH 0657/1472] Respect segment.py highlight group handling in powerline-lint Either `highlight_group` or `name` key is enough to determine highlight group for non-function segments, but powerline-lint requires `highlight_group`. --- powerline/lint/__init__.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 85695caf..6f0183fe 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -527,10 +527,11 @@ type_keys = { } required_keys = { 'function': set(), - 'string': set(('contents', 'highlight_group')), - 'filler': set(('highlight_group',)), + 'string': set(('contents',)), + 'filler': set(), } function_keys = set(('args', 'module')) +highlight_keys = set(('highlight_group', 'name')) def check_key_compatibility(segment, data, context, echoerr): @@ -542,6 +543,8 @@ def check_key_compatibility(segment, data, context, echoerr): problem_mark=segment_type.mark) return False, False, True + hadproblem = False + keys = set(segment) if not ((keys - generic_keys) < type_keys[segment_type]): unknown_keys = keys - generic_keys - type_keys[segment_type] @@ -550,7 +553,7 @@ def check_key_compatibility(segment, data, context, echoerr): problem='found keys not used with the current segment type: {0}'.format( ', '.join((unicode(key) for key in unknown_keys))), problem_mark=list(unknown_keys)[0].mark) - return True, False, True + hadproblem = True if not (keys > required_keys[segment_type]): missing_keys = required_keys[segment_type] - keys @@ -558,9 +561,15 @@ def check_key_compatibility(segment, data, context, echoerr): context_mark=context[-1][1].mark, problem='found missing required keys: {0}'.format( ', '.join((unicode(key) for key in missing_keys)))) - return True, False, True + hadproblem = True - return True, False, False + if not (segment_type == 'function' or (keys & highlight_keys)): + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=context[-1][1].mark, + problem='found missing keys required to determine highlight group. Either highlight_group or name key must be present') + hadproblem = True + + return True, False, hadproblem def check_segment_module(module, data, context, echoerr): From 7e57010c1952e66e56bd6782b33baffd281091e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 00:09:00 +0400 Subject: [PATCH 0658/1472] Move some code from powerline_autodoc to powerline.lib.inspect To make it available later in lint checker. --- docs/source/powerline_autodoc.py | 60 +++----------------------------- powerline/lint/inspect.py | 59 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 55 deletions(-) create mode 100644 powerline/lint/inspect.py diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 87412cfa..4806c3cf 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -1,8 +1,8 @@ +# vim:fileencoding=utf-8:noet from sphinx.ext import autodoc -from sphinx.util.inspect import getargspec -from inspect import ArgSpec, formatargspec -from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment -from itertools import count +from inspect import formatargspec +from powerline.lint.inspect import getconfigargspec +from powerline.lib.threaded import ThreadedSegment try: from __builtin__ import unicode @@ -25,57 +25,7 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): super(ThreadedDocumenter, cls).can_document_member(member, membername, isattr, parent)) def format_args(self): - if isinstance(self.object, ThreadedSegment): - args = ['interval'] - defaults = [getattr(self.object, 'interval', 1)] - if self.object.update_first: - args.append('update_first') - defaults.append(True) - methods = ['render', 'set_state'] - if isinstance(self.object, KwThreadedSegment): - methods += ['key', 'render_one'] - - for method in methods: - if hasattr(self.object, method): - # Note: on = -i: - default = argspec.defaults[i] - defaults.append(default) - args.append(arg) - else: - args.insert(0, arg) - argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) - else: - if hasattr(self.object, 'powerline_origin'): - obj = self.object.powerline_origin - else: - obj = self.object - - argspec = getargspec(obj) - args = [] - defaults = [] - for i, arg in zip(count(-1, -1), reversed(argspec.args)): - if ((arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)) or - arg == 'pl'): - continue - if argspec.defaults and len(argspec.defaults) >= -i: - default = argspec.defaults[i] - defaults.append(default) - args.append(arg) - else: - args.insert(0, arg) - argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults)) - + argspec = getconfigargspec(self.object) return formatargspec(*argspec, formatvalue=formatvalue).replace('\\', '\\\\') diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py new file mode 100644 index 00000000..6c2c0fab --- /dev/null +++ b/powerline/lint/inspect.py @@ -0,0 +1,59 @@ +# vim:fileencoding=utf-8:noet +from __future__ import absolute_import +from inspect import ArgSpec, getargspec +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from itertools import count + +def getconfigargspec(obj): + if isinstance(obj, ThreadedSegment): + args = ['interval'] + defaults = [getattr(obj, 'interval', 1)] + if obj.update_first: + args.append('update_first') + defaults.append(True) + methods = ['render', 'set_state'] + if isinstance(obj, KwThreadedSegment): + methods += ['key', 'render_one'] + + for method in methods: + if hasattr(obj, method): + # Note: on = -i: + default = argspec.defaults[i] + defaults.append(default) + args.append(arg) + else: + args.insert(0, arg) + argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) + else: + if hasattr(obj, 'powerline_origin'): + obj = obj.powerline_origin + else: + obj = obj + + argspec = getargspec(obj) + args = [] + defaults = [] + for i, arg in zip(count(-1, -1), reversed(argspec.args)): + if ((arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) or + arg == 'pl'): + continue + if argspec.defaults and len(argspec.defaults) >= -i: + default = argspec.defaults[i] + defaults.append(default) + args.append(arg) + else: + args.insert(0, arg) + argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults)) + + return argspec From fee328666ff613ef10a3ec8896babeef2d59e04a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 14:20:54 +0400 Subject: [PATCH 0659/1472] Improve arguments checks --- powerline/lint/__init__.py | 201 +++++++++++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 42 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6f0183fe..d8609ba1 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -3,6 +3,9 @@ from powerline import find_config_file, Powerline from powerline.lib.config import load_json_config from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes +from powerline.lint.inspect import getconfigargspec +from powerline.lint.markedjson.markedvalue import gen_marked_value +from powerline.lib.threaded import ThreadedSegment import itertools import sys import os @@ -25,8 +28,33 @@ def open_file(path): EMPTYTUPLE = tuple() +class JStr(unicode): + def join(self, iterable): + return super(JStr, self).join((unicode(item) for item in iterable)) + + +key_sep = JStr('/') +list_sep = JStr(', ') + + def context_key(context): - return '/'.join((unicode(c[0]) for c in context)) + return key_sep.join((c[0] for c in context)) + + +class DelayedEchoErr(object): + def __init__(self, echoerr): + self.echoerr = echoerr + self.errs = [] + + def __call__(self, *args, **kwargs): + self.errs.append((args, kwargs)) + + def echo_all(self): + for args, kwargs in self.errs: + self.echoerr(*args, **kwargs) + + def __nonzero__(self): + return not not self.errs class Spec(object): @@ -80,7 +108,7 @@ class Spec(object): context_mark=context_mark, problem='{0!r} must be a {1} instance, not {2}'.format( value, - ', '.join((t.__name__ for t in types)), + list_sep.join((t.__name__ for t in types)), type(value.value).__name__ ), problem_mark=value.mark) @@ -118,10 +146,7 @@ class Spec(object): return True, hadproblem def check_either(self, value, context_mark, data, context, echoerr, start, end): - errs = [] - - def new_echoerr(*args, **kwargs): - errs.append((args, kwargs)) + new_echoerr = DelayedEchoErr(echoerr) hadproblem = False for spec in self.specs[start:end]: @@ -131,8 +156,7 @@ class Spec(object): if not hadproblem: return True, False - for args, kwargs in errs: - echoerr(*args, **kwargs) + new_echoerr.echo_all() return False, hadproblem @@ -392,6 +416,7 @@ def check_config(d, theme, data, context, echoerr): return True, False, True return True, False, False + divider_spec = Spec().type(unicode).len('le', 3, lambda value: 'Divider {0!r} is too large!'.format(value)).copy divside_spec = Spec( @@ -551,7 +576,7 @@ def check_key_compatibility(segment, data, context, echoerr): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), context_mark=context[-1][1].mark, problem='found keys not used with the current segment type: {0}'.format( - ', '.join((unicode(key) for key in unknown_keys))), + list_sep.join(unknown_keys)), problem_mark=list(unknown_keys)[0].mark) hadproblem = True @@ -560,7 +585,7 @@ def check_key_compatibility(segment, data, context, echoerr): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), context_mark=context[-1][1].mark, problem='found missing required keys: {0}'.format( - ', '.join((unicode(key) for key in missing_keys)))) + list_sep.join(missing_keys))) hadproblem = True if not (segment_type == 'function' or (keys & highlight_keys)): @@ -619,28 +644,39 @@ def check_full_segment_data(segment, data, context, echoerr): return check_key_compatibility(segment_copy, data, context, echoerr) +def import_segment(name, data, context, echoerr, module=None): + if not module: + module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext'])) + + with WithPath(data['import_paths']): + try: + func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name)) + except ImportError: + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='failed to import module {0}'.format(module), + problem_mark=module.mark) + return None + except AttributeError: + echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), + problem='failed to load function {0} from module {1}'.format(name, module), + problem_mark=name.mark) + return None + + if not callable(func): + echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + problem='imported "function" {0} from module {1} is not callable'.format(name, module), + problem_mark=module.mark) + return None + + return func + + def check_segment_name(name, data, context, echoerr): ext = data['ext'] if context[-2][1].get('type', 'function') == 'function': - module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.' + ext)) - with WithPath(data['import_paths']): - try: - func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name)) - except ImportError: - echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='failed to import module {0}'.format(module), - problem_mark=module.mark) - return True, False, True - except AttributeError: - echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), - problem='failed to load function {0} from module {1}'.format(name, module), - problem_mark=name.mark) - return True, False, True + func = import_segment(name, data, context, echoerr) - if not callable(func): - echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='imported "function" {0} from module {1} is not callable'.format(name, module), - problem_mark=module.mark) + if not func: return True, False, True hl_groups = [] @@ -662,32 +698,32 @@ def check_segment_name(name, data, context, echoerr): if r: echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight group {0} not defined in the following colorschemes: {1}\n(Group name was obtained from function documentation.)'.format( - divider_hl_group, ', '.join(r)), + divider_hl_group, list_sep.join(r)), problem_mark=name.mark) hadproblem = True if hl_groups: greg = re.compile(r'``([^`]+)``( \(gradient\))?') - hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (', '.join(hl_groups)).split(', ')] + hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (list_sep.join(hl_groups)).split(', ')] for required_pack in hl_groups: rs = [hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) for hl_group, gradient in required_pack] if all(rs): echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight groups list ({0}) with all groups not defined in some colorschemes\n(Group names were taken from function documentation.)'.format( - ', '.join((unicode(h[0]) for h in required_pack))), + list_sep.join((h[0] for h in required_pack))), problem_mark=name.mark) for r, h in zip(rs, required_pack): echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( - h[0], ', '.join(r))) + h[0], list_sep.join(r))) hadproblem = True else: r = hl_exists(name, data, context, echoerr, allow_gradients=True) if r: echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight group {0} not defined in the following colorschemes: {1}\n(If not specified otherwise in documentation, highlight group for function segments\nis the same as the function name.)'.format( - name, ', '.join(r)), + name, list_sep.join(r)), problem_mark=name.mark) hadproblem = True @@ -753,7 +789,7 @@ def check_highlight_group(hl_group, data, context, echoerr): if r: echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( - hl_group, ', '.join(r)), + hl_group, list_sep.join(r)), problem_mark=hl_group.mark) return True, False, True return True, False, False @@ -764,12 +800,12 @@ def check_highlight_groups(hl_groups, data, context, echoerr): if all(rs): echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format( - ', '.join((unicode(h) for h in hl_groups))), + list_sep.join((unicode(h) for h in hl_groups))), problem_mark=hl_groups.mark) for r, hl_group in zip(rs, hl_groups): echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( - hl_group, ', '.join(r)), + hl_group, list_sep.join(r)), problem_mark=hl_group.mark) return True, False, True return True, False, False @@ -807,11 +843,92 @@ def check_segment_data_key(key, data, context, echoerr): return True, False, False -# FIXME More checks, limit existing to ThreadedSegment instances only +threaded_args_specs = { + 'interval': Spec().cmp('gt', 0.0), + 'update_first': Spec().type(bool), + 'shutdown_event': Spec().error('Shutdown event must be set by powerline'), +} + + +def check_args_variant(segment, args, data, context, echoerr): + argspec = getconfigargspec(segment) + present_args = set(args) + all_args = set(argspec.args) + required_args = set(argspec.args[:-len(argspec.defaults)]) + + hadproblem = False + + if required_args - present_args: + echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), + context_mark=args.mark, + problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args))) + hadproblem = True + + if not all_args >= present_args: + echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), + context_mark=args.mark, + problem='found unknown keys: {0}'.format(list_sep.join(present_args - all_args)), + problem_mark=next(iter(present_args - all_args)).mark) + hadproblem = True + + if isinstance(segment, ThreadedSegment): + for key in set(threaded_args_specs) & present_args: + proceed, khadproblem = threaded_args_specs[key].match(args[key], args.mark, data, context + ((key, args[key]),), echoerr) + if khadproblem: + hadproblem = True + if not proceed: + return hadproblem + + return hadproblem + + +def check_args(get_segment_variants, args, data, context, echoerr): + new_echoerr = DelayedEchoErr(echoerr) + count = 0 + hadproblem = False + for segment in get_segment_variants(data, context, new_echoerr): + count += 1 + shadproblem = check_args_variant(segment, args, data, context, echoerr) + if shadproblem: + hadproblem = True + + if not count: + hadproblem = True + new_echoerr.echo_all() + echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), + context_mark=context[-2][1].mark, + problem='no suitable segments found') + + return True, False, hadproblem + + +def get_one_segment_variant(data, context, echoerr): + name = context[-2][1].get('name') + if name: + func = import_segment(name, data, context, echoerr) + if func: + yield func + + +def get_all_possible_segments(data, context, echoerr): + name = context[-2][0] + module, name = (gen_marked_value(value, name.mark) for value in name.rpartition('.')[::2]) + if module: + func = import_segment(name, data, context, echoerr, module=module) + if func: + yield func + else: + for theme_config in data['ext_theme_configs'].values(): + for segments in theme_config.get('segments', {}).values(): + for segment in segments: + if segment.get('type', 'function') == 'function': + module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext'])) + func = import_segment(name, data, context, echoerr, module=module) + if func: + yield func + + args_spec = Spec( - interval=Spec().cmp('gt', 0.0).optional(), - update_first=Spec().type(bool).optional(), - shutdown_event=Spec().error('Shutdown event must be set by powerline').optional(), pl=Spec().error('pl object must be set by powerline').optional(), segment_info=Spec().error('Segment info dictionary must be set by powerline').optional(), ).unknown_spec(Spec(), Spec()).optional().copy @@ -832,7 +949,7 @@ segments_spec = Spec().optional().list( before=Spec().type(unicode).optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), align=Spec().oneof(set('lr')).optional(), - args=args_spec(), + args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)), contents=Spec().type(unicode).optional(), highlight_group=Spec().list( highlight_group_spec().re('^(?:(?!:divider$).)+$', @@ -849,7 +966,7 @@ theme_spec = (Spec( Spec( after=Spec().type(unicode).optional(), before=Spec().type(unicode).optional(), - args=args_spec(), + args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_segments, *args, **kwargs)), contents=Spec().type(unicode).optional(), ), ).optional().context_message('Error while loading segment data (key {key})'), From ae691b7cd858bebd4609ad78c5ceaecb2275516f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 14:47:39 +0400 Subject: [PATCH 0660/1472] Improve shown errors --- powerline/lint/__init__.py | 12 +++-- powerline/lint/markedjson/error.py | 3 ++ powerline/lint/markedjson/markedvalue.py | 58 +++++++++++++++++++++++- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index d8609ba1..726f255d 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -894,10 +894,12 @@ def check_args(get_segment_variants, args, data, context, echoerr): if not count: hadproblem = True - new_echoerr.echo_all() - echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), - context_mark=context[-2][1].mark, - problem='no suitable segments found') + if new_echoerr: + new_echoerr.echo_all() + else: + echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), + context_mark=context[-2][1].mark, + problem='no suitable segments found') return True, False, hadproblem @@ -912,7 +914,7 @@ def get_one_segment_variant(data, context, echoerr): def get_all_possible_segments(data, context, echoerr): name = context[-2][0] - module, name = (gen_marked_value(value, name.mark) for value in name.rpartition('.')[::2]) + module, name = name.rpartition('.')[::2] if module: func = import_segment(name, data, context, echoerr, module=module) if func: diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index 66fcacef..d1466675 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -29,6 +29,9 @@ class Mark: self.buffer = buffer self.pointer = pointer + def copy(self): + return Mark(self.name, self.line, self.column, self.buffer, self.pointer) + def get_snippet(self, indent=4, max_length=75): if self.buffer is None: return None diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index db45fd13..6a304b9f 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -1,17 +1,71 @@ __all__ = ['gen_marked_value', 'MarkedValue'] +try: + from __builtin__ import unicode +except ImportError: + unicode = str + + +def gen_new(cls): + def __new__(arg_cls, value, mark): + r = super(arg_cls, arg_cls).__new__(arg_cls, value) + r.mark = mark + r.value = value + return r + return __new__ + + +class MarkedUnicode(unicode): + __new__ = gen_new(unicode) + + def _proc_partition(self, part_result): + pointdiff = 1 + r = [] + for s in part_result: + mark = self.mark.copy() + # XXX Does not work properly with escaped strings, but this requires + # saving much more information in mark. + mark.column += pointdiff + mark.pointer += pointdiff + r.append(MarkedUnicode(s, mark)) + pointdiff += len(s) + return tuple(r) + + def rpartition(self, sep): + return self._proc_partition(super(MarkedUnicode, self).rpartition(sep)) + + def partition(self, sep): + return self._proc_partition(super(MarkedUnicode, self).partition(sep)) + + +class MarkedInt(int): + __new__ = gen_new(int) + + +class MarkedFloat(float): + __new__ = gen_new(float) + + class MarkedValue: def __init__(self, value, mark): self.mark = mark self.value = value +specialclasses = { + unicode: MarkedUnicode, + int: MarkedInt, + float: MarkedFloat, +} + classcache = {} -def gen_marked_value(value, mark): - if value.__class__ in classcache: +def gen_marked_value(value, mark, use_special_classes=True): + if use_special_classes and value.__class__ in specialclasses: + Marked = specialclasses[value.__class__] + elif value.__class__ in classcache: Marked = classcache[value.__class__] else: class Marked(MarkedValue): From 5e93d20fb84313fbeffed48822bf63173c2556ca Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 15:05:39 +0400 Subject: [PATCH 0661/1472] Do not sort segments that are always included --- powerline/renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 948d553c..848db1ea 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -195,7 +195,7 @@ class Renderer(object): return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped - segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] is not None] + segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) while sum([segment['_len'] for segment in segments]) > width and len(segments_priority): segments.remove(segments_priority[0]) segments_priority.pop(0) From 4449489e5944c1736adf07cf4dbf7dec37069549 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 15:08:27 +0400 Subject: [PATCH 0662/1472] Do not use second argument to itertools.count --- powerline/lint/inspect.py | 8 ++++---- powerline/segments/vim.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 6c2c0fab..58c83a2c 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -20,16 +20,16 @@ def getconfigargspec(obj): # Note: on = -i: - default = argspec.defaults[i] + if argspec.defaults and len(argspec.defaults) >= i: + default = argspec.defaults[-i] defaults.append(default) args.append(arg) else: diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index cb05b74d..a6fcc203 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -274,6 +274,7 @@ def col_current(pl, segment_info): return str(segment_info['window'].cursor[1] + 1) +# TODO Add &textwidth-based gradient @window_cached def virtcol_current(pl): '''Return current visual column with concealed characters ingored From 25627363b04d0493b10a349a7aba1c0b0c60ee59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 15:16:45 +0400 Subject: [PATCH 0663/1472] Do not use second argument to itertools.count in other place --- powerline/lint/inspect.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 58c83a2c..b0f0c7af 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -44,12 +44,12 @@ def getconfigargspec(obj): argspec = getargspec(obj) args = [] defaults = [] - for i, arg in zip(count(-1, -1), reversed(argspec.args)): + for i, arg in zip(count(1), reversed(argspec.args)): if ((arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) or arg == 'pl'): continue - if argspec.defaults and len(argspec.defaults) >= -i: - default = argspec.defaults[i] + if argspec.defaults and len(argspec.defaults) >= i: + default = argspec.defaults[-i] defaults.append(default) args.append(arg) else: From a4c59ded76d80fd99ad66e864cd60212a2c02996 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 15:44:08 +0400 Subject: [PATCH 0664/1472] Make tests more verbose and catch first KeyboardInterrupt --- tests/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.sh b/tests/test.sh index a8439ac7..c65e6668 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -3,7 +3,7 @@ FAILED=0 export PYTHONPATH="${PYTHONPATH}:`realpath .`" for file in tests/test_*.py ; do - if ! ${PYTHON} $file ; then + if ! ${PYTHON} $file --verbose --catch ; then FAILED=1 fi done From 9e518ab682c1a36f60f9b326b3f6a4899c6657fe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 18:48:07 +0400 Subject: [PATCH 0665/1472] Improve generate_gradients tool Changes: - Port argument parsing to argparse - Add weights support - Add ability to specify cterm palette - Purge out first 16 colors from color choosing by default - Improve different scales and different preview length support --- tools/generate_gradients.py | 154 +++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 56 deletions(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index e4d3d058..6bfe2cce 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -1,8 +1,13 @@ #!/usr/bin/env python +# vim:fileencoding=utf-8:noet +'''Gradients generator +''' +from __future__ import division import sys import json from powerline.colorscheme import cterm_to_hex from itertools import groupby +import argparse try: from __builtin__ import unicode @@ -10,27 +15,38 @@ except ImportError: unicode = str # NOQA -if len(sys.argv) == 1 or sys.argv[1] == '--help': - sys.stderr.write(''' - Usage: generate_gradients.py colors itemnum[ "show" [ min max num]] +def num2(s): + try: + return (True, [int(v) for v in s.partition(' ')[::2]]) + except TypeError: + return (False, [float(v) for v in s.partition(' ')[::2]]) - colors: JSON list with either cterm ([200, 42, 6]) or RGB (["abcdef", - "feffef"]) colors. - itemnum: number of items in generated gradient. +def rgbint_to_rgb(rgbint): + return ((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF) - "show": static string, determines whether gradient sample should be - printed to stdout as well. - min, max: floating point values. - num: integer +def color(s): + if len(s) <= 3: + return rgbint_to_rgb(cterm_to_hex[int(s)]) + else: + return rgbint_to_rgb(int(s, 16)) - all of the above are used to generate sample gradient for given - range (just like the above gradients shown with "show", but with - different scale (controlled by min and max) and, possibly, - different length (controlled by num)). - ''') - sys.exit(1) + +def nums(s): + return [int(i) for i in s.split()] + + +p = argparse.ArgumentParser(description=__doc__) +p.add_argument('gradient', nargs='*', metavar='COLOR', type=color, help='List of colors (either indexes from 8-bit palette or 24-bit RGB in hexadecimal notation)') +p.add_argument('-n', '--num_items', metavar='INT', type=int, help='Number of items in resulting list', default=101) +p.add_argument('-N', '--num_output', metavar='INT', type=int, help='Number of characters in sample', default=101) +p.add_argument('-r', '--range', metavar='V1 V2', type=num2, help='Use this range when outputting scale') +p.add_argument('-s', '--show', action='store_true', help='If present output gradient sample') +p.add_argument('-p', '--palette', choices=('16', '256'), help='Use this palette. Defaults to 240-color palette (256 colors without first 16)') +p.add_argument('-w', '--weights', metavar='INT INT ...', type=nums, help='Adjust weights of colors. Number of weights must be equal to number of colors') + +args = p.parse_args() def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset): @@ -47,13 +63,6 @@ def gradient(DATA): return gradient_function -def get_color(rgb): - if type(rgb) is unicode: - return int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16) - else: - return rgbint_to_rgb(cterm_to_hex[rgb]) - - def get_rgb(*args): return "%02x%02x%02x" % args @@ -62,11 +71,7 @@ def col_distance(rgb1, rgb2): return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3))) -def rgbint_to_rgb(rgbint): - return ((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF) - - -def find_color(urgb, colors): +def find_color(urgb, colors, ctrans): cur_distance = 3 * (255 ** 2 + 1) cur_color = None i = 0 @@ -75,7 +80,7 @@ def find_color(urgb, colors): dist = col_distance(urgb, crgb) if dist < cur_distance: cur_distance = dist - cur_color = (i, crgb) + cur_color = (ctrans(i), crgb) i += 1 return cur_color @@ -88,44 +93,81 @@ def print_color(color): sys.stdout.write('\033[48;' + colstr + 'm ') -def print_colors(colors, num=100): - for i in range(num + 1): +def print_colors(colors, num): + for i in range(num): color = colors[int(round(i * (len(colors) - 1) / num))] print_color(color) sys.stdout.write('\033[0m\n') -c = [get_color(color) for color in json.loads(sys.argv[1])] -m = int(sys.argv[2]) if len(sys.argv) > 2 else 100 -m += m % (len(c) - 1) -step = m / (len(c) - 1) -data = [(i * step, c[i - 1], c[i]) for i in range(1, len(c))] +def dec_scale_generator(num): + j = 0 + r = '' + while num: + r += '\033[{0}m'.format(j % 2) + for i in range(10): + r += str(i) + num -= 1 + if not num: + break + j += 1 + r += '\033[0m\n' + return r + + +m = args.num_items + +maxweight = len(args.gradient) - 1 +if args.weights: + weight_sum = sum(args.weights) + norm_weights = [100.0 * weight / weight_sum for weight in args.weights] + steps = [0] + for weight in norm_weights: + steps.append(steps[-1] + weight) + steps.pop(0) + steps.pop(0) +else: + step = m / maxweight + steps = [i * step for i in range(1, maxweight + 1)] + +data = [(weight, args.gradient[i - 1], args.gradient[i]) for weight, i in zip(steps, range(1, len(args.gradient)))] gr_func = gradient(data) -gradient = [gr_func(y) for y in range(0, m - 1)] +gradient = [gr_func(y) for y in range(0, m)] +palettes = { + '16': (cterm_to_hex[:16], lambda c: c), + '256': (cterm_to_hex, lambda c: c), + None: (cterm_to_hex[16:], lambda c: c + 16), +} r = [get_rgb(*color) for color in gradient] -r2 = [find_color(color, cterm_to_hex)[0] for color in gradient] +r2 = [find_color(color, *palettes[args.palette])[0] for color in gradient] r3 = [i[0] for i in groupby(r2)] print(json.dumps(r)) print(json.dumps(r2)) print(json.dumps(r3)) -if len(sys.argv) > 3 and sys.argv[3] == 'show': - print_colors(gradient) - print_colors(r2) - print_colors(r3) - sys.stdout.write('0') - sys.stdout.write(''.join(('%10u' % (i * 10) for i in range(1, 11)))) - sys.stdout.write('\n') - nums = (''.join((str(i) for i in range(10)))) - sys.stdout.write(''.join(((('\033[1m' if j % 2 else '\033[0m') + nums) for j in range(10)))) - sys.stdout.write('\033[0m0\n') - if len(sys.argv) > 6: - vmin = float(sys.argv[4]) - vmax = float(sys.argv[5]) - num = int(sys.argv[6]) - print_colors(gradient, num) +if args.show: + print_colors(args.gradient, args.num_output) + print_colors(gradient, args.num_output) + print_colors(r2, args.num_output) + print_colors(r3, args.num_output) + if not args.range and args.num_output >= 32 and (args.num_output - 1) // 10 >= 4 and (args.num_output - 1) % 10 == 0: + sys.stdout.write('0') + sys.stdout.write(''.join(('%*u' % (args.num_output // 10, i) for i in range(10, 101, 10)))) + sys.stdout.write('\n') + else: + if args.range: + vmin, vmax = args.range[1] + isint = args.range[0] + else: + isint = True + vmin = 0 + vmax = 100 s = '' - while len(s) < num: + lasts = ' ' + str(vmax) + while len(s) + len(lasts) < args.num_output: curpc = len(s) + 1 if s else 0 - curval = vmin + curpc * (vmax - vmin) / 100.0 + curval = vmin + curpc * (vmax - vmin) / args.num_output + if isint: + curval = int(round(curval)) s += str(curval) + ' ' - print(s) + sys.stdout.write(s[:-1] + lasts + '\n') + sys.stdout.write(dec_scale_generator(args.num_output) + '\n') From b883ac6ee83a9e91032576f1a7c909dde83e3887 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 18:51:49 +0400 Subject: [PATCH 0666/1472] Use same modeline in scripts/* as in other files --- scripts/powerline | 2 +- scripts/powerline-lint | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/powerline b/scripts/powerline index 925ee438..8cb88935 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +# vim:fileencoding=utf-8:noet '''Powerline prompt and statusline script.''' import sys import os diff --git a/scripts/powerline-lint b/scripts/powerline-lint index 6d2df701..6d20e38f 100755 --- a/scripts/powerline-lint +++ b/scripts/powerline-lint @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +# vim:fileencoding=utf-8:noet '''Powerline configuration checker.''' import argparse from powerline.lint import check From 2d036c0de8cb3846e3c740a224d324f328f12ee4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 19:21:11 +0400 Subject: [PATCH 0667/1472] Fix indentation of dictionaries --- tests/vim.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index aaf5e534..89b3e4df 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -316,13 +316,13 @@ class _Buffer(object): self.name = os.path.abspath(name) if name else None _buf_scopes[bufnr] = {} _buf_options[bufnr] = { - 'modified': 0, - 'readonly': 0, - 'fileformat': 'unix', - 'filetype': '', - 'buftype': '', - 'fileencoding': 'utf-8', - } + 'modified': 0, + 'readonly': 0, + 'fileformat': 'unix', + 'filetype': '', + 'buftype': '', + 'fileencoding': 'utf-8', + } _buf_lines[bufnr] = [''] from copy import copy _undostate[bufnr] = [copy(_buf_lines[bufnr])] @@ -393,9 +393,9 @@ def _init(): @_vim def _get_segment_info(): mode_translations = { - chr(ord('V') - 0x40): '^V', - chr(ord('S') - 0x40): '^S', - } + chr(ord('V') - 0x40): '^V', + chr(ord('S') - 0x40): '^S', + } mode = _mode mode = mode_translations.get(mode, mode) return { From a5210d800c1a1f62d2965980cff5809d481b0dd9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 18:50:32 +0400 Subject: [PATCH 0668/1472] Add dark_GREEN_Orange_red gradient Generated with ./tools/generate_gradients.py 22 58 94 52 --weights '60 15 10 2' . For use in virtcol_current. --- powerline/config_files/colors.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 3bbb90f2..18070190 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -74,6 +74,10 @@ "khaki1": 228 }, "gradients": { + "dark_GREEN_Orange_red": [ + [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, 94, 94, 94, 94, 94, 94, 52], + ["005f00", "015f00", "025f00", "035f00", "045f00", "055f00", "065f00", "075f00", "085f00", "095f00", "0b5f00", "0c5f00", "0d5f00", "0e5f00", "0f5f00", "105f00", "115f00", "125f00", "135f00", "145f00", "165f00", "175f00", "185f00", "195f00", "1a5f00", "1b5f00", "1c5f00", "1d5f00", "1e5f00", "1f5f00", "215f00", "225f00", "235f00", "245f00", "255f00", "265f00", "275f00", "285f00", "295f00", "2a5f00", "2c5f00", "2d5f00", "2e5f00", "2f5f00", "305f00", "315f00", "325f00", "335f00", "345f00", "355f00", "375f00", "385f00", "395f00", "3a5f00", "3b5f00", "3c5f00", "3d5f00", "3e5f00", "3f5f00", "415f00", "425f00", "435f00", "445f00", "455f00", "465f00", "475f00", "485f00", "495f00", "4a5f00", "4c5f00", "4d5f00", "4e5f00", "4f5f00", "505f00", "515f00", "525f00", "535f00", "545f00", "555f00", "575f00", "585f00", "595f00", "5a5f00", "5b5f00", "5c5f00", "5d5f00", "5e5f00", "615f00", "655f00", "685f00", "6c5f00", "6f5f00", "735f00", "765f00", "7a5f00", "7d5f00", "815f00", "845f00", "815200", "702900"] + ], "green_yellow_red": [ [190, 184, 178, 172, 166, 160], ["8ae71c", "8ce71c", "8fe71c", "92e71c", "95e71d", "98e71d", "9ae71d", "9de71d", "a0e71e", "a3e71e", "a6e71e", "a8e71e", "abe71f", "aee71f", "b1e71f", "b4e71f", "b6e720", "b9e720", "bce720", "bfe720", "c2e821", "c3e721", "c5e621", "c7e521", "c9e522", "cbe422", "cde322", "cfe222", "d1e223", "d3e123", "d5e023", "d7df23", "d9df24", "dbde24", "dddd24", "dfdc24", "e1dc25", "e3db25", "e5da25", "e7d925", "e9d926", "e9d626", "e9d426", "e9d126", "e9cf27", "e9cc27", "e9ca27", "e9c727", "e9c528", "e9c228", "e9c028", "e9bd28", "e9bb29", "e9b829", "e9b629", "e9b329", "e9b12a", "e9ae2a", "e9ac2a", "e9a92a", "eaa72b", "eaa42b", "eaa22b", "ea9f2b", "ea9d2c", "ea9b2c", "ea982c", "ea962c", "ea942d", "ea912d", "ea8f2d", "ea8d2d", "ea8a2e", "ea882e", "ea862e", "ea832e", "ea812f", "ea7f2f", "ea7c2f", "ea7a2f", "eb7830", "eb7530", "eb7330", "eb7130", "eb6f31", "eb6c31", "eb6a31", "eb6831", "eb6632", "eb6332", "eb6132", "eb5f32", "eb5d33", "eb5a33", "eb5833", "eb5633", "eb5434", "eb5134", "eb4f34", "eb4d34", "ec4b35"] From 1ffa8a471a9fce3fab1da87c40ab88238320fcc9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 19:16:35 +0400 Subject: [PATCH 0669/1472] Add GREEN_Orange_red gradient for solarized --- powerline/config_files/colors.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 18070190..c64b74c9 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -78,6 +78,10 @@ [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, 94, 94, 94, 94, 94, 94, 52], ["005f00", "015f00", "025f00", "035f00", "045f00", "055f00", "065f00", "075f00", "085f00", "095f00", "0b5f00", "0c5f00", "0d5f00", "0e5f00", "0f5f00", "105f00", "115f00", "125f00", "135f00", "145f00", "165f00", "175f00", "185f00", "195f00", "1a5f00", "1b5f00", "1c5f00", "1d5f00", "1e5f00", "1f5f00", "215f00", "225f00", "235f00", "245f00", "255f00", "265f00", "275f00", "285f00", "295f00", "2a5f00", "2c5f00", "2d5f00", "2e5f00", "2f5f00", "305f00", "315f00", "325f00", "335f00", "345f00", "355f00", "375f00", "385f00", "395f00", "3a5f00", "3b5f00", "3c5f00", "3d5f00", "3e5f00", "3f5f00", "415f00", "425f00", "435f00", "445f00", "455f00", "465f00", "475f00", "485f00", "495f00", "4a5f00", "4c5f00", "4d5f00", "4e5f00", "4f5f00", "505f00", "515f00", "525f00", "535f00", "545f00", "555f00", "575f00", "585f00", "595f00", "5a5f00", "5b5f00", "5c5f00", "5d5f00", "5e5f00", "615f00", "655f00", "685f00", "6c5f00", "6f5f00", "735f00", "765f00", "7a5f00", "7d5f00", "815f00", "845f00", "815200", "702900"] ], + "GREEN_Orange_red": [ + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1], + ["005f00", "015f00", "025f00", "035f00", "045f00", "055f00", "065f00", "075f00", "085f00", "095f00", "0b5f00", "0c5f00", "0d5f00", "0e5f00", "0f5f00", "105f00", "115f00", "125f00", "135f00", "145f00", "165f00", "175f00", "185f00", "195f00", "1a5f00", "1b5f00", "1c5f00", "1d5f00", "1e5f00", "1f5f00", "215f00", "225f00", "235f00", "245f00", "255f00", "265f00", "275f00", "285f00", "295f00", "2a5f00", "2c5f00", "2d5f00", "2e5f00", "2f5f00", "305f00", "315f00", "325f00", "335f00", "345f00", "355f00", "375f00", "385f00", "395f00", "3a5f00", "3b5f00", "3c5f00", "3d5f00", "3e5f00", "3f5f00", "415f00", "425f00", "435f00", "445f00", "455f00", "465f00", "475f00", "485f00", "495f00", "4a5f00", "4c5f00", "4d5f00", "4e5f00", "4f5f00", "505f00", "515f00", "525f00", "535f00", "545f00", "555f00", "575f00", "585f00", "595f00", "5a5f00", "5b5f00", "5c5f00", "5d5f00", "5e5f00", "615f00", "655f00", "685f00", "6c5f00", "6f5f00", "735f00", "765f00", "7a5f00", "7d5f00", "815f00", "845f00", "815200", "702900"] + ], "green_yellow_red": [ [190, 184, 178, 172, 166, 160], ["8ae71c", "8ce71c", "8fe71c", "92e71c", "95e71d", "98e71d", "9ae71d", "9de71d", "a0e71e", "a3e71e", "a6e71e", "a8e71e", "abe71f", "aee71f", "b1e71f", "b4e71f", "b6e720", "b9e720", "bce720", "bfe720", "c2e821", "c3e721", "c5e621", "c7e521", "c9e522", "cbe422", "cde322", "cfe222", "d1e223", "d3e123", "d5e023", "d7df23", "d9df24", "dbde24", "dddd24", "dfdc24", "e1dc25", "e3db25", "e5da25", "e7d925", "e9d926", "e9d626", "e9d426", "e9d126", "e9cf27", "e9cc27", "e9ca27", "e9c727", "e9c528", "e9c228", "e9c028", "e9bd28", "e9bb29", "e9b829", "e9b629", "e9b329", "e9b12a", "e9ae2a", "e9ac2a", "e9a92a", "eaa72b", "eaa42b", "eaa22b", "ea9f2b", "ea9d2c", "ea9b2c", "ea982c", "ea962c", "ea942d", "ea912d", "ea8f2d", "ea8d2d", "ea8a2e", "ea882e", "ea862e", "ea832e", "ea812f", "ea7f2f", "ea7c2f", "ea7a2f", "eb7830", "eb7530", "eb7330", "eb7130", "eb6f31", "eb6c31", "eb6a31", "eb6831", "eb6632", "eb6332", "eb6132", "eb5f32", "eb5d33", "eb5a33", "eb5833", "eb5633", "eb5434", "eb5134", "eb4f34", "eb4d34", "ec4b35"] From cce79fda0eb0c78dcc06d3beb62e1d8f2d3ec01a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 19:28:19 +0400 Subject: [PATCH 0670/1472] Add support for getbufvar('%') and &textwidth option to vim emulation --- tests/vim.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index aaf5e534..41186b70 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -196,6 +196,8 @@ def _emul_mode(*args): @_str_func def _emul_getbufvar(bufnr, varname): if varname[0] == '&': + if bufnr == '%': + bufnr = buffers[_buffer()].number if bufnr not in buffers: return '' try: @@ -316,13 +318,14 @@ class _Buffer(object): self.name = os.path.abspath(name) if name else None _buf_scopes[bufnr] = {} _buf_options[bufnr] = { - 'modified': 0, - 'readonly': 0, - 'fileformat': 'unix', - 'filetype': '', - 'buftype': '', - 'fileencoding': 'utf-8', - } + 'modified': 0, + 'readonly': 0, + 'fileformat': 'unix', + 'filetype': '', + 'buftype': '', + 'fileencoding': 'utf-8', + 'textwidth': 80, + } _buf_lines[bufnr] = [''] from copy import copy _undostate[bufnr] = [copy(_buf_lines[bufnr])] From bd0546d68876eb52edaf4ada4b4fa963be730610 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 13 Apr 2013 19:28:34 +0400 Subject: [PATCH 0671/1472] Add virtcol_current textwidth-based gradient --- .../colorschemes/vim/default.json | 1 + .../colorschemes/vim/solarized.json | 53 ++++++++++--------- powerline/segments/vim.py | 18 +++++-- tests/test_segments.py | 8 ++- 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index ec158793..9116aa02 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -26,6 +26,7 @@ "line_percent_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, + "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10" }, "col_current": { "fg": "gray6", "bg": "gray10" }, "modified_buffers": { "fg": "brightyellow", "bg": "gray2" } }, diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 2eb4c36d..1ae0b80b 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,32 +1,33 @@ { "name": "Solarized Dark", "groups": { - "background": { "fg": "oldlace", "bg": "royalblue5" }, - "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5" }, - "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, - "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "darkgreencopper" }, - "branch": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper" }, - "branch_clean": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "branch:divider": { "fg": "gray61", "bg": "darkgreencopper" }, - "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "file_size": { "fg": "oldlace", "bg": "darkgreencopper" }, - "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper" }, - "file_format": { "fg": "gray61", "bg": "royalblue5" }, - "file_encoding": { "fg": "gray61", "bg": "royalblue5" }, - "file_type": { "fg": "gray61", "bg": "royalblue5" }, - "file_vcs_status": { "fg": "red", "bg": "darkgreencopper" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper" }, - "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper" }, - "line_percent": { "fg": "oldlace", "bg": "lightskyblue4" }, - "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, - "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, - "col_current": { "fg": "azure4", "bg": "lightyellow" } + "background": { "fg": "oldlace", "bg": "royalblue5" }, + "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5" }, + "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, + "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "darkgreencopper" }, + "branch": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper" }, + "branch_clean": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "branch:divider": { "fg": "gray61", "bg": "darkgreencopper" }, + "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "file_size": { "fg": "oldlace", "bg": "darkgreencopper" }, + "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper" }, + "file_format": { "fg": "gray61", "bg": "royalblue5" }, + "file_encoding": { "fg": "gray61", "bg": "royalblue5" }, + "file_type": { "fg": "gray61", "bg": "royalblue5" }, + "file_vcs_status": { "fg": "red", "bg": "darkgreencopper" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper" }, + "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper" }, + "line_percent": { "fg": "oldlace", "bg": "lightskyblue4" }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, + "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, + "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, + "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "gray10" }, + "col_current": { "fg": "azure4", "bg": "lightyellow" } }, "mode_translations": { "nc": { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index cb05b74d..63820e70 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import absolute_import, division import os try: @@ -275,13 +275,21 @@ def col_current(pl, segment_info): @window_cached -def virtcol_current(pl): +def virtcol_current(pl, gradient=True): '''Return current visual column with concealed characters ingored - Highlight groups used: ``virtcol_current`` or ``col_current``. + :param bool gradient: + Determines whether it should show textwidth-based gradient (gradient level is ``virtcol * 100 / textwidth``). + + Highlight groups used: ``virtcol_current_gradient`` (gradient), ``virtcol_current`` or ``col_current``. ''' - return [{'contents': str(vim_funcs['virtcol']('.')), - 'highlight_group': ['virtcol_current', 'col_current']}] + col = vim_funcs['virtcol']('.') + r = [{'contents': str(col), 'highlight_group': ['virtcol_current', 'col_current']}] + if gradient: + textwidth = getbufvar('%', '&textwidth') + r[-1]['gradient_level'] = min(col * 100 / textwidth, 100) if textwidth else 0 + r[-1]['highlight_group'].insert(0, 'virtcol_current_gradient') + return r def modified_buffers(pl, text='+ ', join_str=','): diff --git a/tests/test_segments.py b/tests/test_segments.py index 18cdc65e..c4acbc26 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -436,8 +436,12 @@ class TestVim(TestCase): segment_info = vim_module._get_segment_info() self.assertEqual(vim.line_current(pl=pl, segment_info=segment_info), '1') self.assertEqual(vim.col_current(pl=pl, segment_info=segment_info), '1') - self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info), - [{'highlight_group': ['virtcol_current', 'col_current'], 'contents': '1'}]) + self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info), [{ + 'highlight_group': ['virtcol_current_gradient', 'virtcol_current', 'col_current'], 'contents': '1', 'gradient_level': 100.0 / 80, + }]) + self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info, gradient=False), [{ + 'highlight_group': ['virtcol_current', 'col_current'], 'contents': '1', + }]) def test_modified_buffers(self): pl = Pl() From 18579d70a6660f0e85732948da7568847fd80905 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 14 Apr 2013 13:23:25 +0400 Subject: [PATCH 0672/1472] Fix TypeError seen in older vim versions --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 63820e70..5cd9ffb9 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -286,7 +286,7 @@ def virtcol_current(pl, gradient=True): col = vim_funcs['virtcol']('.') r = [{'contents': str(col), 'highlight_group': ['virtcol_current', 'col_current']}] if gradient: - textwidth = getbufvar('%', '&textwidth') + textwidth = int(getbufvar('%', '&textwidth')) r[-1]['gradient_level'] = min(col * 100 / textwidth, 100) if textwidth else 0 r[-1]['highlight_group'].insert(0, 'virtcol_current_gradient') return r From 2a6a4a07e6471b352785cdbcea80eae3043a96c2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 16 Apr 2013 07:58:24 +0400 Subject: [PATCH 0673/1472] Replace {path} with {repository_root} Closes #429 --- docs/source/overview.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 19850793..1300ef12 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -91,12 +91,12 @@ Usage Vim statusline -------------- -Add the following line to your :file:`vimrc`, where ``{path}`` is the +Add the following line to your :file:`vimrc`, where ``{repository_root}`` is the absolute path to your Powerline installation directory: .. code-block:: vim - set rtp+={path}/powerline/bindings/vim + set rtp+={repository_root}/powerline/bindings/vim If you're using Vundle or Pathogen and don't want Powerline functionality in any other applications, simply add Powerline as a bundle and point the path @@ -115,22 +115,22 @@ Shell prompts Bash prompt ^^^^^^^^^^^ -Add the following line to your :file:`bashrc`, where ``{path}`` is the -absolute path to your Powerline installation directory: +Add the following line to your :file:`bashrc`, where ``{repository_root}`` is +the absolute path to your Powerline installation directory: .. code-block:: bash - . {path}/powerline/bindings/bash/powerline.sh + . {repository_root}/powerline/bindings/bash/powerline.sh Zsh prompt ^^^^^^^^^^ -Add the following line to your :file:`zshrc`, where ``{path}`` is the +Add the following line to your :file:`zshrc`, where ``{repository_root}`` is the absolute path to your Powerline installation directory: .. code-block:: bash - . {path}/powerline/bindings/zsh/powerline.zsh + . {repository_root}/powerline/bindings/zsh/powerline.zsh If you are not satisfied with powerline speed in this case, compile zpython branch from https://bitbucket.org/ZyX_I/zsh. @@ -138,10 +138,10 @@ branch from https://bitbucket.org/ZyX_I/zsh. Tmux statusline --------------- -Add the following line to your :file:`tmux.conf`, where ``{path}`` is the -absolute path to your Powerline installation directory:: +Add the following line to your :file:`tmux.conf`, where ``{repository_root}`` is +the absolute path to your Powerline installation directory:: - source '{path}/powerline/bindings/tmux/powerline.conf' + source '{repository_root}/powerline/bindings/tmux/powerline.conf' IPython prompt -------------- @@ -174,12 +174,12 @@ Awesome widget .. note:: The Powerline widget will spawn a shell script that runs in the background and updates the statusline with ``awesome-client``. -Add the following to your :file:`rc.lua`, where ``{path}`` is the absolute -path to your Powerline installation directory: +Add the following to your :file:`rc.lua`, where ``{repository_root}`` is the +absolute path to your Powerline installation directory: .. code-block:: lua - package.path = package.path .. ';{path}/powerline/bindings/awesome/?.lua' + package.path = package.path .. ';{repository_root}/powerline/bindings/awesome/?.lua' require('powerline') Then add the ``powerline_widget`` to your ``wibox``: From 1df447ebc19da48d1813bd6f42916ec86683a3ad Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 18 Apr 2013 07:40:31 +0400 Subject: [PATCH 0674/1472] Record last exit code before running anything Otherwise powerline receieves value from the previous command in _powerline_prompt function. --- powerline/bindings/bash/powerline.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 15e34f58..50eb4878 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -13,9 +13,10 @@ _powerline_tmux_set_columns() { } _powerline_prompt() { + local last_exit_code=$? [[ -z "$POWERLINE_OLD_PROMPT_COMMAND" ]] || eval $POWERLINE_OLD_PROMPT_COMMAND - PS1="$(powerline shell left -r bash_prompt --last_exit_code=$?)" + PS1="$(powerline shell left -r bash_prompt --last_exit_code=$last_exit_code)" _powerline_tmux_set_pwd } From 6ecba94b9abd089e7d01a3f92d41799662dceda7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Apr 2013 00:38:26 +0400 Subject: [PATCH 0675/1472] Added missing pl argument It is a problem with git automatic merging. --- powerline/segment.py | 2 +- powerline/theme.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index 497242e2..c96f1c0e 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -45,7 +45,7 @@ segment_getters = { } -def gen_segment_getter(ext, path, theme_configs, default_module=None): +def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): data = { 'default_module': default_module or 'powerline.segments.' + ext, 'path': path, diff --git a/powerline/theme.py b/powerline/theme.py index 2e3a3533..56a2a1bb 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -44,7 +44,7 @@ class Theme(object): theme_configs = [theme_config] if top_theme_config: theme_configs.append(top_theme_config) - get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module')) + get_segment = gen_segment_getter(pl, ext, common_config['paths'], theme_configs, theme_config.get('default_module')) for side in ['left', 'right']: for segment in theme_config['segments'].get(side, []): segment = get_segment(segment, side) From ad55daf9c363efc967f708db766d4029ffb3b307 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 2 May 2013 11:13:54 +0530 Subject: [PATCH 0676/1472] Make vcs segments *much* faster Do not call vcs status unless the relevant files have changed. Uses inotify, if available, otherwise falls back to stat() based monitoring. Fixes #396 --- powerline/lib/file_watcher.py | 31 ++++-- powerline/lib/inotify.py | 1 + powerline/lib/tree_watcher.py | 24 +++-- powerline/lib/vcs/__init__.py | 174 ++++++++++++++++++++++++++++++++- powerline/lib/vcs/bzr.py | 55 ++++++++--- powerline/lib/vcs/git.py | 117 +++++++++++++--------- powerline/lib/vcs/mercurial.py | 29 +++++- powerline/segments/common.py | 93 ++++-------------- powerline/segments/vim.py | 171 ++++++++------------------------ tests/test_lib.py | 80 ++++++++++++++- tests/test_segments.py | 24 ++--- 11 files changed, 499 insertions(+), 300 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index d2874c88..0750a5fa 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -6,14 +6,18 @@ __docformat__ = 'restructuredtext en' import os import sys +import errno from time import sleep from threading import RLock from powerline.lib.monotonic import monotonic from powerline.lib.inotify import INotify, INotifyError +def realpath(path): + return os.path.abspath(os.path.realpath(path)) class INotifyWatch(INotify): + is_stat_based = False def __init__(self, expire_time=10): @@ -55,7 +59,7 @@ class INotifyWatch(INotify): def unwatch(self, path): ''' Remove the watch for path. Raises an OSError if removing the watch fails for some reason. ''' - path = self.os.path.abspath(path) + path = realpath(path) with self.lock: self.modified.pop(path, None) self.last_query.pop(path, None) @@ -65,15 +69,24 @@ class INotifyWatch(INotify): self.handle_error() def watch(self, path): - ''' Register a watch for the file named path. Raises an OSError if path + ''' Register a watch for the file/directory named path. Raises an OSError if path does not exist. ''' import ctypes - path = self.os.path.abspath(path) + path = realpath(path) with self.lock: if path not in self.watches: bpath = path if isinstance(path, bytes) else path.encode(self.fenc) - wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), - self.MODIFY | self.ATTRIB | self.MOVE_SELF | self.DELETE_SELF) + flags = self.MOVE_SELF | self.DELETE_SELF + buf = ctypes.c_char_p(bpath) + # Try watching path as a directory + wd = self._add_watch(self._inotify_fd, buf, flags | self.ONLYDIR) + if wd == -1: + eno = ctypes.get_errno() + if eno != errno.ENOTDIR: + self.handle_error() + # Try watching path as a file + flags |= (self.MODIFY | self.ATTRIB) + wd = self._add_watch(self._inotify_fd, buf, flags) if wd == -1: self.handle_error() self.watches[path] = wd @@ -82,7 +95,7 @@ class INotifyWatch(INotify): def __call__(self, path): ''' Return True if path has been modified since the last call. Can raise OSError if the path does not exist. ''' - path = self.os.path.abspath(path) + path = realpath(path) with self.lock: self.last_query[path] = monotonic() self.expire_watches() @@ -119,17 +132,17 @@ class StatWatch(object): self.lock = RLock() def watch(self, path): - path = os.path.abspath(path) + path = realpath(path) with self.lock: self.watches[path] = os.path.getmtime(path) def unwatch(self, path): - path = os.path.abspath(path) + path = realpath(path) with self.lock: self.watches.pop(path, None) def __call__(self, path): - path = os.path.abspath(path) + path = realpath(path) with self.lock: if path not in self.watches: self.watches[path] = os.path.getmtime(path) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index 9f247bca..b6edade3 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -60,6 +60,7 @@ def load_inotify(): class INotify(object): + # See for the flags defined below # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index c8568891..b2f11fb7 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -16,18 +16,24 @@ from powerline.lib.inotify import INotify, INotifyError class NoSuchDir(ValueError): pass +class BaseDirChanged(ValueError): + pass class DirTooLarge(ValueError): + def __init__(self, bdir): ValueError.__init__(self, 'The directory {0} is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'.format(bdir)) +def realpath(path): + return os.path.abspath(os.path.realpath(path)) class INotifyTreeWatcher(INotify): + is_dummy = False def __init__(self, basedir): super(INotifyTreeWatcher, self).__init__() - self.basedir = os.path.abspath(basedir) + self.basedir = realpath(basedir) self.watch_tree() self.modified = True @@ -43,7 +49,7 @@ class INotifyTreeWatcher(INotify): def add_watches(self, base, top_level=True): ''' Add watches for this directory and all its descendant directories, recursively. ''' - base = os.path.abspath(base) + base = realpath(base) try: is_dir = self.add_watch(base) except OSError as e: @@ -119,6 +125,8 @@ class INotifyTreeWatcher(INotify): raise DirTooLarge(self.basedir) else: raise + if (mask & self.DELETE_SELF or mask & self.MOVE_SELF) and path == self.basedir: + raise BaseDirChanged('The directory %s was moved/deleted' % path) def __call__(self): self.read() @@ -128,23 +136,24 @@ class INotifyTreeWatcher(INotify): class DummyTreeWatcher(object): + is_dummy = True def __init__(self, basedir): - self.basedir = os.path.abspath(basedir) + self.basedir = realpath(basedir) def __call__(self): return False - class TreeWatcher(object): + def __init__(self, expire_time=10): self.watches = {} self.last_query_times = {} self.expire_time = expire_time * 60 def watch(self, path, logger=None): - path = os.path.abspath(path) + path = realpath(path) try: w = INotifyTreeWatcher(path) except (INotifyError, DirTooLarge) as e: @@ -168,7 +177,7 @@ class TreeWatcher(object): del self.last_query_times[path] def __call__(self, path, logger=None): - path = os.path.abspath(path) + path = realpath(path) self.expire_old_queries() self.last_query_times[path] = monotonic() w = self.watches.get(path, None) @@ -180,6 +189,9 @@ class TreeWatcher(object): return True try: return w() + except BaseDirChanged: + self.watches.pop(path, None) + return True except DirTooLarge as e: if logger is not None: logger.warn(str(e)) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 4b45293c..42c1c3c2 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,7 +1,9 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import -import os +import os, errno +from threading import Lock +from collections import defaultdict vcs_props = ( ('git', '.git', os.path.exists), @@ -11,7 +13,8 @@ vcs_props = ( def generate_directories(path): - yield path + if os.path.isdir(path): + yield path while True: old_path = path path = os.path.dirname(path) @@ -19,6 +22,173 @@ def generate_directories(path): break yield path +_file_watcher = None + +def file_watcher(): + global _file_watcher + if _file_watcher is None: + from powerline.lib.file_watcher import create_file_watcher + _file_watcher = create_file_watcher() + return _file_watcher + +branch_name_cache = {} +branch_lock = Lock() +file_status_lock = Lock() + +def get_branch_name(directory, config_file, get_func): + global branch_name_cache + with branch_lock: + # Check if the repo directory was moved/deleted + try: + changed = file_watcher()(directory) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + changed = True + if changed: + branch_name_cache.pop(config_file, None) + # Remove the watches for this repo + file_watcher().unwatch(directory) + file_watcher().unwatch(config_file) + else: + # Check if the config file has changed + try: + changed = file_watcher()(config_file) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + # Config file does not exist (happens for mercurial) + if config_file not in branch_name_cache: + branch_name_cache[config_file] = get_func(directory, config_file) + if changed: + # Config file has changed or was not tracked + branch_name_cache[config_file] = get_func(directory, config_file) + return branch_name_cache[config_file] + +class FileStatusCache(dict): + + def __init__(self): + self.dirstate_map = defaultdict(set) + self.ignore_map = defaultdict(set) + self.keypath_ignore_map = {} + + def update_maps(self, keypath, directory, dirstate_file, ignore_file_name, extra_ignore_files): + parent = keypath + ignore_files = set() + while parent != directory: + nparent = os.path.dirname(keypath) + if nparent == parent: + break + parent = nparent + ignore_files.add(os.path.join(parent, ignore_file_name)) + for f in extra_ignore_files: + ignore_files.add(os.path.join(directory, *f.split('/'))) + self.keypath_ignore_map[keypath] = ignore_files + for ignf in ignore_files: + self.ignore_map[ignf].add(keypath) + self.dirstate_map[dirstate_file].add(keypath) + + def invalidate(self, dirstate_file=None, ignore_file=None): + for keypath in self.dirstate_map[dirstate_file]: + self.pop(keypath, None) + for keypath in self.ignore_map[ignore_file]: + self.pop(keypath, None) + + def ignore_files(self, keypath): + for ignf in self.keypath_ignore_map[keypath]: + yield ignf + +file_status_cache = FileStatusCache() + +def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_func, extra_ignore_files=()): + global file_status_cache + keypath = file_path if os.path.isabs(file_path) else os.path.join(directory, file_path) + file_status_cache.update_maps(keypath, directory, dirstate_file, ignore_file_name, extra_ignore_files) + + with file_status_lock: + # Optimize case of keypath not being cached + if keypath not in file_status_cache: + file_status_cache[keypath] = ans = get_func(directory, file_path) + return ans + + # Check if any relevant files have changed + file_changed = file_watcher() + changed = False + # Check if dirstate has changed + try: + changed = file_changed(dirstate_file) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + # The .git index file does not exist for a new git repo + return get_func(directory, file_path) + + if changed: + # Remove all cached values for files that depend on this + # dirstate_file + file_status_cache.invalidate(dirstate_file=dirstate_file) + else: + # Check if the file itself has changed + try: + changed ^= file_changed(keypath) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + # Do not call get_func again for a non-existant file + if keypath not in file_status_cache: + file_status_cache[keypath] = get_func(directory, file_path) + return file_status_cache[keypath] + + if changed: + file_status_cache.pop(keypath, None) + else: + # Check if one of the ignore files has changed + for ignf in file_status_cache.ignore_files(keypath): + try: + changed ^= file_changed(ignf) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + if changed: + # Invalidate cache for all files that might be affected + # by this ignore file + file_status_cache.invalidate(ignore_file=ignf) + break + + try: + return file_status_cache[keypath] + except KeyError: + file_status_cache[keypath] = ans = get_func(directory, file_path) + return ans + +class TreeStatusCache(dict): + + def __init__(self): + from powerline.lib.tree_watcher import TreeWatcher + self.tw = TreeWatcher() + + def cache_and_get(self, key, status): + ans = self.get(key, self) + if ans is self: + ans = self[key] = status() + return ans + + def __call__(self, repo, logger): + key = repo.directory + try: + if self.tw(key): + self.pop(key, None) + except OSError as e: + logger.warn('Failed to check %s for changes, with error: %s'% key, e) + return self.cache_and_get(key, repo.status) + +_tree_status_cache = None + +def tree_status(repo, logger): + global _tree_status_cache + if _tree_status_cache is None: + _tree_status_cache = TreeStatusCache() + return _tree_status_cache(repo, logger) def guess(path): for directory in generate_directories(path): diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index c243836f..601546a5 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -2,10 +2,13 @@ from __future__ import absolute_import, unicode_literals, division, print_function import sys +import os +import re from io import StringIO -from bzrlib import (branch, workingtree, status, library_state, trace, ui) +from bzrlib import (workingtree, status, library_state, trace, ui) +from powerline.lib.vcs import get_branch_name, get_file_status class CoerceIO(StringIO): def write(self, arg): @@ -13,13 +16,29 @@ class CoerceIO(StringIO): arg = arg.decode('utf-8', 'replace') return super(CoerceIO, self).write(arg) +state = None + +nick_pat = re.compile(br'nickname\s*=\s*(.+)') + +def branch_name_from_config_file(directory, config_file): + ans = None + try: + with open(config_file, 'rb') as f: + for line in f: + m = nick_pat.match(line) + if m is not None: + ans = m.group(1).strip().decode('utf-8', 'replace') + break + except Exception: + pass + return ans or os.path.basename(directory) class Repository(object): + def __init__(self, directory): if isinstance(directory, bytes): directory = directory.decode(sys.getfilesystemencoding() or sys.getdefaultencoding() or 'utf-8') - self.directory = directory - self.state = library_state.BzrLibraryState(ui=ui.SilentUIFactory, trace=trace.DefaultConfig()) + self.directory = os.path.abspath(directory) def status(self, path=None): '''Return status of repository or file. @@ -33,20 +52,32 @@ class Repository(object): With file argument: returns status of this file: The status codes are those returned by bzr status -S ''' + if path is not None: + return get_file_status(self.directory, os.path.join(self.directory, '.bzr', 'checkout', 'dirstate'), + path, '.bzrignore', self.do_status) + return self.do_status(self.directory, path) + + def do_status(self, directory, path): try: - return self._status(path) - except: + return self._status(self.directory, path) + except Exception: pass - def _status(self, path): + def _status(self, directory, path): + global state + if state is None: + state = library_state.BzrLibraryState(ui=ui.SilentUIFactory, trace=trace.DefaultConfig()) buf = CoerceIO() - w = workingtree.WorkingTree.open(self.directory) + w = workingtree.WorkingTree.open(directory) status.show_tree_status(w, specific_files=[path] if path else None, to_file=buf, short=True) raw = buf.getvalue() if not raw.strip(): return if path: - return raw[:2] + ans = raw[:2] + if ans == 'I ': # Ignored + ans = None + return ans dirtied = untracked = ' ' for line in raw.splitlines(): if len(line) > 1 and line[1] in 'ACDMRIN': @@ -57,8 +88,6 @@ class Repository(object): return ans if ans.strip() else None def branch(self): - try: - b = branch.Branch.open(self.directory) - return b._get_nick(local=True) or None - except: - pass + config_file = os.path.join(self.directory, '.bzr', 'branch', 'branch.conf') + return get_branch_name(self.directory, config_file, branch_name_from_config_file) + diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 033d893b..db4e59d3 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -1,4 +1,46 @@ # vim:fileencoding=utf-8:noet + +import os +import re +import errno + +from powerline.lib.vcs import get_branch_name as _get_branch_name, get_file_status + +_ref_pat = re.compile(br'ref:\s*refs/heads/(.+)') + +def branch_name_from_config_file(directory, config_file): + try: + with open(config_file, 'rb') as f: + raw = f.read() + except EnvironmentError: + return os.path.basename(directory) + m = _ref_pat.match(raw) + if m is not None: + return m.group(1).decode('utf-8', 'replace') + return '[DETACHED HEAD]' + +def get_branch_name(base_dir): + head = os.path.join(base_dir, '.git', 'HEAD') + try: + return _get_branch_name(base_dir, head, branch_name_from_config_file) + except OSError as e: + if getattr(e, 'errno', None) == errno.ENOTDIR or getattr(e, 'winerror', None) == 3: + # We are in a submodule + return '(no branch)' + raise + +def do_status(directory, path, func): + if path: + gitd = os.path.join(directory, '.git') + if os.path.isfile(gitd): + with open(gitd, 'rb') as f: + raw = f.read().partition(b':')[2].strip() + gitd = os.path.abspath(os.path.join(directory, raw)) + return get_file_status(directory, os.path.join(gitd, 'index'), + path, '.gitignore', func, extra_ignore_files=('.git/info/exclude',)) + return func(directory, path) + + try: import pygit2 as git @@ -6,28 +48,12 @@ try: __slots__ = ('directory') def __init__(self, directory): - self.directory = directory + self.directory = os.path.abspath(directory) - def _repo(self): - return git.Repository(self.directory) - - def status(self, path=None): - '''Return status of repository or file. - - Without file argument: returns status of the repository: - - :First column: working directory status (D: dirty / space) - :Second column: index status (I: index dirty / space) - :Third column: presense of untracked files (U: untracked files / space) - :None: repository clean - - With file argument: returns status of this file. Output is - equivalent to the first two columns of "git status --porcelain" - (except for merge statuses as they are not supported by libgit2). - ''' + def do_status(self, directory, path): if path: try: - status = self._repo().status_file(path) + status = git.Repository(directory).status_file(path) except (KeyError, ValueError): return None @@ -60,7 +86,7 @@ try: wt_column = ' ' index_column = ' ' untracked_column = ' ' - for status in self._repo().status().values(): + for status in git.Repository(directory).status().values(): if status & git.GIT_STATUS_WT_NEW: untracked_column = 'U' continue @@ -76,21 +102,24 @@ try: r = wt_column + index_column + untracked_column return r if r != ' ' else None + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :First column: working directory status (D: dirty / space) + :Second column: index status (I: index dirty / space) + :Third column: presence of untracked files (U: untracked files / space) + :None: repository clean + + With file argument: returns status of this file. Output is + equivalent to the first two columns of "git status --porcelain" + (except for merge statuses as they are not supported by libgit2). + ''' + return do_status(self.directory, path, self.do_status) + def branch(self): - try: - ref = self._repo().lookup_reference('HEAD') - except KeyError: - return None - - try: - target = ref.target - except ValueError: - return '[DETACHED HEAD]' - - if target.startswith('refs/heads/'): - return target[11:] - else: - return '[DETACHED HEAD]' + return get_branch_name(self.directory) except ImportError: from subprocess import Popen, PIPE @@ -105,22 +134,22 @@ except ImportError: __slots__ = ('directory',) def __init__(self, directory): - self.directory = directory + self.directory = os.path.abspath(directory) - def _gitcmd(self, *args): - return readlines(('git',) + args, self.directory) + def _gitcmd(self, directory, *args): + return readlines(('git',) + args, directory) - def status(self, path=None): + def do_status(self, directory, path): if path: try: - return next(self._gitcmd('status', '--porcelain', '--ignored', '--', path))[:2] + return next(self._gitcmd(directory, 'status', '--porcelain', '--ignored', '--', path))[:2] except StopIteration: return None else: wt_column = ' ' index_column = ' ' untracked_column = ' ' - for line in self._gitcmd('status', '--porcelain'): + for line in self._gitcmd(directory, 'status', '--porcelain'): if line[0] == '?': untracked_column = 'U' continue @@ -136,8 +165,8 @@ except ImportError: r = wt_column + index_column + untracked_column return r if r != ' ' else None + def status(self, path=None): + return do_status(self.directory, path, self.do_status) + def branch(self): - for line in self._gitcmd('branch', '-l'): - if line[0] == '*': - return line[2:] - return None + return get_branch_name(self.directory) diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index 246bdec2..ade06282 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -1,7 +1,19 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import + +import os + from mercurial import hg, ui, match +from powerline.lib.vcs import get_branch_name, get_file_status + +def branch_name_from_config_file(directory, config_file): + try: + with open(config_file, 'rb') as f: + raw = f.read() + return raw.decode('utf-8', 'replace').strip() + except Exception: + return 'default' class Repository(object): __slots__ = ('directory', 'ui') @@ -11,13 +23,13 @@ class Repository(object): repo_statuses_str = (None, 'D ', ' U', 'DU') def __init__(self, directory): - self.directory = directory + self.directory = os.path.abspath(directory) self.ui = ui.ui() - def _repo(self): + def _repo(self, directory): # Cannot create this object once and use always: when repository updates # functions emit invalid results - return hg.repository(self.ui, self.directory) + return hg.repository(self.ui, directory) def status(self, path=None): '''Return status of repository or file. @@ -32,7 +44,13 @@ class Repository(object): "R"emoved, "D"eleted (removed from filesystem, but still tracked), "U"nknown, "I"gnored, (None)Clean. ''' - repo = self._repo() + if path: + return get_file_status(self.directory, os.path.join(self.directory, '.hg', 'dirstate'), + path, '.hgignore', self.do_status) + return self.do_status(self.directory, path) + + def do_status(self, directory, path): + repo = self._repo(directory) if path: m = match.match(None, None, [path], exact=True) statuses = repo.status(match=m, unknown=True, ignored=True) @@ -48,4 +66,5 @@ class Repository(object): return self.repo_statuses_str[resulting_status] def branch(self): - return self._repo().dirstate.branch() + config_file = os.path.join(self.directory, '.hg', 'branch') + return get_branch_name(self.directory, config_file, branch_name_from_config_file) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f7ccf1dd..014d22c7 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -39,84 +39,27 @@ def hostname(pl, segment_info, only_if_ssh=False, exclude_domain=False): @requires_segment_info -class RepositorySegment(KwThreadedSegment): - def __init__(self): - super(RepositorySegment, self).__init__() - self.directories = {} +def branch(pl, segment_info, status_colors=False): + '''Return the current VCS branch. - @staticmethod - def key(segment_info, **kwargs): - return os.path.abspath(segment_info['getcwd']()) + :param bool status_colors: + determines whether repository status will be used to determine highlighting. Default: True. - def update(self, *args): - # .compute_state() is running only in this method, and only in one - # thread, thus operations with .directories do not need write locks - # (.render() method is not using .directories). If this is changed - # .directories needs redesigning - self.directories.clear() - return super(RepositorySegment, self).update(*args) - - def compute_state(self, path): - repo = guess(path=path) - if repo: - if repo.directory in self.directories: - return self.directories[repo.directory] - else: - r = self.process_repo(repo) - self.directories[repo.directory] = r - return r - - -class RepositoryStatusSegment(RepositorySegment): - interval = 2 - - @staticmethod - def process_repo(repo): - return repo.status() - - -repository_status = with_docstring(RepositoryStatusSegment(), -'''Return the status for the current VCS repository.''') - - -class BranchSegment(RepositorySegment): - interval = 0.2 - started_repository_status = False - - @staticmethod - def process_repo(repo): - return repo.branch() - - @staticmethod - def render_one(branch, status_colors=False, **kwargs): - if branch and status_colors: - return [{ - 'contents': branch, - 'highlight_group': ['branch_dirty' if repository_status(**kwargs) else 'branch_clean', 'branch'], - }] - else: - return branch - - def startup(self, status_colors=False, **kwargs): - super(BranchSegment, self).startup(**kwargs) + Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. + ''' + name = segment_info['getcwd']() + repo = guess(path=name) + if repo is not None: + branch = repo.branch() + scol = ['branch'] if status_colors: - self.started_repository_status = True - repository_status.startup(**kwargs) - - def shutdown(self): - if self.started_repository_status: - repository_status.shutdown() - super(BranchSegment, self).shutdown() - - -branch = with_docstring(BranchSegment(), -'''Return the current VCS branch. - -:param bool status_colors: - determines whether repository status will be used to determine highlighting. Default: True. - -Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. -''') + from powerline.lib.vcs import tree_status + status = tree_status(repo, pl) + scol.insert(0, 'branch_dirty' if status and status.strip() else 'branch_clean') + return [{ + 'contents': branch, + 'highlight_group': scol, + }] @requires_segment_info diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 9e7a8b67..16116639 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -13,7 +13,6 @@ from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess from powerline.lib.humanize_bytes import humanize_bytes -from powerline.lib.threaded import KwThreadedSegment, with_docstring from powerline.lib import wraps_saveargs as wraps from collections import defaultdict @@ -307,137 +306,53 @@ def modified_buffers(pl, text='+ ', join_str=','): return text + join_str.join(buffer_mod) return None +@requires_segment_info +def branch(pl, segment_info, status_colors=True): + '''Return the current working branch. -class KwWindowThreadedSegment(KwThreadedSegment): - def set_state(self, **kwargs): - kwargs = kwargs.copy() - for window in vim.windows: - buffer = window.buffer - kwargs['segment_info'] = {'bufnr': buffer.number, 'buffer': buffer} - super(KwWindowThreadedSegment, self).set_state(**kwargs) + :param bool status_colors: + determines whether repository status will be used to determine highlighting. Default: False. + Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. -class RepositorySegment(KwWindowThreadedSegment): - def __init__(self): - super(RepositorySegment, self).__init__() - self.directories = {} - - @staticmethod - def key(segment_info, **kwargs): - # FIXME os.getcwd() is not a proper variant for non-current buffers - return segment_info['buffer'].name or os.getcwd() - - def update(self, *args): - # .compute_state() is running only in this method, and only in one - # thread, thus operations with .directories do not need write locks - # (.render() method is not using .directories). If this is changed - # .directories needs redesigning - self.directories.clear() - return super(RepositorySegment, self).update(*args) - - def compute_state(self, path): - repo = guess(path=path) - if repo: - if repo.directory in self.directories: - return self.directories[repo.directory] - else: - r = self.process_repo(repo) - self.directories[repo.directory] = r - return r - + Divider highlight group used: ``branch:divider``. + ''' + name = segment_info['buffer'].name + skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype'))) + if not skip: + repo = guess(path=name) + if repo is not None: + branch = repo.branch() + scol = ['branch'] + if status_colors: + from powerline.lib.vcs import tree_status + status = tree_status(repo, pl) + scol.insert(0, 'branch_dirty' if status and status.strip() else 'branch_clean') + return [{ + 'contents': branch, + 'highlight_group': scol, + 'divider_highlight_group': 'branch:divider', + }] @requires_segment_info -class RepositoryStatusSegment(RepositorySegment): - interval = 2 +def file_vcs_status(pl, segment_info): + '''Return the VCS status for this buffer. - @staticmethod - def process_repo(repo): - return repo.status() - - -repository_status = with_docstring(RepositoryStatusSegment(), -'''Return the status for the current repo.''') - - -@requires_segment_info -class BranchSegment(RepositorySegment): - interval = 0.2 - started_repository_status = False - - @staticmethod - def process_repo(repo): - return repo.branch() - - def render_one(self, branch, segment_info, status_colors=False, **kwargs): - if not branch: - return None - - if status_colors: - self.started_repository_status = True - - return [{ - 'contents': branch, - 'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info, **kwargs) else 'branch_clean'] - if status_colors else []) + ['branch'], - 'divider_highlight_group': 'branch:divider', - }] - - def startup(self, status_colors=False, **kwargs): - super(BranchSegment, self).startup(**kwargs) - if status_colors: - self.started_repository_status = True - repository_status.startup(**kwargs) - - def shutdown(self): - if self.started_repository_status: - repository_status.shutdown() - super(BranchSegment, self).shutdown() - - -branch = with_docstring(BranchSegment(), -'''Return the current working branch. - -:param bool status_colors: - determines whether repository status will be used to determine highlighting. Default: False. - -Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. - -Divider highlight group used: ``branch:divider``. -''') - - -@requires_segment_info -class FileVCSStatusSegment(KwWindowThreadedSegment): - interval = 0.2 - - @staticmethod - def key(segment_info, **kwargs): - name = segment_info['buffer'].name - skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype'))) - return name, skip - - @staticmethod - def compute_state(key): - name, skip = key - if not skip: - repo = guess(path=name) - if repo: - status = repo.status(os.path.relpath(name, repo.directory)) - if not status: - return None - status = status.strip() - ret = [] - for status in status: - ret.append({ - 'contents': status, - 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], + Highlight groups used: ``file_vcs_status``. + ''' + name = segment_info['buffer'].name + skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype'))) + if not skip: + repo = guess(path=name) + if repo is not None: + status = repo.status(os.path.relpath(name, repo.directory)) + if not status: + return None + status = status.strip() + ret = [] + for status in status: + ret.append({ + 'contents': status, + 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], }) - return ret - return None - - -file_vcs_status = with_docstring(FileVCSStatusSegment(), -'''Return the VCS status for this buffer. - -Highlight groups used: ``file_vcs_status``. -''') + return ret diff --git a/tests/test_lib.py b/tests/test_lib.py index 654f4905..c3f89525 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -124,6 +124,17 @@ use_mercurial = use_bzr = sys.version_info < (3, 0) class TestVCS(TestCase): + def do_branch_rename_test(self, repo, q): + import time + st = time.time() + while time.time() - st < 1: + # Give inotify time to deliver events + ans = repo.branch() + if ans == q: + break + time.sleep(0.01) + self.assertEqual(ans, q) + def test_git(self): repo = guess(path=GIT_REPO) self.assertNotEqual(repo, None) @@ -143,6 +154,20 @@ class TestVCS(TestCase): self.assertEqual(repo.status(), 'DI ') self.assertEqual(repo.status('file'), 'AM') os.remove(os.path.join(GIT_REPO, 'file')) + # Test changing branch + self.assertEqual(repo.branch(), 'master') + call(['git', 'branch', 'branch1'], cwd=GIT_REPO) + call(['git', 'checkout', '-q', 'branch1'], cwd=GIT_REPO) + self.do_branch_rename_test(repo, 'branch1') + # For some reason the rest of this test fails on travis and only on + # travis, and I can't figure out why + if 'TRAVIS' in os.environ: + raise SkipTest('Part of this test fails on Travis for unknown reasons') + call(['git', 'branch', 'branch2'], cwd=GIT_REPO) + call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) + self.do_branch_rename_test(repo, 'branch2') + call(['git', 'checkout', '-q', '--detach', 'branch1'], cwd=GIT_REPO) + self.do_branch_rename_test(repo, '[DETACHED HEAD]') if use_mercurial: def test_mercurial(self): @@ -170,17 +195,66 @@ class TestVCS(TestCase): f.write('abc') self.assertEqual(repo.status(), ' U') self.assertEqual(repo.status('file'), '? ') - call(['bzr', 'add', '.'], cwd=BZR_REPO, stdout=PIPE) + call(['bzr', 'add', '-q', '.'], cwd=BZR_REPO, stdout=PIPE) self.assertEqual(repo.status(), 'D ') self.assertEqual(repo.status('file'), '+N') - call(['bzr', 'commit', '-m', 'initial commit'], cwd=BZR_REPO, stdout=PIPE, stderr=PIPE) + call(['bzr', 'commit', '-q', '-m', 'initial commit'], cwd=BZR_REPO) self.assertEqual(repo.status(), None) with open(os.path.join(BZR_REPO, 'file'), 'w') as f: f.write('def') self.assertEqual(repo.status(), 'D ') self.assertEqual(repo.status('file'), ' M') self.assertEqual(repo.status('notexist'), None) - os.remove(os.path.join(BZR_REPO, 'file')) + with open(os.path.join(BZR_REPO, 'ignored'), 'w') as f: + f.write('abc') + self.assertEqual(repo.status('ignored'), '? ') + # Test changing the .bzrignore file should update status + with open(os.path.join(BZR_REPO, '.bzrignore'), 'w') as f: + f.write('ignored') + self.assertEqual(repo.status('ignored'), None) + # Test changing the dirstate file should invalidate the cache for + # all files in the repo + with open(os.path.join(BZR_REPO, 'file2'), 'w') as f: + f.write('abc') + call(['bzr', 'add', 'file2'], cwd=BZR_REPO, stdout=PIPE) + call(['bzr', 'commit', '-q', '-m', 'file2 added'], cwd=BZR_REPO) + with open(os.path.join(BZR_REPO, 'file'), 'a') as f: + f.write('hello') + with open(os.path.join(BZR_REPO, 'file2'), 'a') as f: + f.write('hello') + self.assertEqual(repo.status('file'), ' M') + self.assertEqual(repo.status('file2'), ' M') + call(['bzr', 'commit', '-q', '-m', 'multi'], cwd=BZR_REPO) + self.assertEqual(repo.status('file'), None) + self.assertEqual(repo.status('file2'), None) + + # Test changing branch + call(['bzr', 'nick', 'branch1'], cwd=BZR_REPO, stdout=PIPE, stderr=PIPE) + self.do_branch_rename_test(repo, 'branch1') + + # Test branch name/status changes when swapping repos + for x in ('b1', 'b2'): + d = os.path.join(BZR_REPO, x) + os.mkdir(d) + call(['bzr', 'init', '-q'], cwd=d) + call(['bzr', 'nick', '-q', x], cwd=d) + repo = guess(path=d) + self.assertEqual(repo.branch(), x) + self.assertFalse(repo.status()) + if x == 'b1': + open(os.path.join(d, 'dirty'), 'w').close() + self.assertTrue(repo.status()) + os.rename(os.path.join(BZR_REPO, 'b1'), os.path.join(BZR_REPO, 'b')) + os.rename(os.path.join(BZR_REPO, 'b2'), os.path.join(BZR_REPO, 'b1')) + os.rename(os.path.join(BZR_REPO, 'b'), os.path.join(BZR_REPO, 'b2')) + for x, y in (('b1', 'b2'), ('b2', 'b1')): + d = os.path.join(BZR_REPO, x) + repo = guess(path=d) + self.do_branch_rename_test(repo, y) + if x == 'b1': + self.assertFalse(repo.status()) + else: + self.assertTrue(repo.status()) old_HGRCPATH = None old_cwd = None diff --git a/tests/test_segments.py b/tests/test_segments.py index c4acbc26..f6a29952 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -77,16 +77,19 @@ class TestCommon(TestCase): pl = Pl() segment_info = {'getcwd': os.getcwd} with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), 'tests') + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'highlight_group': ['branch'], 'contents': 'tests'}]) self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), 'tests') + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'highlight_group': ['branch'], 'contents': 'tests'}]) self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info), 'tests') + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'highlight_group': ['branch'], 'contents': 'tests'}]) with replace_attr(common, 'guess', lambda path: None): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info), None) + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), None) def test_cwd(self): new_os = new_module('os', path=os.path, sep='/') @@ -451,12 +454,12 @@ class TestVim(TestCase): pl = Pl() with vim_module._with('buffer', '/foo') as segment_info: with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info), + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info), + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) @@ -474,15 +477,6 @@ class TestVim(TestCase): with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None) - def test_repository_status(self): - pl = Pl() - segment_info = vim_module._get_segment_info() - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): - self.assertEqual(vim.repository_status(pl=pl, segment_info=segment_info), None) - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): - self.assertEqual(vim.repository_status(pl=pl, segment_info=segment_info), 'DU') - - old_cwd = None From 4c426df5e020be62572afd0a542ebd9ca53a733f Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 14:13:43 +0400 Subject: [PATCH 0677/1472] Move --config and --theme_option processing to finish_args --- powerline/shell.py | 22 ++++++++++++++++------ scripts/powerline | 5 +++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/powerline/shell.py b/powerline/shell.py index 4e8aa789..2ff2c9e4 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -7,8 +7,9 @@ from powerline.lib import mergedicts, parsedotval def mergeargs(argvalue): if not argvalue: return None - r = dict([argvalue[0]]) - for subval in argvalue[1:]: + argvalue = iter(argvalue) + r = dict([next(argvalue)]) + for subval in argvalue: mergedicts(r, dict([subval])) return r @@ -16,13 +17,13 @@ def mergeargs(argvalue): class ShellPowerline(Powerline): def __init__(self, args, **kwargs): self.args = args - self.theme_option = mergeargs(args.theme_option) or {} + self.theme_option = args.theme_option super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, **kwargs) def load_main_config(self): r = super(ShellPowerline, self).load_main_config() if self.args.config: - mergedicts(r, mergeargs(self.args.config)) + mergedicts(r, self.args.config) return r def load_theme_config(self, name): @@ -49,7 +50,16 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-w', '--width', type=int) p.add_argument('--last_exit_code', metavar='INT', type=int) p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) - p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', type=parsedotval, action='append') - p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', type=parsedotval, action='append') + p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') + p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') p.add_argument('-p', '--config_path', metavar='PATH') return p + + +def finish_args(args): + if args.config: + args.config = mergeargs((parsedotval(v) for v in args.config)) + if args.theme_option: + args.theme_option = mergeargs((parsedotval(v) for v in args.config)) + else: + args.theme_option = {} diff --git a/scripts/powerline b/scripts/powerline index 8cb88935..c4e8b0aa 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -5,14 +5,15 @@ import sys import os try: - from powerline.shell import ShellPowerline, get_argparser + from powerline.shell import ShellPowerline, get_argparser, finish_args except ImportError: import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from powerline.shell import ShellPowerline, get_argparser # NOQA + from powerline.shell import ShellPowerline, get_argparser, finish_args # NOQA if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() + finish_args(args) powerline = ShellPowerline(args, run_once=True) rendered = powerline.render( width=args.width, From a202072292c8f2f60474b49c79f11da34bfeb637 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 20:19:36 +0400 Subject: [PATCH 0678/1472] Use deepcopy in powerline.lib.config --- powerline/lib/config.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index b3ef57b5..8fec4d95 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -2,6 +2,7 @@ from powerline.lib.threaded import MultiRunnedThread from powerline.lib.file_watcher import create_file_watcher +from copy import deepcopy from threading import Event, Lock from collections import defaultdict @@ -103,10 +104,10 @@ class ConfigLoader(MultiRunnedThread): def load(self, path): try: # No locks: GIL does what we need - return self.loaded[path] + return deepcopy(self.loaded[path]) except KeyError: r = self._load(path) - self.loaded[path] = r + self.loaded[path] = deepcopy(r) return r def update(self): @@ -140,7 +141,7 @@ class ConfigLoader(MultiRunnedThread): self.missing.pop(key) for path in toload: try: - self.loaded[path] = self._load(path) + self.loaded[path] = deepcopy(self._load(path)) except Exception as e: self.exception('Error while loading {0}: {1}', path, str(e)) From 26ad06826587d510efba0fb70e6b34004f354318 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 15:19:56 +0400 Subject: [PATCH 0679/1472] Fix tmux cwd segment Fixes #467 Still needs colors for cwd:current_folder, cwd and cwd:divider --- powerline/bindings/bash/powerline.sh | 11 +++++++++-- powerline/bindings/tmux/powerline.conf | 2 +- powerline/bindings/zsh/powerline.zsh | 3 ++- powerline/lib/__init__.py | 6 +++++- powerline/renderers/tmux.py | 12 ++++++++++++ powerline/shell.py | 1 + scripts/powerline | 5 ++++- 7 files changed, 34 insertions(+), 6 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 50eb4878..ba33e2aa 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -1,11 +1,17 @@ _powerline_tmux_setenv() { if [[ -n "$TMUX" ]]; then - tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + tmux setenv -g TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + tmux refresh -S fi } +POWERLINE_SAVED_PWD= + _powerline_tmux_set_pwd() { - _powerline_tmux_setenv PWD "$PWD" + if test "x$POWERLINE_SAVED_PWD" != "x$PWD" ; then + POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi } _powerline_tmux_set_columns() { @@ -18,6 +24,7 @@ _powerline_prompt() { eval $POWERLINE_OLD_PROMPT_COMMAND PS1="$(powerline shell left -r bash_prompt --last_exit_code=$last_exit_code)" _powerline_tmux_set_pwd + return $last_exit_code } trap "_powerline_tmux_set_columns" SIGWINCH diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 5d43cd37..5951848c 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -5,7 +5,7 @@ set -g status-fg colour231 set -g status-bg colour234 set -g status-left-length 20 set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(powerline tmux left)' -set -g status-right '#(powerline tmux right)' +set -g status-right '#(powerline tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[fg=colour249]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index a45a6657..301bc411 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,7 +1,8 @@ _powerline_tmux_setenv() { emulate -L zsh if [[ -n "$TMUX" ]]; then - tmux setenv TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + tmux setenv -g TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + tmux refresh -S fi } diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 8f6a7ba2..f8d831ac 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -44,7 +44,11 @@ def keyvaluesplit(s): raise ValueError('Option names must not start with `_\'') idx = s.index('=') o = s[:idx] - val = json.loads(s[idx + 1:]) + rest = s[idx + 1:] + if rest[0] in '"{[0193456789' or rest in ('null', 'true', 'false'): + val = json.loads(s[idx + 1:]) + else: + val = rest return (o, val) diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 34e4329e..19ac3fed 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -40,5 +40,17 @@ class TmuxRenderer(Renderer): tmux_attr += ['nounderscore'] return '#[' + ','.join(tmux_attr) + ']' + def get_segment_info(self, segment_info): + r = self.segment_info.copy() + if segment_info: + r.update(segment_info) + if 'pane_id' in r: + print r['pane_id'] + print sorted(r['environ'].keys()) + varname = 'TMUX_PWD_' + r['pane_id'].lstrip('%') + if varname in r['environ']: + r['getcwd'] = lambda: r['environ'][varname] + return r + renderer = TmuxRenderer diff --git a/powerline/shell.py b/powerline/shell.py index 2ff2c9e4..8b9e8045 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -53,6 +53,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') p.add_argument('-p', '--config_path', metavar='PATH') + p.add_argument('-R', '--renderer_arg', metavar='KEY="VAL"', type=lambda a: dict([parsedotval(a)])) return p diff --git a/scripts/powerline b/scripts/powerline index c4e8b0aa..264f1794 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -15,10 +15,13 @@ if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() finish_args(args) powerline = ShellPowerline(args, run_once=True) + segment_info = {'args': args, 'environ': os.environ}, + if args.renderer_arg: + segment_info.update(args.renderer_arg) rendered = powerline.render( width=args.width, side=args.side, - segment_info={'args': args, 'environ': os.environ}, + segment_info=segment_info, ) try: sys.stdout.write(rendered) From b78a8cea14229ee74e68a9b9cbfce694921be875 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 15:47:05 +0400 Subject: [PATCH 0680/1472] Fix tmux tests --- tests/lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 85f196fd..a28c4588 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -17,7 +17,7 @@ class Pl(object): class Args(object): - theme_option = None + theme_option = {} config = None config_path = None ext = ['shell'] From 467bc535892c979438376d318ea8c35e6972370c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 16:01:44 +0400 Subject: [PATCH 0681/1472] Also fix bash tests --- tests/test_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 668c57b6..ba30f83d 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -71,7 +71,7 @@ class TestConfig(TestCase): def test_bash(self): from powerline.shell import ShellPowerline - args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]) + args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config={'ext': {'shell': {'theme': 'default_leftonly'}}}) with ShellPowerline(args, run_once=False) as powerline: powerline.render(segment_info={'args': args}) with ShellPowerline(args, run_once=False) as powerline: From 495f87a52752e8c58c2525ed06dac7c97bb00f10 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 16:06:52 +0400 Subject: [PATCH 0682/1472] Remove debugging lines --- powerline/renderers/tmux.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 19ac3fed..655c1a6b 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -45,8 +45,6 @@ class TmuxRenderer(Renderer): if segment_info: r.update(segment_info) if 'pane_id' in r: - print r['pane_id'] - print sorted(r['environ'].keys()) varname = 'TMUX_PWD_' + r['pane_id'].lstrip('%') if varname in r['environ']: r['getcwd'] = lambda: r['environ'][varname] From ed65d34e1aa704af0d8bd2c57d52b17ada1626bb Mon Sep 17 00:00:00 2001 From: Jacob Walker Date: Thu, 2 May 2013 09:35:13 -0500 Subject: [PATCH 0683/1472] Segment to Print Environment Variables - Added segment function to powerline.segments.common. - Added test to check return values. - Added default colorschemes for everything but iPython. Ref #477 --- powerline/config_files/colorschemes/shell/default.json | 3 ++- .../config_files/colorschemes/shell/solarized.json | 3 ++- powerline/config_files/colorschemes/tmux/default.json | 3 ++- powerline/config_files/colorschemes/vim/default.json | 3 ++- powerline/config_files/colorschemes/vim/solarized.json | 3 ++- powerline/config_files/colorschemes/wm/default.json | 3 ++- powerline/segments/common.py | 10 ++++++++++ tests/test_segments.py | 9 +++++++++ 8 files changed, 31 insertions(+), 6 deletions(-) diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 639c1f9b..9a5c5883 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -12,6 +12,7 @@ "cwd:divider": { "fg": "gray7", "bg": "gray4" }, "hostname": { "fg": "brightyellow", "bg": "mediumorange" }, "exit_fail": { "fg": "white", "bg": "darkestred" }, - "exit_success": { "fg": "white", "bg": "darkestgreen" } + "exit_success": { "fg": "white", "bg": "darkestgreen" }, + "environment": { "fg": "white", "bg": "darkestgreen" } } } diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index 5bf86723..70093ba7 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -12,6 +12,7 @@ "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, "exit_fail": { "fg": "oldlace", "bg": "red" }, - "exit_success": { "fg": "oldlace", "bg": "green" } + "exit_success": { "fg": "oldlace", "bg": "green" }, + "environment": { "fg": "oldlace", "bg": "green" } } } diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 35686d32..a300ee85 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -19,6 +19,7 @@ "network_load": { "fg": "gray8", "bg": "gray0" }, "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" } + "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, + "environment": { "fg": "gray8", "bg": "gray0" } } } diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 9116aa02..612eb57e 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -28,7 +28,8 @@ "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10" }, "col_current": { "fg": "gray6", "bg": "gray10" }, - "modified_buffers": { "fg": "brightyellow", "bg": "gray2" } + "modified_buffers": { "fg": "brightyellow", "bg": "gray2" }, + "environment": { "fg": "gray8", "bg": "gray2" } }, "mode_translations": { "nc": { diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 1ae0b80b..0d28db83 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -27,7 +27,8 @@ "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "gray10" }, - "col_current": { "fg": "azure4", "bg": "lightyellow" } + "col_current": { "fg": "azure4", "bg": "lightyellow" }, + "environment": { "fg": "gray61", "bg": "royalblue5" } }, "mode_translations": { "nc": { diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json index d71d4e3c..445da4f6 100644 --- a/powerline/config_files/colorschemes/wm/default.json +++ b/powerline/config_files/colorschemes/wm/default.json @@ -20,6 +20,7 @@ "system_load": { "fg": "gray8", "bg": "gray0" }, "system_load_good": { "fg": "lightyellowgreen", "bg": "gray0" }, "system_load_bad": { "fg": "gold3", "bg": "gray0" }, - "system_load_ugly": { "fg": "orangered", "bg": "gray0" } + "system_load_ugly": { "fg": "orangered", "bg": "gray0" }, + "environment": { "fg": "gray8", "bg": "gray0" } } } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f7ccf1dd..fda80382 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -22,6 +22,16 @@ from collections import namedtuple cpu_count = None +@requires_segment_info +def environment(pl, segment_info, variable=None): + '''Return the value of any defined environment variable + + :param string variable: + The environment variable to return if found + ''' + return segment_info['environ'].get(variable, None) + + @requires_segment_info def hostname(pl, segment_info, only_if_ssh=False, exclude_domain=False): '''Return the current hostname. diff --git a/tests/test_segments.py b/tests/test_segments.py index c4acbc26..6c81c88c 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -327,6 +327,15 @@ class TestCommon(TestCase): segment_info['environ'].pop('VIRTUAL_ENV') self.assertEqual(common.virtualenv(pl=pl, segment_info=segment_info), None) + def test_environment(self): + pl = Pl() + variable = 'FOO'; + value = 'bar'; + with replace_env(variable, value) as segment_info: + self.assertEqual(common.environment(pl=pl, segment_info=segment_info, variable=variable), value) + segment_info['environ'].pop(variable) + self.assertEqual(common.environment(pl=pl, segment_info=segment_info, variable=variable), None) + def test_email_imap_alert(self): # TODO pass From 7b495aff0cfbfd8217bc69be1e9e64d9885bbaa8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 3 May 2013 10:34:50 +0530 Subject: [PATCH 0684/1472] Fix tests failing if inotify not available --- powerline/segments/vim.py | 3 +-- tests/test_segments.py | 40 +++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 16116639..7c3462e9 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -11,7 +11,7 @@ except ImportError: from powerline.bindings.vim import vim_get_func, getbufvar from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group -from powerline.lib.vcs import guess +from powerline.lib.vcs import guess, tree_status from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib import wraps_saveargs as wraps from collections import defaultdict @@ -325,7 +325,6 @@ def branch(pl, segment_info, status_colors=True): branch = repo.branch() scol = ['branch'] if status_colors: - from powerline.lib.vcs import tree_status status = tree_status(repo, pl) scol.insert(0, 'branch_dirty' if status and status.strip() else 'branch_clean') return [{ diff --git a/tests/test_segments.py b/tests/test_segments.py index f6a29952..befad178 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -77,17 +77,19 @@ class TestCommon(TestCase): pl = Pl() segment_info = {'getcwd': os.getcwd} with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), - [{'highlight_group': ['branch'], 'contents': 'tests'}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), - [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) + with replace_attr(common, 'tree_status', lambda repo, pl: None): + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'highlight_group': ['branch'], 'contents': 'tests'}]) + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), + [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), - [{'highlight_group': ['branch'], 'contents': 'tests'}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), - [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), - [{'highlight_group': ['branch'], 'contents': 'tests'}]) + with replace_attr(common, 'tree_status', lambda repo, pl: 'D '): + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'highlight_group': ['branch'], 'contents': 'tests'}]) + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), + [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) + self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'highlight_group': ['branch'], 'contents': 'tests'}]) with replace_attr(common, 'guess', lambda path: None): self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), None) @@ -454,15 +456,17 @@ class TestVim(TestCase): pl = Pl() with vim_module._with('buffer', '/foo') as segment_info: with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) + with replace_attr(vim, 'tree_status', lambda repo, pl: None): + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) + with replace_attr(vim, 'tree_status', lambda repo, pl: 'DU'): + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) + self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), + [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) def test_file_vcs_status(self): pl = Pl() From af018120e121caed724464a754dd63851a89e42c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 3 May 2013 10:50:57 +0530 Subject: [PATCH 0685/1472] Forgot to move the import to module level --- powerline/segments/common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 014d22c7..094c2c1b 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -11,7 +11,7 @@ from multiprocessing import cpu_count as _cpu_count from powerline.lib import add_divider_highlight_group from powerline.lib.url import urllib_read, urllib_urlencode -from powerline.lib.vcs import guess +from powerline.lib.vcs import guess, tree_status from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring from powerline.lib.monotonic import monotonic from powerline.lib.humanize_bytes import humanize_bytes @@ -53,7 +53,6 @@ def branch(pl, segment_info, status_colors=False): branch = repo.branch() scol = ['branch'] if status_colors: - from powerline.lib.vcs import tree_status status = tree_status(repo, pl) scol.insert(0, 'branch_dirty' if status and status.strip() else 'branch_clean') return [{ From 41783344a8421b49f71284e8a43bfc3a96ceb2d2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 4 May 2013 08:21:28 +0530 Subject: [PATCH 0686/1472] Turn off status colors by default in vim as well --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 7c3462e9..ecbfed13 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -307,7 +307,7 @@ def modified_buffers(pl, text='+ ', join_str=','): return None @requires_segment_info -def branch(pl, segment_info, status_colors=True): +def branch(pl, segment_info, status_colors=False): '''Return the current working branch. :param bool status_colors: From cdbe85bbde491b15335d99dc8dc3075de3416c44 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 4 May 2013 09:43:17 +0530 Subject: [PATCH 0687/1472] Add a debug() function to allow command line debugging of the vcs code --- powerline/lib/vcs/__init__.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 42c1c3c2..e87a24f8 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -201,3 +201,23 @@ def guess(path): except: pass return None + +def debug(): + ''' To use run python -c "from powerline.lib.vcs import debug; debug()" some_file_to_watch ''' + import sys + dest = sys.argv[-1] + repo = guess(dest) + if repo is None: + print ('%s is not a recognized vcs repo' % dest) + raise SystemExit(1) + print ('Watching %s' % dest) + print ('Press Ctrl-C to exit.') + try: + while True: + if os.path.isdir(dest): + print ('Branch name: %s Status: %s' % (repo.branch(), repo.status())) + else: + print ('File status: %s' % repo.status(dest)) + raw_input('Press Enter to check again: ') + except KeyboardInterrupt: + pass From 98b7ed2950c0432bc2e2c103637bee5dbce0fffe Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 23:39:19 +0400 Subject: [PATCH 0688/1472] Reset emulate call: - only emulate -L causes options to be restore after function call - using setopt outside of functions does not reintroduce #342 --- powerline/bindings/zsh/powerline.zsh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 301bc411..89b86cff 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -15,15 +15,13 @@ _powerline_tmux_set_columns() { } _powerline_install_precmd() { - emulate zsh + emulate -L zsh for f in "${precmd_functions[@]}"; do if [[ "$f" = "_powerline_precmd" ]]; then return fi done chpwd_functions+=( _powerline_tmux_set_pwd ) - setopt promptpercent - setopt promptsubst if zmodload zsh/zpython &>/dev/null ; then zpython 'from powerline.bindings.zsh import setup as powerline_setup' zpython 'powerline_setup()' @@ -37,4 +35,6 @@ _powerline_install_precmd() { trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns +setopt promptpercent +setopt promptsubst _powerline_install_precmd From ba33c327f56e62d19a1aef0401b01ef3dedda02f Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 23:56:49 +0400 Subject: [PATCH 0689/1472] Add __contains__ method Fixes #471 --- powerline/bindings/zsh/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 1634794e..26800676 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -76,6 +76,14 @@ class Environment(object): except IndexError: return default + @staticmethod + def __contains__(key): + try: + zsh.getvalue(key) + return True + except IndexError: + return False + environ = Environment() From f3d7a6394b081ab2bef5135bb5c9f4ac7f1b8028 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 01:23:04 +0400 Subject: [PATCH 0690/1472] decode takes no keyword arguments in python-2.6 --- powerline/bindings/zsh/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 26800676..ede6870a 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -56,7 +56,7 @@ class Args(object): def string(s): if type(s) is bytes: - return s.decode('utf-8', errors='replace') + return s.decode('utf-8', 'replace') else: return str(s) From ee48836f7c667578845ac20a864c0d1eb3019043 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 2 May 2013 12:59:50 +0400 Subject: [PATCH 0691/1472] Always return true in install.sh --- tests/install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/install.sh b/tests/install.sh index eb81a522..138da22a 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,3 +9,4 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi +true From f83ca080bab7faa34324970d238a6bcb5d7b02ee Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 6 May 2013 19:13:31 +0400 Subject: [PATCH 0692/1472] Fix typo in scripts/powerline --- scripts/powerline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/powerline b/scripts/powerline index 264f1794..e6517c7f 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -15,7 +15,7 @@ if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() finish_args(args) powerline = ShellPowerline(args, run_once=True) - segment_info = {'args': args, 'environ': os.environ}, + segment_info = {'args': args, 'environ': os.environ} if args.renderer_arg: segment_info.update(args.renderer_arg) rendered = powerline.render( From 4c4b7a2bb61de03a9890c6f0e1658f629a3fb54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Mon, 13 May 2013 03:56:55 -0300 Subject: [PATCH 0693/1472] Make docs match actual default. --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 094c2c1b..43ef5f4c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -43,7 +43,7 @@ def branch(pl, segment_info, status_colors=False): '''Return the current VCS branch. :param bool status_colors: - determines whether repository status will be used to determine highlighting. Default: True. + determines whether repository status will be used to determine highlighting. Default: False. Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. ''' From 235d6d3652154d2764eddd5c6e1bd630e4f5e2be Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 14 May 2013 14:33:17 +0530 Subject: [PATCH 0694/1472] Fix #494 --- powerline/lib/vcs/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index e87a24f8..a322e6bf 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -193,7 +193,10 @@ def tree_status(repo, logger): def guess(path): for directory in generate_directories(path): for vcs, vcs_dir, check in vcs_props: - if check(os.path.join(directory, vcs_dir)): + repo_dir = os.path.join(directory, vcs_dir) + if check(repo_dir): + if os.path.isdir(repo_dir) and not os.access(repo_dir, os.X_OK): + continue try: if vcs not in globals(): globals()[vcs] = getattr(__import__('powerline.lib.vcs', fromlist=[vcs]), vcs) From 459370d71deba678d55ad9865c6a148d04f0db90 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 14 May 2013 14:42:51 +0530 Subject: [PATCH 0695/1472] Fix #489 --- powerline/lib/vcs/__init__.py | 2 +- powerline/lib/vcs/git.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index a322e6bf..e5e07f8d 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -82,7 +82,7 @@ class FileStatusCache(dict): parent = nparent ignore_files.add(os.path.join(parent, ignore_file_name)) for f in extra_ignore_files: - ignore_files.add(os.path.join(directory, *f.split('/'))) + ignore_files.add(f) self.keypath_ignore_map[keypath] = ignore_files for ignf in ignore_files: self.ignore_map[ignf].add(keypath) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index db4e59d3..db4cdc77 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -37,7 +37,7 @@ def do_status(directory, path, func): raw = f.read().partition(b':')[2].strip() gitd = os.path.abspath(os.path.join(directory, raw)) return get_file_status(directory, os.path.join(gitd, 'index'), - path, '.gitignore', func, extra_ignore_files=('.git/info/exclude',)) + path, '.gitignore', func, extra_ignore_files=(os.path.join(gitd, '.git/info/exclude'),)) return func(directory, path) From 9f20fb1f3f1881da0979778f6d83a613a5f3a3a3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 14 May 2013 09:01:04 +0400 Subject: [PATCH 0696/1472] Move some functions from VimL to python code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should be faster with new interfaces, but that is not the case Should remove zero that can be viewed temporary in place of statusline in new windows Target: ressurect setup function like source_plugin (but in place of sourcing something code is moved to python) to make it possible to do the following sequence: 1. Install powerline via pip 2. Add a line to the vimrc like py from powerline.vim import setup as setup_powerline; setup_powerline(); del setup_powerline 3. See new fancy statusline Currently there are no convenient options to use powerline installed by pip in vim. powerline/bindings/vim/plugin/powerline.vim will do what it does: check for appropriate python versions available, fix sys.path and so on, but it won’t create autocommands, set options or define PowerlinePyeval function. Note: may be rebased on top of the develop. --- powerline/bindings/vim/plugin/powerline.vim | 32 ++----------- powerline/renderers/vim.py | 7 +-- powerline/vim.py | 53 +++++++++++++++++++++ 3 files changed, 60 insertions(+), 32 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 6ed0f039..6d0365eb 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -41,39 +41,14 @@ finally endtry if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval) - let s:pyeval = function(s:powerline_pyeval) + let PowerlinePyeval = function(s:powerline_pyeval) else exec s:powerline_pycmd 'import json, vim' - exec "function! s:pyeval(e)\n". + exec "function! PowerlinePyeval(e)\n". \ s:powerline_pycmd." vim.command('return ' + json.dumps(eval(vim.eval('a:e'))))\n". \"endfunction" endif -let s:last_window_id = 0 -function! s:GetWinID(winnr) - let r = getwinvar(a:winnr, 'window_id') - if empty(r) - let r = s:last_window_id - let s:last_window_id += 1 - call setwinvar(a:winnr, 'window_id', r) - endif - " Without this condition it triggers unneeded statusline redraw - if getwinvar(a:winnr, '&statusline') isnot# '%!Powerline('.r.')' - call setwinvar(a:winnr, '&statusline', '%!Powerline('.r.')') - endif - return r -endfunction - -function! Powerline(window_id) - let winidx = index(map(range(1, winnr('$')), 's:GetWinID(v:val)'), a:window_id) - let current = w:window_id is# a:window_id - return s:pyeval('powerline.render('. a:window_id .', '. winidx .', '. current .')') -endfunction - -function! PowerlineNew() - call map(range(1, winnr('$')), 's:GetWinID(v:val)') -endfunction - function! PowerlineRegisterCachePurgerEvent(event) exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent' augroup Powerline @@ -91,5 +66,4 @@ exec s:powerline_pycmd 'powerline = VimPowerline()' exec s:powerline_pycmd 'del VimPowerline' " Is immediately changed when PowerlineNew() function is run. Good for global " value. -set statusline=%!PowerlineNew() -call PowerlineNew() +set statusline=%!PowerlinePyeval('powerline.new_window()') diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 43d1d07d..d30529f3 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -68,17 +68,18 @@ class VimRenderer(Renderer): def get_segment_info(self, segment_info): return segment_info or self.segment_info - def render(self, window_id, winidx, current): + def render(self, window, window_id, winnr): '''Render all segments.''' - if current: + if window is vim.current.window: mode = vim_mode(1) mode = mode_translations.get(mode, mode) else: mode = 'nc' segment_info = { - 'window': vim.windows[winidx], + 'window': window, 'mode': mode, 'window_id': window_id, + 'winnr': winnr, } segment_info['buffer'] = segment_info['window'].buffer segment_info['bufnr'] = segment_info['buffer'].number diff --git a/powerline/vim.py b/powerline/vim.py index b49bd45d..a0cbdc3c 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -7,9 +7,12 @@ from powerline import Powerline from powerline.lib import mergedicts from powerline.matcher import gen_matcher_getter import vim +from itertools import count vim_exists = vim_get_func('exists', rettype=int) +vim_getwinvar = vim_get_func('getwinvar') +vim_setwinvar = vim_get_func('setwinvar') def _override_from(config, override_varname): @@ -23,9 +26,13 @@ def _override_from(config, override_varname): return config +WINDOW_STASUSLINE = '%!PowerlinePyeval(\'powerline.statusline({0})\')' + + class VimPowerline(Powerline): def __init__(self): super(VimPowerline, self).__init__('vim') + self.last_window_id = 1 def add_local_theme(self, key, config): '''Add local themes at runtime (during vim session). @@ -93,3 +100,49 @@ class VimPowerline(Powerline): # fine to ignore it: no renderer == no colors to reset == no need to # do anything. pass + + if all((hasattr(vim.current.window, attr) for attr in ('options', 'vars', 'number'))): + def win_idx(self, window_id): + r = None + for window in vim.windows: + try: + curwindow_id = window.vars['powerline_window_id'] + except KeyError: + curwindow_id = self.last_window_id + self.last_window_id += 1 + window.vars['powerline_window_id'] = curwindow_id + statusline = WINDOW_STASUSLINE.format(curwindow_id) + if window.options['statusline'] != statusline: + window.options['statusline'] = statusline + if curwindow_id == window_id if window_id else window is vim.current.window: + assert r is None, "Non-unique window ID" + r = (window, curwindow_id, window.number) + return r + else: + def win_idx(self, window_id): # NOQA + r = None + for winnr, window in zip(count(1), vim.windows): + curwindow_id = vim_getwinvar(winnr, 'powerline_window_id') + if curwindow_id: + curwindow_id = int(curwindow_id) + else: + curwindow_id = self.last_window_id + self.last_window_id += 1 + vim_setwinvar(winnr, 'powerline_window_id', curwindow_id) + statusline = WINDOW_STASUSLINE.format(curwindow_id) + if vim_getwinvar(winnr, '&statusline') != statusline: + vim_setwinvar(winnr, '&statusline', statusline) + if curwindow_id == window_id if window_id else window is vim.current.window: + assert r is None, "Non-unique window ID" + r = (window, curwindow_id, winnr) + return r + + def statusline(self, window_id): + window, window_id, winnr = self.win_idx(window_id) or (None, None, None) + if not window: + return 'No window {0}'.format(window_id) + return self.render(window, window_id, winnr) + + def new_window(self): + window, window_id, winnr = self.win_idx(None) + return self.render(window, window_id, winnr) From 0823f29f7d11b51dc16f720f45fec4960c9b0e7e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 15 May 2013 00:02:11 +0400 Subject: [PATCH 0697/1472] Remove unused purgeonevents_reg and launchevent --- powerline/bindings/vim/plugin/powerline.vim | 7 ------- powerline/segments/vim.py | 23 --------------------- 2 files changed, 30 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 6d0365eb..554cad3b 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -49,13 +49,6 @@ else \"endfunction" endif -function! PowerlineRegisterCachePurgerEvent(event) - exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent' - augroup Powerline - exec 'autocmd' a:event '*' s:powerline_pycmd.' powerline_launchevent("'.a:event.'")' - augroup END -endfunction - augroup Powerline autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.reset_highlight()' autocmd! VimEnter * :redrawstatus! diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index ecbfed13..2bd92bd6 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -51,29 +51,6 @@ bufeventfuncs = defaultdict(lambda: []) defined_events = set() -def purgeonevents_reg(func, events, is_buffer_event=False): - if is_buffer_event: - cureventfuncs = bufeventfuncs - else: - cureventfuncs = eventfuncs - for event in events: - if event not in defined_events: - vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")') - defined_events.add(event) - cureventfuncs[event].append(func) - - -def launchevent(event): - global eventfuncs - global bufeventfuncs - for func in eventfuncs[event]: - func() - if bufeventfuncs[event]: - buffer = vim.buffers[int(vim_funcs['expand']('')) - 1] - for func in bufeventfuncs[event]: - func(buffer) - - # TODO Remove cache when needed def window_cached(func): cache = {} From 35e723e09374c1c65d572df1a2dbcd9ca6b841d5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 15 May 2013 00:15:02 +0400 Subject: [PATCH 0698/1472] Move code to powerline.vim.setup --- powerline/bindings/vim/plugin/powerline.vim | 40 ++++----------- powerline/vim.py | 56 ++++++++++++++++++--- 2 files changed, 61 insertions(+), 35 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 554cad3b..78e9ff53 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -15,18 +15,18 @@ if ! has('python') && ! has('python3') finish endif -let s:powerline_pycmd = substitute(get(g:, 'powerline_pycmd', has('python') ? 'py' : 'py3'), +let s:pycmd = substitute(get(g:, 'powerline_pycmd', has('python') ? 'py' : 'py3'), \'\v^(py)%[thon](3?)$', '\1\2', '') -let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval') +let s:pyeval = get(g:, 'powerline_pyeval', s:pycmd.'eval') -let s:import_cmd = 'from powerline.vim import VimPowerline' +let s:import_cmd = 'from powerline.vim import setup as powerline_setup' try - exec s:powerline_pycmd "try:\n" - \ ." ".s:import_cmd."\n" - \ ."except ImportError:\n" - \ ." import sys, vim\n" - \ ." sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))\n" - \ ." ".s:import_cmd + execute s:pycmd "try:\n" + \ ." ".s:import_cmd."\n" + \ ."except ImportError:\n" + \ ." import sys, vim\n" + \ ." sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))\n" + \ ." ".s:import_cmd let s:launched = 1 finally if !exists('s:launched') @@ -40,23 +40,5 @@ finally endif endtry -if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval) - let PowerlinePyeval = function(s:powerline_pyeval) -else - exec s:powerline_pycmd 'import json, vim' - exec "function! PowerlinePyeval(e)\n". - \ s:powerline_pycmd." vim.command('return ' + json.dumps(eval(vim.eval('a:e'))))\n". - \"endfunction" -endif - -augroup Powerline - autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.reset_highlight()' - autocmd! VimEnter * :redrawstatus! - autocmd! VimLeavePre * :exec s:powerline_pycmd 'powerline.shutdown()' -augroup END - -exec s:powerline_pycmd 'powerline = VimPowerline()' -exec s:powerline_pycmd 'del VimPowerline' -" Is immediately changed when PowerlineNew() function is run. Good for global -" value. -set statusline=%!PowerlinePyeval('powerline.new_window()') +execute s:pycmd 'powerline_setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"))' +execute s:pycmd 'del powerline_setup' diff --git a/powerline/vim.py b/powerline/vim.py index a0cbdc3c..a132f4ea 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -9,6 +9,9 @@ from powerline.matcher import gen_matcher_getter import vim from itertools import count +if not hasattr(vim, 'bindeval'): + import json + vim_exists = vim_get_func('exists', rettype=int) vim_getwinvar = vim_get_func('getwinvar') @@ -26,13 +29,11 @@ def _override_from(config, override_varname): return config -WINDOW_STASUSLINE = '%!PowerlinePyeval(\'powerline.statusline({0})\')' - - class VimPowerline(Powerline): - def __init__(self): + def __init__(self, pyeval='PowerlinePyeval'): super(VimPowerline, self).__init__('vim') self.last_window_id = 1 + self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' def add_local_theme(self, key, config): '''Add local themes at runtime (during vim session). @@ -111,7 +112,7 @@ class VimPowerline(Powerline): curwindow_id = self.last_window_id self.last_window_id += 1 window.vars['powerline_window_id'] = curwindow_id - statusline = WINDOW_STASUSLINE.format(curwindow_id) + statusline = self.window_statusline.format(curwindow_id) if window.options['statusline'] != statusline: window.options['statusline'] = statusline if curwindow_id == window_id if window_id else window is vim.current.window: @@ -129,7 +130,7 @@ class VimPowerline(Powerline): curwindow_id = self.last_window_id self.last_window_id += 1 vim_setwinvar(winnr, 'powerline_window_id', curwindow_id) - statusline = WINDOW_STASUSLINE.format(curwindow_id) + statusline = self.window_statusline.format(curwindow_id) if vim_getwinvar(winnr, '&statusline') != statusline: vim_setwinvar(winnr, '&statusline', statusline) if curwindow_id == window_id if window_id else window is vim.current.window: @@ -146,3 +147,46 @@ class VimPowerline(Powerline): def new_window(self): window, window_id, winnr = self.win_idx(None) return self.render(window, window_id, winnr) + + if not hasattr(vim, 'bindeval'): + # Method for PowerlinePyeval function. Is here to reduce the number of + # requirements to __main__ globals to just one powerline object + # (previously it required as well vim and json) + @staticmethod + def pyeval(): + vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) + + +def setup(pyeval=None, pycmd=None): + import sys + import __main__ + if not pyeval: + pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' + if not pycmd: + pycmd = 'python' if sys.version_info < (3,) else 'python3' + + # pyeval() and vim.bindeval were both introduced in one patch + if not hasattr(vim, 'bindeval'): + vim.command((''' + function! PowerlinePyeval(e) + {pycmd} powerline.pyeval() + endfunction + ''').format(pycmd=pycmd)) + pyeval = 'PowerlinePyeval' + + powerline = VimPowerline(pyeval) + __main__.powerline = powerline + + # Cannot have this in one line due to weird newline handling (in :execute + # context newline is considered part of the command in just the same cases + # when bar is considered part of the command (unless defining function + # inside :execute)). vim.command is :execute equivalent regarding this case. + vim.command('augroup Powerline') + vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd)) + vim.command(' autocmd! VimEnter * :redrawstatus!') + vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) + vim.command('augroup END') + + # Is immediately changed after new_window function is run. Good for global + # value. + vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format(pyeval=pyeval)) From 658232a1aa9ca805cb02c1a08d6037cfe3d9c54c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 15 May 2013 00:33:13 +0400 Subject: [PATCH 0699/1472] Update documentation --- docs/source/overview.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 1300ef12..dd4c06e9 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -91,8 +91,19 @@ Usage Vim statusline -------------- -Add the following line to your :file:`vimrc`, where ``{repository_root}`` is the -absolute path to your Powerline installation directory: +If installed using pip just use + +.. code-block:: vim + + python from powerline.vim import setup as powerline_setup + python powerline_setup() + python del powerline_setup + +(replace ``python`` with ``python3`` if appropriate). + +If you just cloned the repository add the following line to your :file:`vimrc`, +where ``{repository_root}`` is the absolute path to your Powerline installation +directory: .. code-block:: vim @@ -109,6 +120,9 @@ hand: ``powerline`` is installed and run just like any other plugin using call vam#ActivateAddons(['powerline']) +Note: when using Gentoo ebuild you need to specify ``USE=vim`` to enable +powerline. + Shell prompts ------------- From 9060e2b4cfbaf706c86bedc152e5482da6bff0f8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 16 May 2013 07:04:14 +0400 Subject: [PATCH 0700/1472] Fix non-bindeval fallback --- powerline/vim.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/vim.py b/powerline/vim.py index a132f4ea..a15854af 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -154,7 +154,9 @@ class VimPowerline(Powerline): # (previously it required as well vim and json) @staticmethod def pyeval(): - vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) + import __main__ + vim.command('return ' + json.dumps(eval(vim.eval('a:e'), + __main__.__dict__))) def setup(pyeval=None, pycmd=None): From ebd122d4acaf99582fcdccd2d5350afd09268c24 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 17 May 2013 08:51:33 +0400 Subject: [PATCH 0701/1472] Unfinished attempt to adapt tests to new code --- tests/test_configuration.py | 55 +++++++++++++++++++++---------------- tests/vim.py | 38 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index ba30f83d..37f2b813 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -19,36 +19,45 @@ class TestConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') - buffers = ((('bufoptions',), {'buftype': 'help'}), (('buffer', '[Command Line]'), {}), (('bufoptions',), {'buftype': 'quickfix'})) + buffers = ((('bufoptions',), {'buftype': 'help'}), (('bufname', '[Command Line]'), {}), (('bufoptions',), {'buftype': 'quickfix'})) with open(os.path.join(cfg_path, 'config.json'), 'r') as f: self.assertEqual(len(buffers), len(json.load(f)['ext']['vim']['local_themes'])) outputs = {} i = 0 mode = None + args = None + kwargs = None - with VimPowerline() as powerline: - def check_output(*args): - out = powerline.render(*args + (0 if mode == 'nc' else 1,)) - if out in outputs: - self.fail('Duplicate in set #{0} for mode {1!r} (previously defined in set #{2} for mode {3!r})'.format(i, mode, *outputs[out])) - outputs[out] = (i, mode) + with vim_module._with('split'): + with VimPowerline() as powerline: + def check_output(mode): + if mode == 'nc': + window = vim_module.windows[0] + window_id = 2 + else: + vim_module._start_mode(mode) + window = vim_module.current.window + window_id = 1 + winnr = window.number + out = powerline.render(window, window_id, winnr) + if out in outputs: + self.fail('Duplicate in set #{0} ({1}) for mode {2!r} (previously defined in set #{3} ({4!r}) for mode {5!r})'.format(i, (args, kwargs), mode, *outputs[out])) + outputs[out] = (i, (args, kwargs), mode) - with vim_module._with('buffer', 'foo.txt'): - with vim_module._with('globals', powerline_config_path=cfg_path): - exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) - try: - for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: - if mode != 'nc': - vim_module._start_mode(mode) - check_output(1, 0) - for args, kwargs in buffers: - i += 1 - if mode in exclude: - continue - with vim_module._with(*args, **kwargs): - check_output(1, 0) - finally: - vim_module._start_mode('n') + with vim_module._with('bufname', 'foo.txt'): + with vim_module._with('globals', powerline_config_path=cfg_path): + exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) + try: + for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: + check_output(mode) + for args, kwargs in buffers: + i += 1 + if mode in exclude: + continue + with vim_module._with(*args, **kwargs): + check_output(mode) + finally: + vim_module._start_mode('n') def test_tmux(self): from powerline.segments import common diff --git a/tests/vim.py b/tests/vim.py index 8d5fd3c4..5561fff4 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -284,6 +284,7 @@ class _Window(object): global _window_id self.cursor = cursor self.width = width + self.number = len(windows) + 1 if buffer: if type(buffer) is _Buffer: self.buffer = buffer @@ -371,6 +372,10 @@ class _Current(object): def buffer(self): return buffers[_buffer()] + @property + def window(self): + return windows[_window - 1] + current = _Current() @@ -453,6 +458,13 @@ def _new(name=None): _window = len(windows) +@_vim +def _split(): + global _window + _Window(buffer=buffers[_buffer()]) + _window = len(windows) + + @_vim def _del_window(winnr): win = windows.pop(winnr - 1) @@ -587,10 +599,34 @@ class _WithDict(object): self.d.pop(k) +class _WithSplit(object): + def __enter__(self): + _split() + + def __exit__(self, *args): + _close(2, wipe=False) + + +class _WithBufName(object): + def __init__(self, new): + self.new = new + + def __enter__(self): + buffer = buffers[_buffer()] + self.buffer = buffer + self.old = buffer.name + buffer.name = self.new + + def __exit__(self, *args): + self.buffer.name = self.old + + @_vim def _with(key, *args, **kwargs): if key == 'buffer': return _WithNewBuffer(_edit, *args, **kwargs) + elif key == 'bufname': + return _WithBufName(*args, **kwargs) elif key == 'mode': return _WithMode(*args, **kwargs) elif key == 'bufoptions': @@ -599,3 +635,5 @@ def _with(key, *args, **kwargs): return _WithDict(_options, **kwargs) elif key == 'globals': return _WithDict(_g, **kwargs) + elif key == 'split': + return _WithSplit() From f10729f6379884f8a06687a4f5a06bded1623416 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 May 2013 12:14:06 +0400 Subject: [PATCH 0702/1472] Add some new features to tests/vim.py --- tests/vim.py | 60 +++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index 5561fff4..89beb9c3 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -1,10 +1,10 @@ # vim:fileencoding=utf-8:noet _log = [] -_g = {} +vars = {} _window = 0 _mode = 'n' _buf_purge_events = set() -_options = { +options = { 'paste': 0, 'ambiwidth': 'single', } @@ -151,7 +151,7 @@ def command(cmd): if cmd.startswith('let g:'): import re varname, value = re.compile(r'^let g:(\w+)\s*=\s*(.*)').match(cmd).groups() - _g[varname] = value + vars[varname] = value elif cmd.startswith('hi '): sp = cmd.split() _highlights[sp[1]] = sp[2:] @@ -162,9 +162,9 @@ def command(cmd): @_vim def eval(expr): if expr.startswith('g:'): - return _g[expr[2:]] + return vars[expr[2:]] elif expr.startswith('&'): - return _options[expr[1:]] + return options[expr[1:]] elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) return "0" @@ -174,7 +174,7 @@ def eval(expr): @_vim def bindeval(expr): if expr == 'g:': - return _g + return vars import re match = re.compile(r'^function\("([^"\\]+)"\)$').match(expr) if match: @@ -201,10 +201,10 @@ def _emul_getbufvar(bufnr, varname): if bufnr not in buffers: return '' try: - return _buf_options[bufnr][varname[1:]] + return buffers[bufnr].options[varname[1:]] except KeyError: try: - return _options[varname[1:]] + return options[varname[1:]] except KeyError: return '' raise NotImplementedError @@ -213,12 +213,12 @@ def _emul_getbufvar(bufnr, varname): @_vim @_str_func def _emul_getwinvar(winnr, varname): - return _win_scopes[winnr][varname] + return windows[winnr].vars[varname] @_vim def _emul_setwinvar(winnr, varname, value): - _win_scopes[winnr][varname] = value + windows[winnr].vars[varname] = value @_vim @@ -262,7 +262,7 @@ def _emul_bufnr(expr): @_vim def _emul_exists(varname): if varname.startswith('g:'): - return varname[2:] in _g + return varname[2:] in vars raise NotImplementedError @@ -273,10 +273,9 @@ def _emul_line2byte(line): return sum((len(s) for s in buflines)) + 1 raise NotImplementedError + _window_ids = [None] _window_id = 0 -_win_scopes = [None] -_win_options = [None] class _Window(object): @@ -295,15 +294,13 @@ class _Window(object): windows.append(self) _window_id += 1 _window_ids.append(_window_id) - _win_scopes.append({}) - _win_options.append({}) + self.options = {} + self.vars = {} def __repr__(self): return '' -_buf_scopes = {} -_buf_options = {} _buf_lines = {} _undostate = {} _undo_written = {} @@ -317,8 +314,8 @@ class _Buffer(object): bufnr = _last_bufnr self.number = bufnr self.name = os.path.abspath(name) if name else None - _buf_scopes[bufnr] = {} - _buf_options[bufnr] = { + self.vars = {} + self.options = { 'modified': 0, 'readonly': 0, 'fileformat': 'unix', @@ -337,13 +334,13 @@ class _Buffer(object): return _buf_lines[self.number][line] def __setitem__(self, line, value): - _buf_options[self.number]['modified'] = 1 + self.options['modified'] = 1 _buf_lines[self.number][line] = value from copy import copy _undostate[self.number].append(copy(_buf_lines[self.number])) def __setslice__(self, *args): - _buf_options[self.number]['modified'] = 1 + self.options['modified'] = 1 _buf_lines[self.number].__setslice__(*args) from copy import copy _undostate[self.number].append(copy(_buf_lines[self.number])) @@ -359,12 +356,10 @@ class _Buffer(object): def __del__(self): bufnr = self.number - if _buf_options: - _buf_options.pop(bufnr) + if _buf_lines: _buf_lines.pop(bufnr) _undostate.pop(bufnr) _undo_written.pop(bufnr) - _buf_scopes.pop(bufnr) class _Current(object): @@ -436,8 +431,9 @@ def _undo(): return _undostate[_buffer()].pop(-1) _buf_lines[_buffer()] = _undostate[_buffer()][-1] + buf = current.buffer if _undo_written[_buffer()] == len(_undostate[_buffer()]): - _buf_options[_buffer()]['modified'] = 0 + buf.options['modified'] = 0 @_vim @@ -468,8 +464,6 @@ def _split(): @_vim def _del_window(winnr): win = windows.pop(winnr - 1) - _win_scopes.pop(winnr) - _win_options.pop(winnr) _window_ids.pop(winnr) return win @@ -525,7 +519,7 @@ def _get_buffer(): @_vim def _set_bufoption(option, value, bufnr=None): - _buf_options[bufnr or _buffer()][option] = value + buffers[bufnr or _buffer()].options[option] = value if option == 'filetype': _launch_event('FileType') @@ -565,11 +559,11 @@ class _WithBufOption(object): self.new = new def __enter__(self): - self.bufnr = _buffer() - self.old = _set_dict(_buf_options[self.bufnr], self.new, _set_bufoption)[0] + self.buffer = buffers[_buffer()] + self.old = _set_dict(self.buffer.options, self.new, _set_bufoption)[0] def __exit__(self, *args): - _buf_options[self.bufnr].update(self.old) + self.buffer.options.update(self.old) class _WithMode(object): @@ -632,8 +626,8 @@ def _with(key, *args, **kwargs): elif key == 'bufoptions': return _WithBufOption(**kwargs) elif key == 'options': - return _WithDict(_options, **kwargs) + return _WithDict(options, **kwargs) elif key == 'globals': - return _WithDict(_g, **kwargs) + return _WithDict(vars, **kwargs) elif key == 'split': return _WithSplit() From 07a130ab25edc41d83470c374ccdab6be4ccad84 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 May 2013 12:56:30 +0400 Subject: [PATCH 0703/1472] Use vim.vars and *.options if possible --- powerline/bindings/vim/__init__.py | 47 ++++++++++++++++++++---------- powerline/matchers/vim.py | 6 ++-- powerline/segments/vim.py | 16 +++++----- powerline/vim.py | 40 ++++++++++++------------- 4 files changed, 61 insertions(+), 48 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 3d7f0c08..46ae29a5 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -7,13 +7,7 @@ try: except ImportError: vim = {} -try: - _vim_globals = vim.bindeval('g:') - - def vim_set_global_var(var, val): - '''Set a global var in vim using bindeval().''' - _vim_globals[var] = val - +if hasattr(vim, 'bindeval'): def vim_get_func(f, rettype=None): '''Return a vim function binding.''' try: @@ -23,16 +17,9 @@ try: return func except vim.error: return None -except AttributeError: +else: import json - def vim_set_global_var(var, val): # NOQA - '''Set a global var in vim using vim.command(). - - This is a fallback function for older vim versions. - ''' - vim.command('let g:{0}={1}'.format(var, json.dumps(val))) - class VimFunc(object): '''Evaluate a vim function using vim.eval(). @@ -52,6 +39,36 @@ except AttributeError: vim_get_func = VimFunc + +if hasattr(vim, 'vars'): + def vim_getvar(varname): + return vim.vars[bytes(varname)] +elif hasattr(vim, 'bindeval'): + _vim_globals = vim.bindeval('g:') + + def vim_getvar(varname): # NOQA + try: + return _vim_globals[bytes(varname)] + except (KeyError, IndexError): + raise KeyError(varname) +else: + _vim_exists = vim_get_func('exists', rettype=int) + + def vim_getvar(varname): # NOQA + varname = 'g:' + varname + if _vim_exists(varname): + return vim.eval(varname) + else: + raise KeyError(varname) + +if hasattr(vim, 'options'): + def vim_getbufoption(info, option): + return info['buffer'].options[option] +else: + def vim_getbufoption(info, option): # NOQA + return getbufvar(info['bufnr'], '&' + option) + + if sys.version_info < (3,) or not hasattr(vim, 'bindeval'): getbufvar = vim_get_func('getbufvar') else: diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index 9d8eec37..1a16fb49 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -3,11 +3,11 @@ from __future__ import absolute_import import os -from powerline.bindings.vim import getbufvar +from powerline.bindings.vim import vim_getbufoption def help(matcher_info): - return str(getbufvar(matcher_info['bufnr'], '&buftype')) == 'help' + return str(vim_getbufoption(matcher_info, 'buftype')) == 'help' def cmdwin(matcher_info): @@ -16,4 +16,4 @@ def cmdwin(matcher_info): def quickfix(matcher_info): - return str(getbufvar(matcher_info['bufnr'], '&buftype')) == 'quickfix' + return str(vim_getbufoption(matcher_info, 'buftype')) == 'quickfix' diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 2bd92bd6..5ba63588 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -8,7 +8,7 @@ try: except ImportError: vim = {} # NOQA -from powerline.bindings.vim import vim_get_func, getbufvar +from powerline.bindings.vim import vim_get_func, getbufvar, vim_getbufoption from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -94,7 +94,7 @@ def modified_indicator(pl, segment_info, text='+'): :param string text: text to display if the current buffer is modified ''' - return text if int(getbufvar(segment_info['bufnr'], '&modified')) else None + return text if int(vim_getbufoption(segment_info, 'modified')) else None @requires_segment_info @@ -114,7 +114,7 @@ def readonly_indicator(pl, segment_info, text=''): :param string text: text to display if the current buffer is read-only ''' - return text if int(getbufvar(segment_info['bufnr'], '&readonly')) else None + return text if int(vim_getbufoption(segment_info, 'readonly')) else None @requires_segment_info @@ -189,7 +189,7 @@ def file_format(pl, segment_info): Divider highlight group used: ``background:divider``. ''' - return getbufvar(segment_info['bufnr'], '&fileformat') or None + return vim_getbufoption(segment_info, 'fileformat') or None @requires_segment_info @@ -201,7 +201,7 @@ def file_encoding(pl, segment_info): Divider highlight group used: ``background:divider``. ''' - return getbufvar(segment_info['bufnr'], '&fileencoding') or None + return vim_getbufoption(segment_info, 'fileencoding') or None @requires_segment_info @@ -213,7 +213,7 @@ def file_type(pl, segment_info): Divider highlight group used: ``background:divider``. ''' - return getbufvar(segment_info['bufnr'], '&filetype') or None + return vim_getbufoption(segment_info, 'filetype') or None @requires_segment_info @@ -295,7 +295,7 @@ def branch(pl, segment_info, status_colors=False): Divider highlight group used: ``branch:divider``. ''' name = segment_info['buffer'].name - skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype'))) + skip = not (name and (not vim_getbufoption(segment_info, 'buftype'))) if not skip: repo = guess(path=name) if repo is not None: @@ -317,7 +317,7 @@ def file_vcs_status(pl, segment_info): Highlight groups used: ``file_vcs_status``. ''' name = segment_info['buffer'].name - skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype'))) + skip = not (name and (not vim_getbufoption(segment_info, 'buftype'))) if not skip: repo = guess(path=name) if repo is not None: diff --git a/powerline/vim.py b/powerline/vim.py index a15854af..b658b8df 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -2,7 +2,7 @@ from __future__ import absolute_import -from powerline.bindings.vim import vim_get_func +from powerline.bindings.vim import vim_get_func, vim_getvar from powerline import Powerline from powerline.lib import mergedicts from powerline.matcher import gen_matcher_getter @@ -13,19 +13,12 @@ if not hasattr(vim, 'bindeval'): import json -vim_exists = vim_get_func('exists', rettype=int) -vim_getwinvar = vim_get_func('getwinvar') -vim_setwinvar = vim_get_func('setwinvar') - - def _override_from(config, override_varname): - if vim_exists(override_varname): - # FIXME vim.eval has problem with numeric types, vim.bindeval may be - # absent (and requires converting values to python built-in types), - # vim.eval with typed call like the one I implemented in frawor is slow. - # Maybe eval(vime.eval('string({0})'.format(override_varname)))? - overrides = vim.eval(override_varname) - mergedicts(config, overrides) + try: + overrides = vim_getvar(override_varname) + except KeyError: + return config + mergedicts(config, overrides) return config @@ -64,14 +57,14 @@ class VimPowerline(Powerline): return True def load_main_config(self): - return _override_from(super(VimPowerline, self).load_main_config(), 'g:powerline_config_overrides') + return _override_from(super(VimPowerline, self).load_main_config(), 'powerline_config_overrides') def load_theme_config(self, name): # Note: themes with non-[a-zA-Z0-9_] names are impossible to override # (though as far as I know exists() won’t throw). Won’t fix, use proper # theme names. return _override_from(super(VimPowerline, self).load_theme_config(name), - 'g:powerline_theme_overrides__' + name) + 'powerline_theme_overrides__' + name) def get_local_themes(self, local_themes): if not local_themes: @@ -82,9 +75,9 @@ class VimPowerline(Powerline): for key, val in local_themes.items())) def get_config_paths(self): - if vim_exists('g:powerline_config_path'): - return [vim.eval('g:powerline_config_path')] - else: + try: + return [vim_getvar('powerline_config_path')] + except KeyError: return super(VimPowerline, self).get_config_paths() @staticmethod @@ -120,19 +113,22 @@ class VimPowerline(Powerline): r = (window, curwindow_id, window.number) return r else: + _vim_getwinvar = staticmethod(vim_get_func('getwinvar')) + _vim_setwinvar = staticmethod(vim_get_func('setwinvar')) + def win_idx(self, window_id): # NOQA r = None for winnr, window in zip(count(1), vim.windows): - curwindow_id = vim_getwinvar(winnr, 'powerline_window_id') + curwindow_id = self._vim_getwinvar(winnr, 'powerline_window_id') if curwindow_id: curwindow_id = int(curwindow_id) else: curwindow_id = self.last_window_id self.last_window_id += 1 - vim_setwinvar(winnr, 'powerline_window_id', curwindow_id) + self._vim_setwinvar(winnr, 'powerline_window_id', curwindow_id) statusline = self.window_statusline.format(curwindow_id) - if vim_getwinvar(winnr, '&statusline') != statusline: - vim_setwinvar(winnr, '&statusline', statusline) + if self._vim_getwinvar(winnr, '&statusline') != statusline: + self._vim_setwinvar(winnr, '&statusline', statusline) if curwindow_id == window_id if window_id else window is vim.current.window: assert r is None, "Non-unique window ID" r = (window, curwindow_id, winnr) From dfaf8c3b3a1b66607510c2528c4024cc17eb2d0f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 May 2013 12:57:33 +0400 Subject: [PATCH 0704/1472] Fix messages --- tests/test_configuration.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 37f2b813..108625d7 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -24,13 +24,10 @@ class TestConfig(TestCase): self.assertEqual(len(buffers), len(json.load(f)['ext']['vim']['local_themes'])) outputs = {} i = 0 - mode = None - args = None - kwargs = None with vim_module._with('split'): with VimPowerline() as powerline: - def check_output(mode): + def check_output(mode, args, kwargs): if mode == 'nc': window = vim_module.windows[0] window_id = 2 @@ -49,13 +46,13 @@ class TestConfig(TestCase): exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) try: for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: - check_output(mode) + check_output(mode, None, None) for args, kwargs in buffers: i += 1 if mode in exclude: continue with vim_module._with(*args, **kwargs): - check_output(mode) + check_output(mode, args, kwargs) finally: vim_module._start_mode('n') From ad35b48942a7e8a3d22ad51b04a10c3d6e682922 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 May 2013 13:06:57 +0400 Subject: [PATCH 0705/1472] Add VimEnviron object --- powerline/bindings/vim/__init__.py | 18 ++++++++++++++++++ powerline/renderers/vim.py | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 46ae29a5..da2a0c46 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -79,3 +79,21 @@ else: if type(r) is bytes: return r.decode('utf-8') return r + + +class VimEnviron(object): + @staticmethod + def __getitem__(key): + return vim.eval('$' + key) + + @staticmethod + def get(key, default=None): + return vim.eval('$' + key) or default + + @staticmethod + def __setitem__(key, value): + return vim.command('let $' + key + '="' + + value.replace('"', '\\"').replace('\\', '\\\\').replace('\n', '\\n').replace('\0', '') + + '"') + +environ = VimEnviron() diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index d30529f3..8e8fb0e9 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -2,7 +2,7 @@ from __future__ import absolute_import -from powerline.bindings.vim import vim_get_func +from powerline.bindings.vim import vim_get_func, environ from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme @@ -80,6 +80,7 @@ class VimRenderer(Renderer): 'mode': mode, 'window_id': window_id, 'winnr': winnr, + 'environ': environ, } segment_info['buffer'] = segment_info['window'].buffer segment_info['bufnr'] = segment_info['buffer'].number From 962a1b6fc2fa6fdf2d42daddd5e887d5ded8aa1e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 May 2013 13:17:30 +0400 Subject: [PATCH 0706/1472] Fix tests --- tests/test_configuration.py | 2 +- tests/vim.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 108625d7..f8053fb6 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -41,7 +41,7 @@ class TestConfig(TestCase): self.fail('Duplicate in set #{0} ({1}) for mode {2!r} (previously defined in set #{3} ({4!r}) for mode {5!r})'.format(i, (args, kwargs), mode, *outputs[out])) outputs[out] = (i, (args, kwargs), mode) - with vim_module._with('bufname', 'foo.txt'): + with vim_module._with('bufname', '/tmp/foo.txt'): with vim_module._with('globals', powerline_config_path=cfg_path): exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) try: diff --git a/tests/vim.py b/tests/vim.py index 89beb9c3..bad320d7 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -358,7 +358,9 @@ class _Buffer(object): bufnr = self.number if _buf_lines: _buf_lines.pop(bufnr) + if _undostate: _undostate.pop(bufnr) + if _undo_written: _undo_written.pop(bufnr) From 5c2c47aa2fcfff5832a74182306ab5d51fab323a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 May 2013 13:13:31 +0400 Subject: [PATCH 0707/1472] Make .shutdown work if renderer was not created Fixes #486 --- powerline/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index fdbc33f3..1689f1da 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -376,7 +376,10 @@ class Powerline(object): current application. ''' self.shutdown_event.set() - self.renderer.shutdown() + try: + self.renderer.shutdown() + except AttributeError: + pass functions = ( self.on_main_change, self.on_colors_change, From ea3c9393206083cfdf0e5c12793d5d8f9c5b3efc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 May 2013 13:13:31 +0400 Subject: [PATCH 0708/1472] Fix tests on python3 --- powerline/bindings/vim/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index da2a0c46..7b8bdb24 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -42,13 +42,13 @@ else: if hasattr(vim, 'vars'): def vim_getvar(varname): - return vim.vars[bytes(varname)] + return vim.vars[str(varname)] elif hasattr(vim, 'bindeval'): _vim_globals = vim.bindeval('g:') def vim_getvar(varname): # NOQA try: - return _vim_globals[bytes(varname)] + return _vim_globals[str(varname)] except (KeyError, IndexError): raise KeyError(varname) else: From 79e44dd2a01694cb40bc42692442db07e7d5ed47 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 May 2013 13:28:29 +0400 Subject: [PATCH 0709/1472] Check self.theme_option before using Fixes #493 --- powerline/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/shell.py b/powerline/shell.py index 8b9e8045..5ec2355b 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -28,7 +28,7 @@ class ShellPowerline(Powerline): def load_theme_config(self, name): r = super(ShellPowerline, self).load_theme_config(name) - if name in self.theme_option: + if self.theme_option and name in self.theme_option: mergedicts(r, self.theme_option[name]) return r From bb4bc52c8adfac9de97f9407924a38ef56f9d31d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 14 May 2013 21:43:33 +0530 Subject: [PATCH 0710/1472] Fix path issue --- powerline/lib/vcs/git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index db4cdc77..a7fafd66 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -37,7 +37,7 @@ def do_status(directory, path, func): raw = f.read().partition(b':')[2].strip() gitd = os.path.abspath(os.path.join(directory, raw)) return get_file_status(directory, os.path.join(gitd, 'index'), - path, '.gitignore', func, extra_ignore_files=(os.path.join(gitd, '.git/info/exclude'),)) + path, '.gitignore', func, extra_ignore_files=(os.path.join(gitd, 'info/exclude'),)) return func(directory, path) From 795b9874ba8c96fdbb374f1062b9d51a41f50e5a Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 22 May 2013 00:09:58 +0400 Subject: [PATCH 0711/1472] Add missing import Also add tests Fixes #511 --- powerline/bindings/vim/plugin/powerline.vim | 1 + tests/test.sh | 3 +++ tests/test_plugin_file.vim | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100755 tests/test_plugin_file.vim diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 78e9ff53..ad2c60fc 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -40,5 +40,6 @@ finally endif endtry +execute s:pycmd 'import vim' execute s:pycmd 'powerline_setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"))' execute s:pycmd 'del powerline_setup' diff --git a/tests/test.sh b/tests/test.sh index c65e6668..9bbe0b2e 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -10,4 +10,7 @@ done if ! ${PYTHON} scripts/powerline-lint -p powerline/config_files ; then FAILED=1 fi +if ! vim -S tests/test_plugin_file.vim ; then + FAILED=1 +fi exit $FAILED diff --git a/tests/test_plugin_file.vim b/tests/test_plugin_file.vim new file mode 100755 index 00000000..6c9c3a6f --- /dev/null +++ b/tests/test_plugin_file.vim @@ -0,0 +1,16 @@ +#!/usr/bin/vim -S +set nocompatible +try + source powerline/bindings/vim/plugin/powerline.vim +catch + cquit +endtry +set ls=2 +redrawstatus! +redir =>mes + messages +redir END +if len(split(mes, "\n"))>1 + cquit +endif +quit! From 75bf6ac7bf3a8c79afce80088dfeb3ce46879c5e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 23 May 2013 10:42:51 +0530 Subject: [PATCH 0712/1472] Fix #417 --- powerline/segments/common.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 23ff2115..0d69aa08 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -711,7 +711,7 @@ class NetworkLoadSegment(KwThreadedSegment): self.interfaces[interface] = idata idata['last'] = (monotonic(), _get_bytes(interface)) - return idata + return idata.copy() def render_one(self, idata, recv_format='⬇ {value:>8}', sent_format='⬆ {value:>8}', suffix='B/s', si_prefix=False, **kwargs): if not idata or 'prev' not in idata: @@ -723,14 +723,15 @@ class NetworkLoadSegment(KwThreadedSegment): if None in (b1, b2): return None - if measure_interval == 0: - self.error('Measure interval is zero. This should not happen') - return None r = [] for i, key in zip((0, 1), ('recv', 'sent')): format = locals()[key + '_format'] - value = (b2[i] - b1[i]) / measure_interval + try: + value = (b2[i] - b1[i]) / measure_interval + except ZeroDivisionError: + self.warn('Measure interval zero.') + value = 0 max_key = key + '_max' is_gradient = max_key in kwargs hl_groups = ['network_load_' + key, 'network_load'] From 9080a34ee82d02c8b8e47856d98e889150d68579 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 May 2013 18:22:49 +0400 Subject: [PATCH 0713/1472] Fix problems with merging vim.Dictionary Also add tests Fixes #516 --- .gitignore | 2 ++ powerline/bindings/vim/__init__.py | 24 +++++++++++++++++-- tests/test.sh | 11 ++++++--- tests/test_local_overrides.vim | 38 ++++++++++++++++++++++++++++++ tests/test_plugin_file.vim | 5 +++- tests/vim.py | 4 ++++ 6 files changed, 78 insertions(+), 6 deletions(-) create mode 100755 tests/test_local_overrides.vim diff --git a/.gitignore b/.gitignore index c60af16c..7468a1a5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ __pycache__ *.egg-info dist build + +message.fail diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 7b8bdb24..db926f6a 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -17,6 +17,26 @@ if hasattr(vim, 'bindeval'): return func except vim.error: return None + + if hasattr(vim, 'Dictionary'): + VimDictionary = vim.Dictionary + VimList = vim.List + VimFunction = vim.Function + else: + VimDictionary = type(vim.bindeval('{}')) + VimList = type(vim.bindeval('[]')) + VimFunction = type(vim.bindeval('function("mode")')) + + _vim_to_python_types = { + VimDictionary: lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), + VimList: lambda value: [_vim_to_python(item) for item in value], + VimFunction: lambda _: None, + } + + _id = lambda value: value + + def _vim_to_python(value): + return _vim_to_python_types.get(type(value), _id)(value) else: import json @@ -42,13 +62,13 @@ else: if hasattr(vim, 'vars'): def vim_getvar(varname): - return vim.vars[str(varname)] + return _vim_to_python(vim.vars[str(varname)]) elif hasattr(vim, 'bindeval'): _vim_globals = vim.bindeval('g:') def vim_getvar(varname): # NOQA try: - return _vim_globals[str(varname)] + return _vim_to_python(_vim_globals[str(varname)]) except (KeyError, IndexError): raise KeyError(varname) else: diff --git a/tests/test.sh b/tests/test.sh index 9bbe0b2e..e5b137ff 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -10,7 +10,12 @@ done if ! ${PYTHON} scripts/powerline-lint -p powerline/config_files ; then FAILED=1 fi -if ! vim -S tests/test_plugin_file.vim ; then - FAILED=1 -fi +for script in tests/*.vim ; do + if ! vim -u NONE -S $script || test -f message.fail ; then + echo "Failed script $script" >&2 + cat message.fail >&2 + rm message.fail + FAILED=1 + fi +done exit $FAILED diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim new file mode 100755 index 00000000..a58578fb --- /dev/null +++ b/tests/test_local_overrides.vim @@ -0,0 +1,38 @@ +#!/usr/bin/vim -S +let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' +let g:powerline_config_overrides = {'common': {'dividers': {'left': {'hard': ' ', 'soft': ' > '}, 'right': {'hard': ' ', 'soft': ' < '}}}} +let g:powerline_theme_overrides__default = {'segment_data': {'line_current_symbol': {'contents': 'LN '}, 'branch': {'before': 'B '}}} +try + python import powerline.vim + let pycmd = 'python' +catch + try + python3 import powerline.vim + let pycmd = 'python3' + catch + call writefile(['Unable to determine python version', v:exception], 'message.fail') + cquit + endtry +endtry + +try + execute pycmd 'powerline.vim.setup()' +catch + call writefile(['Failed to run setup function', v:exception], 'message.fail') + cquit +endtry + +try + let &columns = 80 + let result = eval(&statusline[2:]) +catch + call writefile(['Exception while evaluating &stl', v:exception], 'message.fail') + cquit +endtry + +if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                 %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_160_15485749_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold#  1%#Pl_22_24320_252_13684944_NONE#:1  ' + call writefile(['Unexpected result', result], 'message.fail') + cquit +endif + +qall! diff --git a/tests/test_plugin_file.vim b/tests/test_plugin_file.vim index 6c9c3a6f..02d916fd 100755 --- a/tests/test_plugin_file.vim +++ b/tests/test_plugin_file.vim @@ -3,6 +3,7 @@ set nocompatible try source powerline/bindings/vim/plugin/powerline.vim catch + call writefile([v:exception], 'message.fail') cquit endtry set ls=2 @@ -10,7 +11,9 @@ redrawstatus! redir =>mes messages redir END -if len(split(mes, "\n"))>1 +let mess=split(mes, "\n") +if len(mess)>1 + call writefile(mess, 'message.fail') cquit endif quit! diff --git a/tests/vim.py b/tests/vim.py index bad320d7..bbcc2f27 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -175,6 +175,10 @@ def eval(expr): def bindeval(expr): if expr == 'g:': return vars + elif expr == '{}': + return {} + elif expr == '[]': + return [] import re match = re.compile(r'^function\("([^"\\]+)"\)$').match(expr) if match: From 716f7617c48ec3d4a62fe2c77b252bd7dfb6bec3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 08:52:33 +0400 Subject: [PATCH 0714/1472] Return exception string in place of throwing an exception Big bunch of `AttributeError`s when renderer failed to be created that renders vim unusable in case of some of the errors (e.g. invalid configuration) is annoying. Conflicts: powerline/__init__.py --- powerline/__init__.py | 55 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 1689f1da..4192bb27 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -10,10 +10,43 @@ from powerline.lib.config import ConfigLoader from threading import Lock, Event +try: + from __builtin__ import unicode +except ImportError: + unicode = str # NOQA + DEFAULT_SYSTEM_CONFIG_DIR = None +def safe_unicode(s): + '''Return unicode instance without raising an exception. + ''' + try: + try: + return unicode(s) + except UnicodeDecodeError: + try: + return unicode(s, 'utf-8') + except TypeError: + return unicode(str(s), 'utf-8') + except Exception as e: + return safe_unicode(e) + + +class FailedUnicode(unicode): + '''Builtin ``unicode`` (``str`` in python 3) subclass indicating fatal + error. + + If your code for some reason wants to determine whether `.render()` method + failed it should check returned string for being a FailedUnicode instance. + Alternatively you could subclass Powerline and override `.render()` method + to do what you like in place of catching the exception and returning + FailedUnicode. + ''' + pass + + def find_config_file(search_paths, config_file): config_file += '.json' for path in search_paths: @@ -360,16 +393,28 @@ class Powerline(object): try: self.create_renderer(**create_renderer_kwargs) except Exception as e: - self.pl.exception('Failed to create renderer: {0}', str(e)) - finally: - self.create_renderer_kwargs.clear() + if self.pl: + self.pl.exception('Failed to create renderer: {0}', str(e), prefix='powerline') + if hasattr(self, 'renderer'): + with self.cr_kwargs_lock: + self.create_renderer_kwargs.clear() + else: + raise + else: + with self.cr_kwargs_lock: + self.create_renderer_kwargs.clear() def render(self, *args, **kwargs): '''Update/create renderer if needed and pass all arguments further to ``self.renderer.render()``. ''' - self.update_renderer() - return self.renderer.render(*args, **kwargs) + try: + self.update_renderer() + return self.renderer.render(*args, **kwargs) + except Exception as e: + if self.pl: + self.pl.exception('Failed to render: {0}', str(e), prefix='powerline') + return FailedUnicode(safe_unicode(e)) def shutdown(self): '''Shut down all background threads. Must be run only prior to exiting From f389c43e4399d52395e6b88f693294c74d6276da Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 18:09:34 +0400 Subject: [PATCH 0715/1472] Use `try/except KeyError` in place of `if in dict/else` --- powerline/segments/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 23ff2115..f0170156 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -697,13 +697,13 @@ class NetworkLoadSegment(KwThreadedSegment): total = activity interface = name - if interface in self.interfaces: + try: idata = self.interfaces[interface] try: idata['prev'] = idata['last'] except KeyError: pass - else: + except KeyError: idata = {} if self.run_once: idata['prev'] = (monotonic(), _get_bytes(interface)) From 201175d368f8fe4ddd1a3dae1ab22e929aad9331 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 20:04:22 +0400 Subject: [PATCH 0716/1472] Pop path from self.loaded on exception --- powerline/lib/config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 8fec4d95..3b8ce5a4 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -144,6 +144,10 @@ class ConfigLoader(MultiRunnedThread): self.loaded[path] = deepcopy(self._load(path)) except Exception as e: self.exception('Error while loading {0}: {1}', path, str(e)) + try: + self.loaded.pop(path) + except KeyError: + pass def run(self): while self.interval is not None and not self.shutdown_event.is_set(): From 82e2ea10c47ea4c904a79df19d7cb236d75693b2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 23 Apr 2013 19:29:31 +0400 Subject: [PATCH 0717/1472] Replace self.pl.exception with self.exception New function is checking for self.pl being set before using logger and also removes the need of specifying explicit prefix="powerline". Conflicts: powerline/__init__.py --- powerline/__init__.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 4192bb27..b709bd92 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -266,7 +266,7 @@ class Powerline(object): try: Renderer = __import__(self.renderer_module, fromlist=['renderer']).renderer except Exception as e: - self.pl.exception('Failed to import renderer module: {0}', str(e)) + self.exception('Failed to import renderer module: {0}', str(e)) sys.exit(1) # Renderer updates configuration file via segments’ .startup thus it @@ -275,7 +275,7 @@ class Powerline(object): try: renderer = Renderer(**self.renderer_options) except Exception as e: - self.pl.exception('Failed to construct renderer object: {0}', str(e)) + self.exception('Failed to construct renderer object: {0}', str(e)) if not hasattr(self, 'renderer'): raise else: @@ -393,8 +393,7 @@ class Powerline(object): try: self.create_renderer(**create_renderer_kwargs) except Exception as e: - if self.pl: - self.pl.exception('Failed to create renderer: {0}', str(e), prefix='powerline') + self.exception('Failed to create renderer: {0}', str(e)) if hasattr(self, 'renderer'): with self.cr_kwargs_lock: self.create_renderer_kwargs.clear() @@ -412,8 +411,15 @@ class Powerline(object): self.update_renderer() return self.renderer.render(*args, **kwargs) except Exception as e: - if self.pl: - self.pl.exception('Failed to render: {0}', str(e), prefix='powerline') + try: + self.exception('Failed to render: {0}', str(e)) + except Exception as e: + # Updates e variable to new value, masking previous one. + # Normally it is the same exception (due to raise in case pl is + # unset), but it may also show error in logger. Note that latter + # is not logged by logger for obvious reasons, thus this also + # prevents us from seeing logger traceback. + pass return FailedUnicode(safe_unicode(e)) def shutdown(self): @@ -455,3 +461,9 @@ class Powerline(object): def __exit__(self, *args): self.shutdown() + + def exception(self, msg, prefix='powerline', *args, **kwargs): + if self.pl: + return self.pl.exception(msg, prefix=prefix, *args, **kwargs) + else: + raise From 1977a0125c50ade9fbeaff72eacc8f51842dc1da Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 20:31:04 +0400 Subject: [PATCH 0718/1472] Remove cache before running self.exception, not after --- powerline/lib/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 3b8ce5a4..60aff1eb 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -143,11 +143,11 @@ class ConfigLoader(MultiRunnedThread): try: self.loaded[path] = deepcopy(self._load(path)) except Exception as e: - self.exception('Error while loading {0}: {1}', path, str(e)) try: self.loaded.pop(path) except KeyError: pass + self.exception('Error while loading {0}: {1}', path, str(e)) def run(self): while self.interval is not None and not self.shutdown_event.is_set(): From d882d312fcd0d4663212a099b726241e332de081 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 21:18:08 +0400 Subject: [PATCH 0719/1472] Fix Powerline.exception --- powerline/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index b709bd92..0e03aaf1 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -462,8 +462,10 @@ class Powerline(object): def __exit__(self, *args): self.shutdown() - def exception(self, msg, prefix='powerline', *args, **kwargs): + def exception(self, msg, *args, **kwargs): + if 'prefix' not in kwargs: + kwargs['prefix'] = 'powerline' if self.pl: - return self.pl.exception(msg, prefix=prefix, *args, **kwargs) + return self.pl.exception(msg, *args, **kwargs) else: raise From 5bb32fadce5aa6e78f95a5051cb7eae2a78d051d Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 May 2013 19:03:40 +0400 Subject: [PATCH 0720/1472] Use fallback logger if normal one is not available --- powerline/__init__.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 0e03aaf1..53ffd932 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -94,6 +94,29 @@ class PowerlineLogger(object): self._log('debug', msg, *args, **kwargs) +_fallback_logger = None + + +def _get_fallback_logger(): + global _fallback_logger + if _fallback_logger: + return _fallback_logger + + log_format = '%(asctime)s:%(levelname)s:%(message)s' + formatter = logging.Formatter(log_format) + + level = logging.WARNING + handler = logging.StreamHandler() + handler.setLevel(level) + handler.setFormatter(formatter) + + logger = logging.getLogger('powerline') + logger.setLevel(level) + logger.addHandler(handler) + _fallback_logger = PowerlineLogger(None, logger, '_fallback_') + return _fallback_logger + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -465,7 +488,8 @@ class Powerline(object): def exception(self, msg, *args, **kwargs): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' - if self.pl: + try: return self.pl.exception(msg, *args, **kwargs) - else: - raise + except AttributeError: + pl = _get_fallback_logger() + return pl.exception(msg, *args, **kwargs) From 884a8b0d7866d25fe22bf90cef79461ea63f014d Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 24 May 2013 06:25:48 +0400 Subject: [PATCH 0721/1472] Use powerline-client if possible --- powerline/bindings/bash/powerline.sh | 10 +++++++++- powerline/bindings/tmux/powerline.conf | 6 ++++-- powerline/bindings/zsh/powerline.zsh | 12 ++++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index ba33e2aa..cc0a22cc 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -1,3 +1,11 @@ +if test -z "${POWERLINE_COMMAND}" ; then + if which powerline-client &>/dev/null ; then + export POWERLINE_COMMAND=powerline-client + else + export POWERLINE_COMMAND=powerline + fi +fi + _powerline_tmux_setenv() { if [[ -n "$TMUX" ]]; then tmux setenv -g TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" @@ -22,7 +30,7 @@ _powerline_prompt() { local last_exit_code=$? [[ -z "$POWERLINE_OLD_PROMPT_COMMAND" ]] || eval $POWERLINE_OLD_PROMPT_COMMAND - PS1="$(powerline shell left -r bash_prompt --last_exit_code=$last_exit_code)" + PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code)" _powerline_tmux_set_pwd return $last_exit_code } diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 5951848c..81eda186 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,11 +1,13 @@ +if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline-client" "set-environment -g POWERLINE_COMMAND powerline-client" "set-environment -g POWERLINE_COMMAND powerline"' set -g status on set -g status-utf8 on set -g status-interval 2 set -g status-fg colour231 set -g status-bg colour234 set -g status-left-length 20 -set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(powerline tmux left)' -set -g status-right '#(powerline tmux right -R pane_id=`tmux display -p "#D"`)' +set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#($POWERLINE_COMMAND tmux left)' +set -g status-right '#($POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[fg=colour249]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" +# vim: ft=tmux diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 89b86cff..e5b8d6d8 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,3 +1,11 @@ +if test -z "${POWERLINE_COMMAND}" ; then + if which powerline-client &>/dev/null ; then + export POWERLINE_COMMAND=powerline-client + else + export POWERLINE_COMMAND=powerline + fi +fi + _powerline_tmux_setenv() { emulate -L zsh if [[ -n "$TMUX" ]]; then @@ -27,8 +35,8 @@ _powerline_install_precmd() { zpython 'powerline_setup()' zpython 'del powerline_setup' else - PS1='$(powerline shell left -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' - RPS1='$(powerline shell right -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' fi } From 374da15667c327e0a9e8e713afc97db6a6d6b9a5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 24 May 2013 07:18:03 +0400 Subject: [PATCH 0722/1472] Use eval in tmux/powerline.conf fish does not support variables used as commands --- powerline/bindings/tmux/powerline.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 81eda186..ba75336e 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -5,8 +5,8 @@ set -g status-interval 2 set -g status-fg colour231 set -g status-bg colour234 set -g status-left-length 20 -set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#($POWERLINE_COMMAND tmux left)' -set -g status-right '#($POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' +set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(eval $POWERLINE_COMMAND tmux left)' +set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[fg=colour249]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" From baa44475e547e6bf908585f3863715095f076852 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 24 May 2013 07:21:40 +0400 Subject: [PATCH 0723/1472] Add note about POWERLINE_COMMAND to documentation --- docs/source/configuration.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 40adca33..073afdde 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -526,3 +526,18 @@ use ``c.Powerline.KEY``. Supported ``KEY`` strings or keyword argument names: Sets directory where configuration should be read from. If present, no default locations are searched for configuration. No expansions are performed thus you cannot use paths starting with ``~/``. + +Prompt command +-------------- + +In addition to the above configuration options you can use +``$POWERLINE_COMMAND`` environment variable to tell shell or tmux to use +specific powerline implementation. This is mostly useful for putting powerline +into different directory or replacing ``powerline`` script with +``powerline-client`` for performance reasons. + +Note: ``$POWERLINE_COMMAND`` appears in shell scripts without quotes thus you +can specify additional parameters in bash. In zsh you will have to make +``$POWERLINE_COMMAND`` an array parameter to achieve the same result. In tmux it +is passed to ``eval`` and depends on the shell used. POSIX-compatible shells, +zsh, bash and fish will split this variable in this case. From 0435d44984d06f4fada20e3d6df9b5bd2f601f16 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 30 May 2013 07:29:04 +0530 Subject: [PATCH 0724/1472] Fix committing with fugitive => stuck file status Fixes #482 (I hope) --- powerline/lib/vcs/git.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index a7fafd66..3c32db8f 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -36,8 +36,12 @@ def do_status(directory, path, func): with open(gitd, 'rb') as f: raw = f.read().partition(b':')[2].strip() gitd = os.path.abspath(os.path.join(directory, raw)) - return get_file_status(directory, os.path.join(gitd, 'index'), - path, '.gitignore', func, extra_ignore_files=(os.path.join(gitd, 'info/exclude'),)) + # We need HEAD as without it using fugitive to commit causes the + # current file's status (and only the current file) to not be updated + # for some reason I cannot be bothered to figure out. + return get_file_status( + directory, os.path.join(gitd, 'index'), + path, '.gitignore', func, extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('HEAD', 'info/exclude'))) return func(directory, path) From 441f17464c0d4922305468ebe0de900e956ae502 Mon Sep 17 00:00:00 2001 From: Hoang Xuan Phu Date: Thu, 30 May 2013 11:12:24 +0700 Subject: [PATCH 0725/1472] detect script directory to make bash and zsh usable without having `powerline` in `$PATH` --- powerline/bindings/bash/powerline.sh | 5 ++++- powerline/bindings/zsh/powerline.zsh | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index cc0a22cc..694451c8 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -1,8 +1,11 @@ if test -z "${POWERLINE_COMMAND}" ; then if which powerline-client &>/dev/null ; then export POWERLINE_COMMAND=powerline-client - else + elif which powerline &>/dev/null ; then export POWERLINE_COMMAND=powerline + else + # `$0` is set to `-bash` when using SSH so that won't work + export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" fi fi diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index e5b8d6d8..11ffd3d9 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,8 +1,10 @@ if test -z "${POWERLINE_COMMAND}" ; then if which powerline-client &>/dev/null ; then export POWERLINE_COMMAND=powerline-client - else + elif which powerline &>/dev/null ; then export POWERLINE_COMMAND=powerline + else + export POWERLINE_COMMAND="$0:A:h:h:h:h/scripts/powerline" fi fi From 2758c349db2e76d7be6911606008a6cb827cadc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Jun 2013 11:53:40 +0200 Subject: [PATCH 0726/1472] Revert "Fix committing with fugitive => stuck file status" This reverts commit 0435d44984d06f4fada20e3d6df9b5bd2f601f16. The commit introduces a huge performance penalty and is removed for now. --- powerline/lib/vcs/git.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 3c32db8f..a7fafd66 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -36,12 +36,8 @@ def do_status(directory, path, func): with open(gitd, 'rb') as f: raw = f.read().partition(b':')[2].strip() gitd = os.path.abspath(os.path.join(directory, raw)) - # We need HEAD as without it using fugitive to commit causes the - # current file's status (and only the current file) to not be updated - # for some reason I cannot be bothered to figure out. - return get_file_status( - directory, os.path.join(gitd, 'index'), - path, '.gitignore', func, extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('HEAD', 'info/exclude'))) + return get_file_status(directory, os.path.join(gitd, 'index'), + path, '.gitignore', func, extra_ignore_files=(os.path.join(gitd, 'info/exclude'),)) return func(directory, path) From 8de2a9abf99684677ae2b2367a0783bd9c25ffb5 Mon Sep 17 00:00:00 2001 From: michaelbeaumont Date: Wed, 12 Jun 2013 11:09:50 +0200 Subject: [PATCH 0727/1472] Fix infinite add_watches loop with symbolic links Added a check to ensure that add_watches doesn't run on the same folder over and over again. This occurs at least when circular symbolic links are present. Fix #543 --- powerline/lib/tree_watcher.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index b2f11fb7..7b932e42 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -50,6 +50,10 @@ class INotifyTreeWatcher(INotify): ''' Add watches for this directory and all its descendant directories, recursively. ''' base = realpath(base) + # There may exist a link which leads to an endless + # add_watches loop or to maximum recursion depth exceeded + if not top_level and base in self.watched_dirs: + return try: is_dir = self.add_watch(base) except OSError as e: From 14bec211aae63b9f878680a12f2b434af97ad023 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 14 Jun 2013 13:38:24 +0530 Subject: [PATCH 0728/1472] Various performance improvements for the vcs backend 1) Fix a bug where watching a directory would also report the directory as changed if any files inside it were changed. This would causes excessive polling by the branch segment 2) Fix a bug where the watch for the repo dir in the branch segment was being continually recreated, again causing excessive polling. This was particularly noticeable with the patch to fix fugitive file status updates. 3) Improve the performance of branch coloring for git repos by ignoring change events for the index.lock file. This file is created/deleted every time git access the index and would cause the entire working tree status to be re-queried every time. --- powerline/lib/file_watcher.py | 18 +++++++++++++----- powerline/lib/tree_watcher.py | 15 ++++++++------- powerline/lib/vcs/__init__.py | 22 ++++++++++++++++------ powerline/lib/vcs/git.py | 13 ++++++++++--- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 0750a5fa..bf78b7bb 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -84,14 +84,18 @@ class INotifyWatch(INotify): eno = ctypes.get_errno() if eno != errno.ENOTDIR: self.handle_error() - # Try watching path as a file - flags |= (self.MODIFY | self.ATTRIB) - wd = self._add_watch(self._inotify_fd, buf, flags) - if wd == -1: - self.handle_error() + # Try watching path as a file + flags |= (self.MODIFY | self.ATTRIB) + wd = self._add_watch(self._inotify_fd, buf, flags) + if wd == -1: + self.handle_error() self.watches[path] = wd self.modified[path] = False + def is_watched(self, path): + with self.lock: + return realpath(path) in self.watches + def __call__(self, path): ''' Return True if path has been modified since the last call. Can raise OSError if the path does not exist. ''' @@ -141,6 +145,10 @@ class StatWatch(object): with self.lock: self.watches.pop(path, None) + def is_watched(self, path): + with self.lock: + return realpath(path) in self.watches + def __call__(self, path): path = realpath(path) with self.lock: diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index b2f11fb7..77389f61 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -31,11 +31,12 @@ class INotifyTreeWatcher(INotify): is_dummy = False - def __init__(self, basedir): + def __init__(self, basedir, ignore_event=None): super(INotifyTreeWatcher, self).__init__() self.basedir = realpath(basedir) self.watch_tree() self.modified = True + self.ignore_event = (lambda path, name: False) if ignore_event is None else ignore_event def watch_tree(self): self.watched_dirs = {} @@ -93,7 +94,7 @@ class INotifyTreeWatcher(INotify): self.MODIFY | self.CREATE | self.DELETE | self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | - self.ATTRIB | self.MOVE_SELF | self.DELETE_SELF) + self.ATTRIB | self.DELETE_SELF) if wd == -1: eno = ctypes.get_errno() if eno == errno.ENOTDIR: @@ -112,7 +113,7 @@ class INotifyTreeWatcher(INotify): return path = self.watched_rmap.get(wd, None) if path is not None: - self.modified = True + self.modified = not self.ignore_event(path, name) if mask & self.CREATE: # A new sub-directory might have been created, monitor it. try: @@ -152,10 +153,10 @@ class TreeWatcher(object): self.last_query_times = {} self.expire_time = expire_time * 60 - def watch(self, path, logger=None): + def watch(self, path, logger=None, ignore_event=None): path = realpath(path) try: - w = INotifyTreeWatcher(path) + w = INotifyTreeWatcher(path, ignore_event=ignore_event) except (INotifyError, DirTooLarge) as e: if logger is not None: logger.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) @@ -176,14 +177,14 @@ class TreeWatcher(object): for path in pop: del self.last_query_times[path] - def __call__(self, path, logger=None): + def __call__(self, path, logger=None, ignore_event=None): path = realpath(path) self.expire_old_queries() self.last_query_times[path] = monotonic() w = self.watches.get(path, None) if w is None: try: - self.watch(path) + self.watch(path, logger=logger, ignore_event=ignore_event) except NoSuchDir: pass return True diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index e5e07f8d..848543a7 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -39,8 +39,15 @@ def get_branch_name(directory, config_file, get_func): global branch_name_cache with branch_lock: # Check if the repo directory was moved/deleted + # We cannot use the file_watcher for this as the inotify based file + # watcher will mark a directory as changed if any files inside it have + # changed, this is a big performance hit in vim, which continuously + # changes files inside the repo dir (backup/swap files, for instance). + # Check if the repo directory was moved/deleted + fw = file_watcher() + is_watched = fw.is_watched(directory) try: - changed = file_watcher()(directory) + changed = fw(directory) except OSError as e: if getattr(e, 'errno', None) != errno.ENOENT: raise @@ -48,12 +55,13 @@ def get_branch_name(directory, config_file, get_func): if changed: branch_name_cache.pop(config_file, None) # Remove the watches for this repo - file_watcher().unwatch(directory) - file_watcher().unwatch(config_file) + if is_watched: + fw.unwatch(directory) + fw.unwatch(config_file) else: # Check if the config file has changed try: - changed = file_watcher()(config_file) + changed = fw(config_file) except OSError as e: if getattr(e, 'errno', None) != errno.ENOENT: raise @@ -176,7 +184,7 @@ class TreeStatusCache(dict): def __call__(self, repo, logger): key = repo.directory try: - if self.tw(key): + if self.tw(key, logger=logger, ignore_event=getattr(repo, 'ignore_event', None)): self.pop(key, None) except OSError as e: logger.warn('Failed to check %s for changes, with error: %s'% key, e) @@ -209,7 +217,7 @@ def debug(): ''' To use run python -c "from powerline.lib.vcs import debug; debug()" some_file_to_watch ''' import sys dest = sys.argv[-1] - repo = guess(dest) + repo = guess(os.path.abspath(dest)) if repo is None: print ('%s is not a recognized vcs repo' % dest) raise SystemExit(1) @@ -224,3 +232,5 @@ def debug(): raw_input('Press Enter to check again: ') except KeyboardInterrupt: pass + except EOFError: + pass diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 3c32db8f..09c029c6 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -41,18 +41,24 @@ def do_status(directory, path, func): # for some reason I cannot be bothered to figure out. return get_file_status( directory, os.path.join(gitd, 'index'), - path, '.gitignore', func, extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('HEAD', 'info/exclude'))) + path, '.gitignore', func, extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('logs/HEAD', 'info/exclude'))) return func(directory, path) +def ignore_event(path, name): + # Ignore changes to the index.lock file, since they happen frequently and + # dont indicate an actual change in the working tree status + return False + return path.endswith('.git') and name == 'index.lock' try: import pygit2 as git class Repository(object): - __slots__ = ('directory') + __slots__ = ('directory', 'ignore_event') def __init__(self, directory): self.directory = os.path.abspath(directory) + self.ignore_event = ignore_event def do_status(self, directory, path): if path: @@ -135,10 +141,11 @@ except ImportError: yield line[:-1].decode('utf-8') class Repository(object): - __slots__ = ('directory',) + __slots__ = ('directory', 'ignore_event') def __init__(self, directory): self.directory = os.path.abspath(directory) + self.ignore_event = ignore_event def _gitcmd(self, directory, *args): return readlines(('git',) + args, directory) From a03035dd0d3f5a6048aedfd7e04ba449c422ed64 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 14 Jun 2013 13:45:13 +0530 Subject: [PATCH 0729/1472] Remove obsolete comment --- powerline/lib/vcs/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 848543a7..8666f2bf 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -38,11 +38,6 @@ file_status_lock = Lock() def get_branch_name(directory, config_file, get_func): global branch_name_cache with branch_lock: - # Check if the repo directory was moved/deleted - # We cannot use the file_watcher for this as the inotify based file - # watcher will mark a directory as changed if any files inside it have - # changed, this is a big performance hit in vim, which continuously - # changes files inside the repo dir (backup/swap files, for instance). # Check if the repo directory was moved/deleted fw = file_watcher() is_watched = fw.is_watched(directory) From 31c86486fe702bba79ada7e4e5b58d3a467410e1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 19:00:56 +0400 Subject: [PATCH 0730/1472] Disable vim.bindeval support for vim_getwar for old vim versions --- powerline/bindings/vim/__init__.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index db926f6a..76d26251 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -60,17 +60,11 @@ else: vim_get_func = VimFunc -if hasattr(vim, 'vars'): +# It may crash on some old vim versions and I do not remember in which patch +# I fixed this crash. +if hasattr(vim, 'vars') and vim.vvars['version'] > 703: def vim_getvar(varname): return _vim_to_python(vim.vars[str(varname)]) -elif hasattr(vim, 'bindeval'): - _vim_globals = vim.bindeval('g:') - - def vim_getvar(varname): # NOQA - try: - return _vim_to_python(_vim_globals[str(varname)]) - except (KeyError, IndexError): - raise KeyError(varname) else: _vim_exists = vim_get_func('exists', rettype=int) From 79b842ec9b611d9fda5503d5f94a86a3795e9b6d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 19:12:57 +0400 Subject: [PATCH 0731/1472] Remove uneeded code --- powerline/bindings/vim/__init__.py | 31 +++++++++++------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 76d26251..6705222d 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -17,26 +17,6 @@ if hasattr(vim, 'bindeval'): return func except vim.error: return None - - if hasattr(vim, 'Dictionary'): - VimDictionary = vim.Dictionary - VimList = vim.List - VimFunction = vim.Function - else: - VimDictionary = type(vim.bindeval('{}')) - VimList = type(vim.bindeval('[]')) - VimFunction = type(vim.bindeval('function("mode")')) - - _vim_to_python_types = { - VimDictionary: lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), - VimList: lambda value: [_vim_to_python(item) for item in value], - VimFunction: lambda _: None, - } - - _id = lambda value: value - - def _vim_to_python(value): - return _vim_to_python_types.get(type(value), _id)(value) else: import json @@ -63,6 +43,17 @@ else: # It may crash on some old vim versions and I do not remember in which patch # I fixed this crash. if hasattr(vim, 'vars') and vim.vvars['version'] > 703: + _vim_to_python_types = { + vim.Dictionary: lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), + vim.List: lambda value: [_vim_to_python(item) for item in value], + vim.Function: lambda _: None, + } + + _id = lambda value: value + + def _vim_to_python(value): + return _vim_to_python_types.get(type(value), _id)(value) + def vim_getvar(varname): return _vim_to_python(vim.vars[str(varname)]) else: From 987376aecb0ee6bcaaa77d015361b6b165106714 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 19:14:07 +0400 Subject: [PATCH 0732/1472] Fix tests --- tests/vim.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/vim.py b/tests/vim.py index bbcc2f27..f5c1b158 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet _log = [] vars = {} +vvars = {'version': 703} _window = 0 _mode = 'n' _buf_purge_events = set() From a85d9017fd68f4d51c8d80a888fba7e8f0681497 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 19:32:56 +0400 Subject: [PATCH 0733/1472] In place of failure create new window ID in case of duplicate ID Fixes #545 Fixes #537 --- powerline/vim.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/vim.py b/powerline/vim.py index b658b8df..b9cf5820 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -101,6 +101,8 @@ class VimPowerline(Powerline): for window in vim.windows: try: curwindow_id = window.vars['powerline_window_id'] + if r is not None and curwindow_id == window_id: + raise KeyError except KeyError: curwindow_id = self.last_window_id self.last_window_id += 1 @@ -109,7 +111,6 @@ class VimPowerline(Powerline): if window.options['statusline'] != statusline: window.options['statusline'] = statusline if curwindow_id == window_id if window_id else window is vim.current.window: - assert r is None, "Non-unique window ID" r = (window, curwindow_id, window.number) return r else: @@ -120,7 +121,7 @@ class VimPowerline(Powerline): r = None for winnr, window in zip(count(1), vim.windows): curwindow_id = self._vim_getwinvar(winnr, 'powerline_window_id') - if curwindow_id: + if curwindow_id and not (r is not None and curwindow_id == window_id): curwindow_id = int(curwindow_id) else: curwindow_id = self.last_window_id @@ -130,7 +131,6 @@ class VimPowerline(Powerline): if self._vim_getwinvar(winnr, '&statusline') != statusline: self._vim_setwinvar(winnr, '&statusline', statusline) if curwindow_id == window_id if window_id else window is vim.current.window: - assert r is None, "Non-unique window ID" r = (window, curwindow_id, winnr) return r From 5ccf8d2cb409441691b512c9053b50bdf9f645ea Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 20:30:53 +0400 Subject: [PATCH 0734/1472] Support ~/ directories in powerline-lint --- powerline/lint/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 726f255d..37c1c180 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,3 +1,5 @@ +# vim:fileencoding=utf-8:noet + from powerline.lint.markedjson import load from powerline import find_config_file, Powerline from powerline.lib.config import load_json_config @@ -436,9 +438,9 @@ main_spec = (Spec( term_truecolor=Spec().type(bool).optional(), # Python is capable of loading from zip archives. Thus checking path # only for existence of the path, not for it being a directory - paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(value.value))), + paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))), lambda value: 'path does not exist: {0}'.format(value)).optional(), - log_file=Spec().type(str).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(value))), + log_file=Spec().type(str).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(os.path.expanduser(value)))), lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))).optional(), log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)), lambda value: 'unknown debugging level {0}'.format(value)).optional(), From 3cf04f3aabf5b564da12a826dd2ef874e2f68b49 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 27 Jun 2013 19:24:28 +0400 Subject: [PATCH 0735/1472] Support low values of line2byte Fixes #567 --- powerline/segments/vim.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 5ba63588..f6dc7119 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -177,6 +177,8 @@ def file_size(pl, suffix='B', si_prefix=False): # Note: returns file size in &encoding, not in &fileencoding. But returned # size is updated immediately; and it is valid for any buffer file_size = vim_funcs['line2byte'](len(vim.current.buffer) + 1) - 1 + if file_size < 0: + file_size = 0 return humanize_bytes(file_size, suffix, si_prefix) From 72b082a5103ecf790383c66f3e2570028562afee Mon Sep 17 00:00:00 2001 From: Jack Zhou Date: Tue, 18 Jun 2013 16:46:46 -0700 Subject: [PATCH 0736/1472] Change [DETACHED HEAD] message to a short hash of the detached head. --- powerline/lib/vcs/git.py | 2 +- tests/test_lib.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 09c029c6..a8514ad8 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -17,7 +17,7 @@ def branch_name_from_config_file(directory, config_file): m = _ref_pat.match(raw) if m is not None: return m.group(1).decode('utf-8', 'replace') - return '[DETACHED HEAD]' + return raw[:7] def get_branch_name(base_dir): head = os.path.join(base_dir, '.git', 'HEAD') diff --git a/tests/test_lib.py b/tests/test_lib.py index c3f89525..18326d72 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -5,6 +5,7 @@ from powerline.lib.vcs import guess from subprocess import call, PIPE import os import sys +import re from functools import partial from tests import TestCase, SkipTest @@ -130,10 +131,17 @@ class TestVCS(TestCase): while time.time() - st < 1: # Give inotify time to deliver events ans = repo.branch() - if ans == q: - break + if hasattr(q, '__call__'): + if q(ans): + break + else: + if ans == q: + break time.sleep(0.01) - self.assertEqual(ans, q) + if hasattr(q, '__call__'): + self.assertTrue(q(ans)) + else: + self.assertEqual(ans, q) def test_git(self): repo = guess(path=GIT_REPO) @@ -167,7 +175,7 @@ class TestVCS(TestCase): call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) self.do_branch_rename_test(repo, 'branch2') call(['git', 'checkout', '-q', '--detach', 'branch1'], cwd=GIT_REPO) - self.do_branch_rename_test(repo, '[DETACHED HEAD]') + self.do_branch_rename_test(repo, lambda b: re.match(r'^[a-f0-9]+$', b)) if use_mercurial: def test_mercurial(self): From 17435ecf90f7b5a368b9fbeab1d0704d2652dcc2 Mon Sep 17 00:00:00 2001 From: mwcz Date: Mon, 10 Jun 2013 15:33:27 -0400 Subject: [PATCH 0737/1472] Fix documented path to fontpatcher.py --- docs/source/fontpatching.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index c04d4985..b1d0f254 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -42,7 +42,7 @@ Code point Glyph Description Usage ===== -The font patcher is located at :file:`powerline/fontpatcher/fontpatcher.py`. +The font patcher is located at :file:`powerline/font/fontpatcher.py`. It requires Python 2.7 and FontForge compiled with Python bindings to work. Patched fonts are renamed by default (" for Powerline" is added to the font From 8d9fa13797bd4e9e91757ebf9edbb0c209280dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Fri, 28 Jun 2013 14:13:57 +0200 Subject: [PATCH 0738/1472] Update Arch Linux packages to use updated VCS syntax Closes #479 --- .../archlinux/python-powerline-git/PKGBUILD | 38 +++++++------------ .../archlinux/python2-powerline-git/PKGBUILD | 38 +++++++------------ 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 8053c5dc..5bc77d5e 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -1,7 +1,9 @@ # Maintainer: Kim Silkebækken -pkgname=python-powerline-git -pkgver=20130412 +_gitname=powerline +_gitbranch=develop +pkgname="python-${_gitname}-git" +pkgver=793.4ee5072 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -15,33 +17,19 @@ optdepends=('python-psutil: improved system information' 'gvim: vim compiled with Python support') conflicts=('python2-powerline-git' 'powerline-git') -install='powerline.install' -source=() +install="${_gitname}.install" +source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" + "${install}") +sha256sums=('SKIP' + '7b1257cdacce60e19280f7d918e5f3aa6f13b519dff16ecc6f732c881ef63ca1') -_gitroot="https://github.com/Lokaltog/powerline.git" -_gitname="powerline" -_gitbranch="develop" - -build() { - cd "${srcdir}" - - msg "Connecting to GitHub..." - - if [ -d "${srcdir}/${_gitname}" ]; then - cd "${_gitname}" - git pull origin "${_gitbranch}" - msg "The local files are updated." - else - git clone "${_gitroot}" - cd "${_gitname}" - git checkout "${_gitbranch}" - fi - - msg "Git checkout done or server timeout." +pkgver() { + cd "${_gitname}" + echo "$(git rev-list --count ${_gitbranch}).$(git rev-parse --short ${_gitbranch})" } package() { - cd "${srcdir}/${_gitname}" + cd "${_gitname}" python setup.py install --root="${pkgdir}" --optimize=1 || return 1 msg2 "Installing fonts..." diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index e6db1ffb..680916cd 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -1,7 +1,9 @@ # Maintainer: Kim Silkebækken -pkgname=python2-powerline-git -pkgver=20130412 +_gitname=powerline +_gitbranch=develop +pkgname="python2-${_gitname}-git" +pkgver=793.4ee5072 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' @@ -16,33 +18,19 @@ optdepends=('python2-psutil: improved system information' 'gvim: vim compiled with Python support') conflicts=('python-powerline-git') replaces=('powerline-git') -install='powerline.install' -source=() +install="${_gitname}.install" +source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" + "${install}") +sha256sums=('SKIP' + 'e8ab7fb51ac7244bfad973a999c9333ba4334fa391aa890489cf8c8f1211c94f') -_gitroot="https://github.com/Lokaltog/powerline.git" -_gitname="powerline" -_gitbranch="develop" - -build() { - cd "${srcdir}" - - msg "Connecting to GitHub..." - - if [ -d "${srcdir}/${_gitname}" ]; then - cd "${_gitname}" - git pull origin "${_gitbranch}" - msg "The local files are updated." - else - git clone "${_gitroot}" - cd "${_gitname}" - git checkout "${_gitbranch}" - fi - - msg "Git checkout done or server timeout." +pkgver() { + cd "${_gitname}" + echo "$(git rev-list --count ${_gitbranch}).$(git rev-parse --short ${_gitbranch})" } package() { - cd "${srcdir}/${_gitname}" + cd "${_gitname}" python2 setup.py install --root="${pkgdir}" --optimize=1 || return 1 msg2 "Installing fonts..." From 1970d0787cb6e2987a5506c21447a2747a91c07e Mon Sep 17 00:00:00 2001 From: Sam Stuewe Date: Fri, 28 Jun 2013 10:43:12 -0500 Subject: [PATCH 0739/1472] Clean up package function for python2 The "|| return 1" is very old syntax and is no longer needed. And, the ``install`` command can handle directory creation and file installation in the same command which cuts a great deal of unnecessary commands out. Also, remember, msg2s in the package function will be printed during the making of the package, not during the installation. Thus, instead of msg2s (as this really would serve only as documentation), comments should be preferred. --- .../archlinux/python2-powerline-git/PKGBUILD | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 680916cd..735b6276 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -31,29 +31,23 @@ pkgver() { package() { cd "${_gitname}" - python2 setup.py install --root="${pkgdir}" --optimize=1 || return 1 + python2 setup.py install --root="${pkgdir}" --optimize=1 - msg2 "Installing fonts..." - install -dm755 "${pkgdir}/usr/share/fonts/OTF/" - install -dm755 "${pkgdir}/etc/fonts/conf.avail" + # Fonts install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" - install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" + install -Dm644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" + install -Dm644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - msg2 "Installing vim plugin..." - install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" - install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + # Vim Plugin + install -Dm644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" - msg2 "Installing zsh plugin..." - install -dm755 "${pkgdir}/usr/share/zsh/site-contrib" - install -m644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" + # Zsh Plugin + install -Dm644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" - msg2 "Installing tmux configuration..." - install -dm755 "${pkgdir}/usr/share/tmux" - install -m644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" + # Tmux Configuration + install -Dm644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" - msg2 "Installing license..." - install -dm755 "${pkgdir}/usr/share/licenses/${pkgname}" - install -m644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" + # License + install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } From b0c5c059240c5655e141041a0dbe7f28f415e3d9 Mon Sep 17 00:00:00 2001 From: Sam Stuewe Date: Fri, 28 Jun 2013 10:48:01 -0500 Subject: [PATCH 0740/1472] Clean up package function for python3 ``|| return 1`` is very old syntax and is no longer needed. And, the ``install`` command can handle directory creation and file installation in the same command which cuts a great deal of unnecessary commands out. Also, remember, ``msg2`` in the package function will be printed during the making of the package, not during the installation. Thus, instead of ``msg2`` (as they really serve only as documentation), comments should be preferred. --- .../archlinux/python-powerline-git/PKGBUILD | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 5bc77d5e..8918ade6 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -30,29 +30,23 @@ pkgver() { package() { cd "${_gitname}" - python setup.py install --root="${pkgdir}" --optimize=1 || return 1 + python setup.py install --root="${pkgdir}" --optimize=1 - msg2 "Installing fonts..." - install -dm755 "${pkgdir}/usr/share/fonts/OTF/" - install -dm755 "${pkgdir}/etc/fonts/conf.avail" + # Fonts install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -m644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" - install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" + install -Dm644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" + install -Dm644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - msg2 "Installing vim plugin..." - install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" - install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" + # Vim Plugin + install -Dm644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" - msg2 "Installing zsh plugin..." - install -dm755 "${pkgdir}/usr/share/zsh/site-contrib" - install -m644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" + # Zsh Plugin + install -Dm644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" - msg2 "Installing tmux configuration..." - install -dm755 "${pkgdir}/usr/share/tmux" - install -m644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" + # Tmux Configuration + install -Dm644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" - msg2 "Installing license..." - install -dm755 "${pkgdir}/usr/share/licenses/${pkgname}" - install -m644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" + # License + install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } From 79e7640bba69dd00713324be4461c482f237494f Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Fri, 28 Jun 2013 23:10:36 -0400 Subject: [PATCH 0741/1472] arch: add provides=('powerline') to PKGBUILDs. --- packages/archlinux/python-powerline-git/PKGBUILD | 1 + packages/archlinux/python2-powerline-git/PKGBUILD | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 5bc77d5e..c369879c 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -15,6 +15,7 @@ optdepends=('python-psutil: improved system information' 'python-pygit2: improved git support' 'zsh: better shell prompt' 'gvim: vim compiled with Python support') +provides=('powerline') conflicts=('python2-powerline-git' 'powerline-git') install="${_gitname}.install" diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 680916cd..1c7beb2d 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -16,6 +16,7 @@ optdepends=('python2-psutil: improved system information' 'mercurial: improved mercurial support' 'zsh: better shell prompt' 'gvim: vim compiled with Python support') +provides=('powerline') conflicts=('python-powerline-git') replaces=('powerline-git') install="${_gitname}.install" From ffb29d60d8cc773275aa537ccd17dd7157cb2c5d Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Fri, 28 Jun 2013 22:49:22 -0400 Subject: [PATCH 0742/1472] arch: add fontpatcher to archlinux/python2-powerline. --- .../archlinux/python2-powerline-git/.gitignore | 1 + packages/archlinux/python2-powerline-git/PKGBUILD | 15 ++++++++++++++- .../python2-powerline-git/fontpatcher.py.patch | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/archlinux/python2-powerline-git/fontpatcher.py.patch diff --git a/packages/archlinux/python2-powerline-git/.gitignore b/packages/archlinux/python2-powerline-git/.gitignore index 4ef5aeeb..cc7d5336 100644 --- a/packages/archlinux/python2-powerline-git/.gitignore +++ b/packages/archlinux/python2-powerline-git/.gitignore @@ -2,3 +2,4 @@ !.gitignore !PKGBUILD !*.install +!*.patch diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 1c7beb2d..f05a0042 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -21,15 +21,22 @@ conflicts=('python-powerline-git') replaces=('powerline-git') install="${_gitname}.install" source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" + "fontpatcher.py.patch" "${install}") sha256sums=('SKIP' - 'e8ab7fb51ac7244bfad973a999c9333ba4334fa391aa890489cf8c8f1211c94f') + '85576097662ab4203968b5fba1d59ec2653a390cdd4db9cee8ffa7bd4c5a7253' + 'e8ab7fb51ac7244bfad973a999c9333ba4334fa391aa890489cf8c8f1211c94f') pkgver() { cd "${_gitname}" echo "$(git rev-list --count ${_gitbranch}).$(git rev-parse --short ${_gitbranch})" } +prepare() { + cd "${srcdir}/${_gitname}" + patch -p1 < ../fontpatcher.py.patch +} + package() { cd "${_gitname}" python2 setup.py install --root="${pkgdir}" --optimize=1 || return 1 @@ -42,6 +49,12 @@ package() { install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" + msg2 "Installing fontpatcher..." + install -dm755 "${pkgdir}/usr/share/$_gitname" + install -dm755 "${pkgdir}/usr/bin" + install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" + install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" + msg2 "Installing vim plugin..." install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" diff --git a/packages/archlinux/python2-powerline-git/fontpatcher.py.patch b/packages/archlinux/python2-powerline-git/fontpatcher.py.patch new file mode 100644 index 00000000..fb394ce2 --- /dev/null +++ b/packages/archlinux/python2-powerline-git/fontpatcher.py.patch @@ -0,0 +1,13 @@ +diff --git a/font/fontpatcher.py b/font/fontpatcher.py +index e2bbf2a..aa172f6 100755 +--- a/font/fontpatcher.py ++++ b/font/fontpatcher.py +@@ -21,7 +21,7 @@ except ImportError: + parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') + parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) + parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') +-parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) ++parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='/usr/share/powerline/fontpatcher-symbols.sfd', type=argparse.FileType('rb')) + args = parser.parse_args() + + From 01560ae71436a5c2a29faf57383bad895af76337 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Fri, 28 Jun 2013 22:53:02 -0400 Subject: [PATCH 0743/1472] arch: add fontpatcher to archlinux/python-powerline. --- .../archlinux/python-powerline-git/.gitignore | 1 + packages/archlinux/python-powerline-git/PKGBUILD | 15 ++++++++++++++- .../python-powerline-git/fontpatcher.py.patch | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/archlinux/python-powerline-git/fontpatcher.py.patch diff --git a/packages/archlinux/python-powerline-git/.gitignore b/packages/archlinux/python-powerline-git/.gitignore index 4ef5aeeb..cc7d5336 100644 --- a/packages/archlinux/python-powerline-git/.gitignore +++ b/packages/archlinux/python-powerline-git/.gitignore @@ -2,3 +2,4 @@ !.gitignore !PKGBUILD !*.install +!*.patch diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index c369879c..1395d37d 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -20,15 +20,22 @@ conflicts=('python2-powerline-git' 'powerline-git') install="${_gitname}.install" source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" + "fontpatcher.py.patch" "${install}") sha256sums=('SKIP' - '7b1257cdacce60e19280f7d918e5f3aa6f13b519dff16ecc6f732c881ef63ca1') + '85576097662ab4203968b5fba1d59ec2653a390cdd4db9cee8ffa7bd4c5a7253' + '7b1257cdacce60e19280f7d918e5f3aa6f13b519dff16ecc6f732c881ef63ca1') pkgver() { cd "${_gitname}" echo "$(git rev-list --count ${_gitbranch}).$(git rev-parse --short ${_gitbranch})" } +prepare() { + cd "${srcdir}/${_gitname}" + patch -p1 < ../fontpatcher.py.patch +} + package() { cd "${_gitname}" python setup.py install --root="${pkgdir}" --optimize=1 || return 1 @@ -41,6 +48,12 @@ package() { install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" + msg2 "Installing fontpatcher..." + install -dm755 "${pkgdir}/usr/share/$_gitname" + install -dm755 "${pkgdir}/usr/bin" + install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" + install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" + msg2 "Installing vim plugin..." install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" diff --git a/packages/archlinux/python-powerline-git/fontpatcher.py.patch b/packages/archlinux/python-powerline-git/fontpatcher.py.patch new file mode 100644 index 00000000..fb394ce2 --- /dev/null +++ b/packages/archlinux/python-powerline-git/fontpatcher.py.patch @@ -0,0 +1,13 @@ +diff --git a/font/fontpatcher.py b/font/fontpatcher.py +index e2bbf2a..aa172f6 100755 +--- a/font/fontpatcher.py ++++ b/font/fontpatcher.py +@@ -21,7 +21,7 @@ except ImportError: + parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') + parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) + parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') +-parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) ++parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='/usr/share/powerline/fontpatcher-symbols.sfd', type=argparse.FileType('rb')) + args = parser.parse_args() + + From 4083cf4cc0c8c7ca87e74950a0e5c2ab8a2a3695 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jul 2013 08:38:04 -0400 Subject: [PATCH 0744/1472] arch: break package() into separate packages. --- .../archlinux/python-powerline-git/PKGBUILD | 24 ++++++++++++------- .../archlinux/python2-powerline-git/PKGBUILD | 24 ++++++++++++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 1395d37d..c4706ed9 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -2,7 +2,8 @@ _gitname=powerline _gitbranch=develop -pkgname="python-${_gitname}-git" +pkgname=("python-${_gitname}-git" "powerline-fontpatcher") +pkgbase=powerline pkgver=793.4ee5072 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' @@ -36,7 +37,20 @@ prepare() { patch -p1 < ../fontpatcher.py.patch } -package() { +package_powerline-fontpatcher() { + pkgdesc='OTF/TTF font patcher for powerline symbols' + depends=('python2' 'fontforge') + + cd "${_gitname}" + + msg2 "Installing fontpatcher..." + install -dm755 "${pkgdir}/usr/share/$_gitname" + install -dm755 "${pkgdir}/usr/bin" + install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" + install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" +} + +package_python-powerline-git() { cd "${_gitname}" python setup.py install --root="${pkgdir}" --optimize=1 || return 1 @@ -48,12 +62,6 @@ package() { install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - msg2 "Installing fontpatcher..." - install -dm755 "${pkgdir}/usr/share/$_gitname" - install -dm755 "${pkgdir}/usr/bin" - install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" - install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" - msg2 "Installing vim plugin..." install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index f05a0042..b6994b06 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -2,7 +2,8 @@ _gitname=powerline _gitbranch=develop -pkgname="python2-${_gitname}-git" +pkgname=("python2-${_gitname}-git" "powerline-fontpatcher") +pkgbase=powerline pkgver=793.4ee5072 pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' @@ -37,7 +38,20 @@ prepare() { patch -p1 < ../fontpatcher.py.patch } -package() { +package_powerline-fontpatcher() { + pkgdesc='OTF/TTF font patcher for powerline symbols' + depends=('python2' 'fontforge') + + cd "${_gitname}" + + msg2 "Installing fontpatcher..." + install -dm755 "${pkgdir}/usr/share/$_gitname" + install -dm755 "${pkgdir}/usr/bin" + install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" + install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" +} + +package_python2-powerline-git() { cd "${_gitname}" python2 setup.py install --root="${pkgdir}" --optimize=1 || return 1 @@ -49,12 +63,6 @@ package() { install -m644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - msg2 "Installing fontpatcher..." - install -dm755 "${pkgdir}/usr/share/$_gitname" - install -dm755 "${pkgdir}/usr/bin" - install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" - install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" - msg2 "Installing vim plugin..." install -dm755 "${pkgdir}/usr/share/vim/vimfiles/plugin" install -m644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" From 2be3059ebd8da58caf09fe87ac4903b75364d47f Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Tue, 2 Jul 2013 08:49:09 -0400 Subject: [PATCH 0745/1472] arch: update pkgrel to 2. --- packages/archlinux/python-powerline-git/PKGBUILD | 4 ++-- packages/archlinux/python2-powerline-git/PKGBUILD | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index c4706ed9..a96d77ca 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -4,8 +4,8 @@ _gitname=powerline _gitbranch=develop pkgname=("python-${_gitname}-git" "powerline-fontpatcher") pkgbase=powerline -pkgver=793.4ee5072 -pkgrel=1 +pkgver=800.c1ae7f3 +pkgrel=2 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index b6994b06..48236d4c 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -4,8 +4,8 @@ _gitname=powerline _gitbranch=develop pkgname=("python2-${_gitname}-git" "powerline-fontpatcher") pkgbase=powerline -pkgver=793.4ee5072 -pkgrel=1 +pkgver=800.c1ae7f3 +pkgrel=2 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') From 52c6bff67fe84d6ac0b8148db2207dd97c0b654e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 3 Jul 2013 09:40:05 +0530 Subject: [PATCH 0746/1472] Dont log inotify missing error when using branch coloring The error message about inotify being missing when branch coloring is enabled was being logged for every directory and for every invocation of powerline in a shell, without powerline-daemon. Dont log it, since the log is printed to stderr when using powerline in a shell without powerline-daemon. Also improve the error message on OS X. Fixes #578 --- powerline/lib/inotify.py | 2 ++ powerline/lib/tree_watcher.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index b6edade3..1e143f5f 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -26,6 +26,8 @@ def load_inotify(): # if the one chosen by ctypes is compatible with the currently # loaded one. raise INotifyError('INotify not available on windows') + if sys.platform == 'darwin': + raise INotifyError('INotify not available on OS X') import ctypes if not hasattr(ctypes, 'c_ssize_t'): raise INotifyError('You need python >= 2.7 to use inotify') diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index a7983289..f283e10b 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -162,7 +162,7 @@ class TreeWatcher(object): try: w = INotifyTreeWatcher(path, ignore_event=ignore_event) except (INotifyError, DirTooLarge) as e: - if logger is not None: + if logger is not None and not isinstance(e, INotifyError): logger.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) w = DummyTreeWatcher(path) self.watches[path] = w From 82842e015cda89fb48b1256d34c53f964e2fa151 Mon Sep 17 00:00:00 2001 From: Ian Yang Date: Tue, 9 Jul 2013 09:32:01 +0800 Subject: [PATCH 0747/1472] Set different window name foreground based on activity and bell status. --- powerline/bindings/tmux/powerline.conf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index ba75336e..22cc9b6c 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -8,6 +8,11 @@ set -g status-left-length 20 set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(eval $POWERLINE_COMMAND tmux left)' set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 -set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[fg=colour249]#W " +set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" +set-window-option -g window-status-fg colour249 +set-window-option -g window-status-activity-attr none +set-window-option -g window-status-bell-attr none +set-window-option -g window-status-activity-fg yellow +set-window-option -g window-status-bell-fg red # vim: ft=tmux From 24cda1d947cb91359363f6f7e38bb48ca13ddb52 Mon Sep 17 00:00:00 2001 From: Trevor Murphy Date: Wed, 8 May 2013 23:28:24 -0400 Subject: [PATCH 0748/1472] Add a battery info segment. Default colors range from red (full battery) to white (no battery) but can be changed via the `battery' and `battery_gradient' settings. Default presentation is a formatted percentage string (with keyword `batt'). The `gamify' setting changes this to a sequence of video game hearts. Number of steps from 100% to 0% / number of video game heart icons is controlled by the `steps' setting (default 5). --- powerline/config_files/colors.json | 4 ++ .../colorschemes/tmux/default.json | 4 +- powerline/segments/common.py | 51 +++++++++++++++++++ tests/test_segments.py | 36 +++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index c64b74c9..5b8a7645 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -101,6 +101,10 @@ "blue_red": [ [39, 74, 68, 67, 103, 97, 96, 132, 131, 167, 203, 197], ["19b4fe", "1bb2fc", "1db1fa", "1faff8", "22aef6", "24adf4", "26abf2", "29aaf0", "2ba9ee", "2da7ec", "30a6ea", "32a5e8", "34a3e6", "36a2e4", "39a0e2", "3b9fe1", "3d9edf", "409cdd", "429bdb", "449ad9", "4798d7", "4997d5", "4b96d3", "4d94d1", "5093cf", "5292cd", "5490cb", "578fc9", "598dc7", "5b8cc6", "5e8bc4", "6089c2", "6288c0", "6487be", "6785bc", "6984ba", "6b83b8", "6e81b6", "7080b4", "727eb2", "757db0", "777cae", "797aac", "7b79ab", "7e78a9", "8076a7", "8275a5", "8574a3", "8772a1", "89719f", "8c709d", "8e6e9b", "906d99", "926b97", "956a95", "976993", "996791", "9c668f", "9e658e", "a0638c", "a3628a", "a56188", "a75f86", "a95e84", "ac5c82", "ae5b80", "b05a7e", "b3587c", "b5577a", "b75678", "ba5476", "bc5374", "be5273", "c05071", "c34f6f", "c54e6d", "c74c6b", "ca4b69", "cc4967", "ce4865", "d14763", "d34561", "d5445f", "d7435d", "da415b", "dc4059", "de3f58", "e13d56", "e33c54", "e53a52", "e83950", "ea384e", "ec364c", "ee354a", "f13448", "f33246", "f53144", "f83042", "fa2e40"] + ], + "white_red": [ + [231, 223, 216, 209, 196], + ["ffffff", "fffe61", "fffcc4", "fffb28", "fff98b", "fff7ef", "fff651", "fff4b4", "fff318", "fff17b", "ffefdf", "ffee41", "ffeca4", "ffeb08", "ffe96b", "ffe7cf", "ffe631", "ffe494", "ffe2f8", "ffe15b", "ffdfbf", "ffde21", "ffdc84", "ffdae8", "ffd94b", "ffd7af", "ffd602", "ffd455", "ffd2aa", "ffd0fd", "ffcf50", "ffcda5", "ffcbf8", "ffca4b", "ffc8a0", "ffc6f3", "ffc546", "ffc39b", "ffc1ee", "ffc041", "ffbe96", "ffbce9", "ffbb3c", "ffb991", "ffb7e4", "ffb637", "ffb48c", "ffb2df", "ffb132", "ffaf87", "ffadda", "ffac2d", "ffaa82", "ffa8d5", "ffa728", "ffa57d", "ffa3d0", "ffa223", "ffa078", "ff9ecb", "ff9d1e", "ff9b73", "ff99c6", "ff9819", "ff966e", "ff94c1", "ff9314", "ff9169", "ff8fbc", "ff8e0f", "ff8c64", "ff8ab7", "ff890a", "ff875f", "ff81f4", "ff7c8a", "ff771f", "ff71b5", "ff6c4c", "ff66e1", "ff6177", "ff5c0c", "ff56a2", "ff5139", "ff4bce", "ff4664", "ff40f9", "ff3b8f", "ff3626", "ff30bb", "ff2b51", "ff25e6", "ff207c", "ff1b13", "ff15a8", "ff103e", "ff0ad3", "ff0569", "ff0000"] ] } } diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index 35686d32..9d1933f9 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -19,6 +19,8 @@ "network_load": { "fg": "gray8", "bg": "gray0" }, "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" } + "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, + "battery": { "fg": "gray8", "bg": "gray0" }, + "battery_gradient": { "fg": "white_red", "bg": "gray0" } } } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 094c2c1b..9fdc2205 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1003,3 +1003,54 @@ class NowPlayingSegment(object): 'total': now_playing[4], } now_playing = NowPlayingSegment() + + +if os.path.exists('/sys/class/power_supply/BAT0/capacity'): + def _get_capacity(): + with open('/sys/class/power_supply/BAT0/capacity', 'r') as f: + return int(float(f.readline().split()[0])) +else: + def _get_capacity(): + raise NotImplementedError + + +def battery(pl, format='{batt:3.0%}', steps=5, gamify=False): + '''Return battery charge status. + + :param int steps: + number of discrete steps to show between 0% and 100% capacity + :param bool gamify: + measure in hearts (♥) instead of percentages + + Highlight groups used: ``battery_gradient`` (gradient), ``battery``. + ''' + try: + capacity = _get_capacity() + except NotImplementedError: + pl.warn('Unable to get battery capacity.') + return None + ret = [] + denom = int(steps) + numer = int(denom * capacity / 100) + full_heart = '♥' + if gamify: + ret.append({ + 'contents': full_heart * numer, + 'draw_soft_divider': False, + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 99 + }) + ret.append({ + 'contents': full_heart * (denom - numer), + 'draw_soft_divider': False, + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 1 + }) + else: + batt = numer / float(denom) + ret.append({ + 'contents': format.format(batt=batt), + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': batt * 100 + }) + return ret diff --git a/tests/test_segments.py b/tests/test_segments.py index befad178..dd028116 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -340,6 +340,42 @@ class TestCommon(TestCase): # TODO pass + def test_battery(self): + pl = Pl() + + def _get_capacity(): + return 86 + + with replace_attr(common, '_get_capacity', _get_capacity): + self.assertEqual(common.battery(pl=pl), [{ + 'contents': '80%', + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 80.0 + }]) + self.assertEqual(common.battery(pl=pl, format='{batt:.2f}'), [{ + 'contents': '0.80', + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 80.0 + }]) + self.assertEqual(common.battery(pl=pl, steps=7), [{ + 'contents': '86%', + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 85.71428571428571 + }]) + self.assertEqual(common.battery(pl=pl, gamify=True), [ + { + 'contents': '♥♥♥♥', + 'draw_soft_divider': False, + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 99 + }, + { + 'contents': '♥', + 'draw_soft_divider': False, + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 1 + } + ]) class TestVim(TestCase): def test_mode(self): From d66806e5064ab176cafc25667770ff5d4388c696 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 14 Jul 2013 15:04:34 +0530 Subject: [PATCH 0749/1472] Fix latest psutil breaking network segment --- powerline/segments/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 0d69aa08..9fce0eb1 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -497,7 +497,10 @@ try: import psutil def _get_bytes(interface): - io_counters = psutil.network_io_counters(pernic=True) + try: + io_counters = psutil.net_io_counters(pernic=True) + except AttributeError: + io_counters = psutil.network_io_counters(pernic=True) if_io = io_counters.get(interface) if not if_io: return None From d1cdf860d1b35b010f8f6abf32ed4c1dd3fbdb54 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 20 Jul 2013 18:44:18 +0530 Subject: [PATCH 0750/1472] Fix #604 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 3d0fcb7e..9a78bcb6 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet - +from __future__ import unicode_literals import os import sys @@ -8,7 +8,7 @@ from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) try: - README = open(os.path.join(here, 'README.rst')).read() + README = open(os.path.join(here, 'README.rst'), 'rb').read().decode('utf-8') except IOError: README = '' From 99e0cf57b87393982fada1a534bd024ef741e208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 31 Jul 2013 15:24:40 +0200 Subject: [PATCH 0751/1472] Improve Arch Linux packages Use simplified syntax and messages, fix package names, ref HalosGhost/patch-{2,3}. --- .../archlinux/python-powerline-git/PKGBUILD | 30 +++++++++---------- .../archlinux/python2-powerline-git/PKGBUILD | 30 +++++++++---------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 5de13cd2..2d9141b7 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -2,10 +2,10 @@ _gitname=powerline _gitbranch=develop -pkgname=("python-${_gitname}-git" "powerline-fontpatcher") +pkgname=("python-${_gitname}-git" "powerline-fontpatcher-git") pkgbase=powerline -pkgver=800.c1ae7f3 -pkgrel=2 +pkgver=808.5c88c0a +pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') @@ -13,16 +13,16 @@ arch=('any') depends=('python>=3.2') makedepends=('git' 'python-distribute') optdepends=('python-psutil: improved system information' - 'python-pygit2: improved git support' - 'zsh: better shell prompt' - 'gvim: vim compiled with Python support') + 'python-pygit2: improved git support' + 'zsh: better shell prompt' + 'gvim: vim compiled with Python support') provides=('powerline') conflicts=('python2-powerline-git' - 'powerline-git') + 'powerline-git') install="${_gitname}.install" source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" - "fontpatcher.py.patch" - "${install}") + "fontpatcher.py.patch" + "${install}") sha256sums=('SKIP' '85576097662ab4203968b5fba1d59ec2653a390cdd4db9cee8ffa7bd4c5a7253' '7b1257cdacce60e19280f7d918e5f3aa6f13b519dff16ecc6f732c881ef63ca1') @@ -37,17 +37,15 @@ prepare() { patch -p1 < ../fontpatcher.py.patch } -package_powerline-fontpatcher() { - pkgdesc='OTF/TTF font patcher for powerline symbols' +package_powerline-fontpatcher-git() { + pkgdesc='OTF/TTF font patcher for Powerline symbols' depends=('python2' 'fontforge') cd "${_gitname}" - msg2 "Installing fontpatcher..." - install -dm755 "${pkgdir}/usr/share/$_gitname" - install -dm755 "${pkgdir}/usr/bin" - install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" - install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" + # Font patcher + install -Dm755 "font/fontpatcher.py" "${pkgdir}/usr/bin/powerline-fontpatcher" + install -Dm644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname/fontpatcher-symbols.sfd" } package_python-powerline-git() { diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 3efcb81f..796ac74b 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -2,10 +2,10 @@ _gitname=powerline _gitbranch=develop -pkgname=("python2-${_gitname}-git" "powerline-fontpatcher") +pkgname=("python2-${_gitname}-git" "powerline-fontpatcher-git") pkgbase=powerline -pkgver=800.c1ae7f3 -pkgrel=2 +pkgver=808.5c88c0a +pkgrel=1 pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') @@ -13,17 +13,17 @@ arch=('any') depends=('python2>=2.6') makedepends=('git' 'python2-distribute') optdepends=('python2-psutil: improved system information' - 'python2-pygit2: improved git support' - 'mercurial: improved mercurial support' - 'zsh: better shell prompt' - 'gvim: vim compiled with Python support') + 'python2-pygit2: improved git support' + 'mercurial: improved mercurial support' + 'zsh: better shell prompt' + 'gvim: vim compiled with Python support') provides=('powerline') conflicts=('python-powerline-git') replaces=('powerline-git') install="${_gitname}.install" source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" - "fontpatcher.py.patch" - "${install}") + "fontpatcher.py.patch" + "${install}") sha256sums=('SKIP' '85576097662ab4203968b5fba1d59ec2653a390cdd4db9cee8ffa7bd4c5a7253' 'e8ab7fb51ac7244bfad973a999c9333ba4334fa391aa890489cf8c8f1211c94f') @@ -38,17 +38,15 @@ prepare() { patch -p1 < ../fontpatcher.py.patch } -package_powerline-fontpatcher() { - pkgdesc='OTF/TTF font patcher for powerline symbols' +package_powerline-fontpatcher-git() { + pkgdesc='OTF/TTF font patcher for Powerline symbols' depends=('python2' 'fontforge') cd "${_gitname}" - msg2 "Installing fontpatcher..." - install -dm755 "${pkgdir}/usr/share/$_gitname" - install -dm755 "${pkgdir}/usr/bin" - install -m755 "font/fontpatcher.py" "${pkgdir}/usr/bin/" - install -m644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname" + # Font patcher + install -Dm755 "font/fontpatcher.py" "${pkgdir}/usr/bin/powerline-fontpatcher" + install -Dm644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/${_gitname}/fontpatcher-symbols.sfd" } package_python2-powerline-git() { From ad4a1b546a16f719001ed76002e81bd987495d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 31 Jul 2013 16:11:20 +0200 Subject: [PATCH 0752/1472] Fix Arch Linux AUR package upload issues Some workarounds have been added so that AUR correctly parses the package name and dependencies when the package is uploaded. --- .../archlinux/python-powerline-git/PKGBUILD | 18 +++++++++-------- .../archlinux/python2-powerline-git/PKGBUILD | 20 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index 2d9141b7..b49dffbf 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -2,20 +2,15 @@ _gitname=powerline _gitbranch=develop -pkgname=("python-${_gitname}-git" "powerline-fontpatcher-git") +pkgname="python-${_gitname}-git" # Workaround for missing split package support in AUR +true && pkgname=("python-${_gitname}-git" "${_gitname}-fontpatcher-git") pkgbase=powerline -pkgver=808.5c88c0a +pkgver=822.225ac48 pkgrel=1 -pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') arch=('any') -depends=('python>=3.2') makedepends=('git' 'python-distribute') -optdepends=('python-psutil: improved system information' - 'python-pygit2: improved git support' - 'zsh: better shell prompt' - 'gvim: vim compiled with Python support') provides=('powerline') conflicts=('python2-powerline-git' 'powerline-git') @@ -49,6 +44,13 @@ package_powerline-fontpatcher-git() { } package_python-powerline-git() { + pkgdesc='The ultimate statusline/prompt utility.' + depends=('python>=3.2') + optdepends=('python-psutil: improved system information' + 'python-pygit2: improved git support' + 'zsh: better shell prompt' + 'gvim: vim compiled with Python support') + cd "${_gitname}" python setup.py install --root="${pkgdir}" --optimize=1 diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index 796ac74b..e01e70ff 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -2,21 +2,15 @@ _gitname=powerline _gitbranch=develop -pkgname=("python2-${_gitname}-git" "powerline-fontpatcher-git") +pkgname="python2-${_gitname}-git" # Workaround for missing split package support in AUR +true && pkgname=("python2-${_gitname}-git" "${_gitname}-fontpatcher-git") pkgbase=powerline -pkgver=808.5c88c0a +pkgver=822.225ac48 pkgrel=1 -pkgdesc='The ultimate statusline/prompt utility.' url='https://github.com/Lokaltog/powerline' license=('MIT') arch=('any') -depends=('python2>=2.6') makedepends=('git' 'python2-distribute') -optdepends=('python2-psutil: improved system information' - 'python2-pygit2: improved git support' - 'mercurial: improved mercurial support' - 'zsh: better shell prompt' - 'gvim: vim compiled with Python support') provides=('powerline') conflicts=('python-powerline-git') replaces=('powerline-git') @@ -50,6 +44,14 @@ package_powerline-fontpatcher-git() { } package_python2-powerline-git() { + pkgdesc='The ultimate statusline/prompt utility.' + depends=('python2>=2.6') + optdepends=('python2-psutil: improved system information' + 'python2-pygit2: improved git support' + 'mercurial: improved mercurial support' + 'zsh: better shell prompt' + 'gvim: vim compiled with Python support') + cd "${_gitname}" python2 setup.py install --root="${pkgdir}" --optimize=1 From aa05f22652b8b7f0ffef776ea39ec88381a4e75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 5 Aug 2013 10:56:27 +0200 Subject: [PATCH 0753/1472] Update README --- README.rst | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index a5a4fee6..595bd29f 100644 --- a/README.rst +++ b/README.rst @@ -9,16 +9,53 @@ Powerline :target: `travis-build-status`_ :alt: Build status -This is the upcoming version of Powerline, implemented in Python. The -project is currently in a stable beta and almost ready for release. +.. _travis-build-status: https://travis-ci.org/Lokaltog/powerline + +**Powerline is a statusline plugin for vim, and provides statuslines and +prompts for several other applications, including zsh, bash, tmux, IPython, +Awesome and Qtile.** + +Features +-------- + +* **Extensible and feature rich, written in Python.** Powerline was + completely rewritten in Python to get rid of as much vimscript as + possible. This has allowed much better extensibility, leaner and better + config files, and a structured, object-oriented codebase with no mandatory + third-party dependencies other than a Python interpreter. +* **Stable and testable code base.** Using Python has allowed unit testing + of all the project code. The code is tested to work in Python 2.6+ and + Python 3. +* **Support for prompts and statuslines in many applications.** Originally + created exclusively for vim statuslines, the project has evolved to + provide statuslines in tmux and several WMs, and prompts for shells like + bash/zsh and other applications. It's simple to write renderers for any + other applications that Powerline doesn't yet support. +* **Configuration and colorschemes written in JSON.** JSON is + a standardized, simple and easy to use file format that allows for easy + user configuration across all of Powerline's supported applications. +* **Fast and lightweight, with daemon support for even better performance.** + Although the code base spans a couple of thousand lines of code with no + goal of "less than X lines of code", the main focus is on good performance + and as little code as possible while still providing a rich set of + features. The new daemon also ensures that only one Python instance is + launched for prompts and statuslines, which provides excellent + performance. + +*But I hate Python / I don't need shell prompts / this is just too much +hassle for me / what happened to the original vim-powerline project / …* + +You should check out some of the Powerline derivatives. The most lightweight +and feature-rich alternative is currently Bailey Ling's `vim-airline +`_ project. + +------ * Consult the `documentation `_ for more information and installation instructions. * Check out `powerline-fonts `_ - for pre-patched versions of popular coding fonts. - -.. _travis-build-status: https://travis-ci.org/Lokaltog/powerline + for pre-patched versions of popular, open source coding fonts. Screenshots ----------- From ab6140136dbee459c0fa3925545eda17455dbe05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 6 Aug 2013 10:34:14 +0200 Subject: [PATCH 0754/1472] Add visual_range segment Returns a value similar to `showcmd`. TODO: Make proper test case. --- .../colorschemes/vim/default.json | 1 + .../colorschemes/vim/solarized.json | 1 + .../config_files/themes/vim/default.json | 5 ++++ powerline/segments/vim.py | 26 +++++++++++++++++++ tests/test_segments.py | 4 +++ tests/vim.py | 9 ++++++- 6 files changed, 45 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 612eb57e..cd7090a7 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -4,6 +4,7 @@ "background": { "fg": "white", "bg": "gray2" }, "background:divider": { "fg": "gray6", "bg": "gray2" }, "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, + "visual_range": { "fg": "brightestorange", "bg": "darkorange", "attr": ["bold"] }, "modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attr": ["bold"] }, "paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] }, "readonly_indicator": { "fg": "brightestred", "bg": "gray4" }, diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 0d28db83..28bc2bb3 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -4,6 +4,7 @@ "background": { "fg": "oldlace", "bg": "royalblue5" }, "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5" }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, + "visual_range": { "fg": "green", "bg": "oldlace", "attr": ["bold"] }, "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, "readonly_indicator": { "fg": "red", "bg": "darkgreencopper" }, diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 29d6a2ac..1380fe13 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -20,6 +20,11 @@ "name": "mode", "exclude_modes": ["nc"] }, + { + "name": "visual_range", + "exclude_modes": ["nc"], + "priority": 10 + }, { "name": "paste_indicator", "exclude_modes": ["nc"], diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f6dc7119..b41fb5e5 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -18,6 +18,7 @@ from collections import defaultdict vim_funcs = { 'virtcol': vim_get_func('virtcol', rettype=int), + 'getpos': vim_get_func('getpos'), 'fnamemodify': vim_get_func('fnamemodify', rettype=str), 'expand': vim_get_func('expand', rettype=str), 'bufnr': vim_get_func('bufnr', rettype=int), @@ -87,6 +88,31 @@ def mode(pl, segment_info, override=None): return vim_modes[mode] +@requires_segment_info +def visual_range(pl, segment_info): + '''Return the current visual selection range. + + Returns a value similar to `showcmd`. + ''' + if segment_info['mode'] not in ('v', 'V', '^V'): + return None + pos_start = vim_funcs['getpos']('v') + pos_end = vim_funcs['getpos']('.') + # Workaround for vim's "excellent" handling of multibyte characters and display widths + pos_start[2] = vim_funcs['virtcol']([pos_start[1], pos_start[2], pos_start[3]]) + pos_end[2] = vim_funcs['virtcol']([pos_end[1], pos_end[2], pos_end[3]]) + visual_start = (int(pos_start[1]), int(pos_start[2])) + visual_end = (int(pos_end[1]), int(pos_end[2])) + diff_rows = abs(visual_end[0] - visual_start[0]) + 1 + diff_cols = abs(visual_end[1] - visual_start[1]) + 1 + if segment_info['mode'] == '^V': + return '{0} × {1}'.format(diff_rows, diff_cols) + elif segment_info['mode'] == 'V' or diff_rows > 1: + return '{0} rows'.format(diff_rows) + else: + return '{0} cols'.format(diff_cols) + + @requires_segment_info def modified_indicator(pl, segment_info, text='+'): '''Return a file modified indicator. diff --git a/tests/test_segments.py b/tests/test_segments.py index 86f57f19..d9d12527 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -399,6 +399,10 @@ class TestVim(TestCase): self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'V·BLCK') self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') + def test_visual_range(self): + # TODO + pass + def test_modified_indicator(self): pl = Pl() segment_info = vim_module._get_segment_info() diff --git a/tests/vim.py b/tests/vim.py index f5c1b158..beec40de 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -228,11 +228,18 @@ def _emul_setwinvar(winnr, varname, value): @_vim def _emul_virtcol(expr): - if expr == '.': + if expr == '.' or isinstance(expr, list): return windows[_window - 1].cursor[1] + 1 raise NotImplementedError +@_vim +def _emul_getpos(expr): + if expr == '.' or expr == 'v': + return [0, windows[_window - 1].cursor[0] + 1, windows[_window - 1].cursor[1] + 1, 0] + raise NotImplementedError + + @_vim @_str_func def _emul_fnamemodify(path, modstring): From f15bb19c353991152124b764194e45f6789b83ab Mon Sep 17 00:00:00 2001 From: Daniel Brodie Date: Thu, 8 Aug 2013 19:53:34 +0300 Subject: [PATCH 0755/1472] Don't let the vcs detection cross mount point boundaries Fixes issue 625 --- powerline/lib/vcs/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 8666f2bf..005edf83 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -16,6 +16,8 @@ def generate_directories(path): if os.path.isdir(path): yield path while True: + if os.path.ismount(path): + break old_path = path path = os.path.dirname(path) if path == old_path or not path: From e58cb30d7d8ce201030c27560b3f12efcd7f24c1 Mon Sep 17 00:00:00 2001 From: Daniel Brodie Date: Tue, 13 Aug 2013 11:05:04 +0300 Subject: [PATCH 0756/1472] Fix UnicodeDecodeError when dir_limit_depth and dir_shorten_len are set --- powerline/segments/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 373d1ca4..f13f6f8c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -104,10 +104,10 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) cwd_split_len = len(cwd_split) - if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: - del(cwd_split[0:-dir_limit_depth]) - cwd_split.insert(0, '⋯') cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] + if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: + del(cwd[0:-dir_limit_depth]) + cwd.insert(0, '⋯') ret = [] if not cwd[0]: cwd[0] = '/' From 51a5b430a82d8395770feb8589fb264b9e8cf6b5 Mon Sep 17 00:00:00 2001 From: Aaron Schrab Date: Mon, 8 Apr 2013 13:51:24 -0400 Subject: [PATCH 0757/1472] Fix handling of segment include_modes Segments should only be included if rules for both inclusion and exclusion are satisfied, satisfying one or the other is not sufficient. If the include_modes list for a segment is missing or empty consider all modes to be included so that users do not need to supply an exhaustive list, since there isn't much point in actually wanting an empty include list. Actually check for mode name in the list rather than mistakenly checking for the segment object. --- powerline/renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 848db1ea..2117eeee 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -186,7 +186,7 @@ class Renderer(object): # Handle excluded/included segments for the current mode segments = [self._get_highlighting(segment, mode) for segment in segments - if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])] + if mode not in segment['exclude_modes'] and (not segment['include_modes'] or mode in segment['include_modes'])] segments = [segment for segment in self._render_segments(theme, segments)] From 9aee39e412ec1347316b05dbc560ca23dd8ff0f1 Mon Sep 17 00:00:00 2001 From: Aaron Schrab Date: Fri, 16 Aug 2013 21:52:28 -0400 Subject: [PATCH 0758/1472] Handle gitfiles when retrieving branch name Move code to parse a .git file from the do_status method to a new method shared with get_branch_name so that branch name can be retreived from repositories which use a .git file. Closes #634. --- powerline/lib/vcs/git.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index a8514ad8..b20cafb9 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -19,23 +19,22 @@ def branch_name_from_config_file(directory, config_file): return m.group(1).decode('utf-8', 'replace') return raw[:7] +def git_directory(directory): + path = os.path.join(directory, '.git') + if os.path.isfile(path): + with open(path, 'rb') as f: + raw = f.read().partition(b':')[2].strip() + return os.path.abspath(os.path.join(directory, raw)) + else: + return path + def get_branch_name(base_dir): - head = os.path.join(base_dir, '.git', 'HEAD') - try: - return _get_branch_name(base_dir, head, branch_name_from_config_file) - except OSError as e: - if getattr(e, 'errno', None) == errno.ENOTDIR or getattr(e, 'winerror', None) == 3: - # We are in a submodule - return '(no branch)' - raise + head = os.path.join(git_directory(base_dir), 'HEAD') + return _get_branch_name(base_dir, head, branch_name_from_config_file) def do_status(directory, path, func): if path: - gitd = os.path.join(directory, '.git') - if os.path.isfile(gitd): - with open(gitd, 'rb') as f: - raw = f.read().partition(b':')[2].strip() - gitd = os.path.abspath(os.path.join(directory, raw)) + gitd = git_directory(directory) # We need HEAD as without it using fugitive to commit causes the # current file's status (and only the current file) to not be updated # for some reason I cannot be bothered to figure out. From 28c1c0dfb4ecbcc32ab0fb998fdc47f34d879705 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 20 Aug 2013 09:28:11 +0530 Subject: [PATCH 0759/1472] More informative error message. Fixes #638 --- powerline/lib/inotify.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index 1e143f5f..320cd92c 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -131,7 +131,10 @@ class INotify(object): def handle_error(self): import ctypes eno = ctypes.get_errno() - raise OSError(eno, self.os.strerror(eno)) + extra = '' + if eno == errno.ENOSPC: + extra = 'You may need to increase the inotify limits on your system, via /proc/sys/inotify/max_user_*' + raise OSError(eno, self.os.strerror(eno) + extra) def __del__(self): # This method can be called during interpreter shutdown, which means we From f107494d841ffd9adeecc49014e9334c98279385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 13:07:56 +0200 Subject: [PATCH 0760/1472] Make author name ASCII --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9a78bcb6..0b449011 100755 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ setup( description='The ultimate statusline/prompt utility.', long_description=README, classifiers=[], - author='Kim Silkebækken', + author='Kim Silkebaekken', author_email='kim.silkebaekken+vim@gmail.com', url='https://github.com/Lokaltog/powerline', scripts=[ From f94ed68211826aa449c46c9b6f925c801c6c818c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 7 Aug 2013 16:58:27 +0530 Subject: [PATCH 0761/1472] Fix #621 --- powerline/lib/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 8fec4d95..9cf6ee4a 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -8,10 +8,11 @@ from threading import Event, Lock from collections import defaultdict import json +import codecs def open_file(path): - return open(path, 'r') + return codecs.open(path, encoding='utf-8') def load_json_config(config_file_path, load=json.load, open_file=open_file): From 42ea2bf53989f7003dcf101d7cea752a0dc7f914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 13:40:36 +0200 Subject: [PATCH 0762/1472] Create shared namespaces for powerline.{matchers,segments}.plugin --- powerline/matchers/__init__.py | 2 ++ powerline/matchers/plugin/__init__.py | 2 ++ powerline/segments/__init__.py | 2 ++ powerline/segments/plugin/__init__.py | 2 ++ 4 files changed, 8 insertions(+) create mode 100644 powerline/matchers/plugin/__init__.py create mode 100644 powerline/segments/plugin/__init__.py diff --git a/powerline/matchers/__init__.py b/powerline/matchers/__init__.py index e69de29b..3ad9513f 100644 --- a/powerline/matchers/__init__.py +++ b/powerline/matchers/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/powerline/matchers/plugin/__init__.py b/powerline/matchers/plugin/__init__.py new file mode 100644 index 00000000..3ad9513f --- /dev/null +++ b/powerline/matchers/plugin/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py index e69de29b..3ad9513f 100644 --- a/powerline/segments/__init__.py +++ b/powerline/segments/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/powerline/segments/plugin/__init__.py b/powerline/segments/plugin/__init__.py new file mode 100644 index 00000000..3ad9513f --- /dev/null +++ b/powerline/segments/plugin/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) From 2e38ab568651cec323d76c9748859b2b3bfdc2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 13:41:53 +0200 Subject: [PATCH 0763/1472] Add NERDTree statusline --- powerline/config_files/config.json | 4 +++- .../themes/vim/plugin/nerdtree.json | 17 +++++++++++++++++ powerline/matchers/plugin/nerdtree.py | 9 +++++++++ powerline/segments/plugin/nerdtree.py | 17 +++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 powerline/config_files/themes/vim/plugin/nerdtree.json create mode 100644 powerline/matchers/plugin/nerdtree.py create mode 100644 powerline/segments/plugin/nerdtree.py diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index a70922ff..b825f9b0 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -37,7 +37,9 @@ "local_themes": { "cmdwin": "cmdwin", "help": "help", - "quickfix": "quickfix" + "quickfix": "quickfix", + + "powerline.matchers.plugin.nerdtree.nerdtree": "plugin/nerdtree" } }, "wm": { diff --git a/powerline/config_files/themes/vim/plugin/nerdtree.json b/powerline/config_files/themes/vim/plugin/nerdtree.json new file mode 100644 index 00000000..95495db1 --- /dev/null +++ b/powerline/config_files/themes/vim/plugin/nerdtree.json @@ -0,0 +1,17 @@ +{ + "default_module": "powerline.segments.plugin.nerdtree", + "segments": { + "left": [ + { + "name": "nerdtree" + }, + { + "type": "string", + "highlight_group": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline/matchers/plugin/nerdtree.py b/powerline/matchers/plugin/nerdtree.py new file mode 100644 index 00000000..a9f8f0bc --- /dev/null +++ b/powerline/matchers/plugin/nerdtree.py @@ -0,0 +1,9 @@ +# vim:fileencoding=utf-8:noet + +import os +import re + + +def nerdtree(matcher_info): + name = matcher_info['buffer'].name + return name and re.match(r'NERD_tree_\d+', os.path.basename(name)) diff --git a/powerline/segments/plugin/nerdtree.py b/powerline/segments/plugin/nerdtree.py new file mode 100644 index 00000000..466aad72 --- /dev/null +++ b/powerline/segments/plugin/nerdtree.py @@ -0,0 +1,17 @@ +# vim:fileencoding=utf-8:noet + +import vim + +from powerline.segments.vim import window_cached + + +@window_cached +def nerdtree(pl): + ntr = vim.eval('getbufvar("%", "NERDTreeRoot")') + if not ntr: + return + path_str = vim.eval('getbufvar("%", "NERDTreeRoot").path.str()') + return [{ + 'contents': path_str, + 'highlight_group': ['nerdtree.path', 'file_name'], + }] From 0b5b81774f15a32546d0635e8620858acd0d0f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 15:29:09 +0200 Subject: [PATCH 0764/1472] Add Control-P statuslines This hacks around the way CtrlP handles statuslines. Powerline will always override the statusline, but CtrlP doesn't make its details available as buffer variables so separate functions assign the details to buffer variables each statusline redraw. Currently this statusline only uses a couple of highlight groups, colorschemes should add the HL groups to make this prettier. --- powerline/config_files/config.json | 3 +- .../config_files/themes/vim/plugin/ctrlp.json | 22 +++++ powerline/matchers/plugin/ctrlp.py | 27 ++++++ powerline/segments/plugin/ctrlp.py | 94 +++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 powerline/config_files/themes/vim/plugin/ctrlp.json create mode 100644 powerline/matchers/plugin/ctrlp.py create mode 100644 powerline/segments/plugin/ctrlp.py diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index b825f9b0..aace2c63 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -39,7 +39,8 @@ "help": "help", "quickfix": "quickfix", - "powerline.matchers.plugin.nerdtree.nerdtree": "plugin/nerdtree" + "powerline.matchers.plugin.nerdtree.nerdtree": "plugin/nerdtree", + "powerline.matchers.plugin.ctrlp.ctrlp": "plugin/ctrlp" } }, "wm": { diff --git a/powerline/config_files/themes/vim/plugin/ctrlp.json b/powerline/config_files/themes/vim/plugin/ctrlp.json new file mode 100644 index 00000000..da43736a --- /dev/null +++ b/powerline/config_files/themes/vim/plugin/ctrlp.json @@ -0,0 +1,22 @@ +{ + "default_module": "powerline.segments.plugin.ctrlp", + "segments": { + "left": [ + { + "name": "ctrlp_left" + }, + { + "type": "string", + "highlight_group": ["ctrlp.background", "background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "name": "ctrlp_right" + } + ] + } +} diff --git a/powerline/matchers/plugin/ctrlp.py b/powerline/matchers/plugin/ctrlp.py new file mode 100644 index 00000000..525a0d95 --- /dev/null +++ b/powerline/matchers/plugin/ctrlp.py @@ -0,0 +1,27 @@ +# vim:fileencoding=utf-8:noet + +import os +import vim + +vim.command(''' +function! Powerline_plugin_ctrlp_main(...) + let b:powerline_ctrlp_type = 'main' + let b:powerline_ctrlp_args = a:000 +endfunction +''') + +vim.command(''' +function! Powerline_plugin_ctrlp_prog(...) + let b:powerline_ctrlp_type = 'prog' + let b:powerline_ctrlp_args = a:000 +endfunction +''') + +vim.command(''' +let g:ctrlp_status_func = { 'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog' } +''') + + +def ctrlp(matcher_info): + name = matcher_info['buffer'].name + return name and os.path.basename(name) == 'ControlP' diff --git a/powerline/segments/plugin/ctrlp.py b/powerline/segments/plugin/ctrlp.py new file mode 100644 index 00000000..f696f233 --- /dev/null +++ b/powerline/segments/plugin/ctrlp.py @@ -0,0 +1,94 @@ +# vim:fileencoding=utf-8:noet + +import vim + + +def ctrlp_left(pl): + ctrlp_type = vim.eval('getbufvar("%", "powerline_ctrlp_type")') + ctrlp_args = vim.eval('getbufvar("%", "powerline_ctrlp_args")') + + return globals()['ctrlp_stl_left_' + ctrlp_type](pl, *ctrlp_args) + + +def ctrlp_right(pl): + ctrlp_type = vim.eval('getbufvar("%", "powerline_ctrlp_type")') + ctrlp_args = vim.eval('getbufvar("%", "powerline_ctrlp_args")') + + return globals()['ctrlp_stl_right_' + ctrlp_type](pl, *ctrlp_args) + + +def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): + marked = marked[2:-1] + segments = [] + + if int(regex): + segments.append({ + 'contents': 'regex', + 'highlight_group': ['ctrlp.regex', 'background'], + }) + + segments += [ + { + 'contents': prev + ' ', + 'highlight_group': ['ctrlp.prev', 'background'], + 'draw_inner_divider': True, + 'priority': 40, + }, + { + 'contents': item, + 'highlight_group': ['ctrlp.item', 'file_name'], + 'draw_inner_divider': True, + 'width': 10, + }, + { + 'contents': ' ' + next, + 'highlight_group': ['ctrlp.next', 'background'], + 'draw_inner_divider': True, + 'priority': 40, + }, + ] + + if marked != '-': + segments.append({ + 'contents': marked, + 'highlight_group': ['ctrlp.marked', 'background'], + 'draw_inner_divider': True, + }) + + return segments + + +def ctrlp_stl_right_main(pl, focus, byfname, regex, prev, item, next, marked): + segments = [ + { + 'contents': focus, + 'highlight_group': ['ctrlp.focus', 'background'], + 'draw_inner_divider': True, + 'priority': 50, + }, + { + 'contents': byfname, + 'highlight_group': ['ctrlp.byfname', 'background'], + 'priority': 50, + }, + ] + + return segments + + +def ctrlp_stl_left_prog(pl, progress): + return [ + { + 'contents': 'Loading...', + 'highlight_group': ['ctrlp.progress', 'file_name'], + }, + ] + + +def ctrlp_stl_right_prog(pl, progress): + return [ + { + 'contents': progress, + 'highlight_group': ['ctrlp.progress', 'file_name'], + }, + ] From 0ffe6da4e64824ebd7fb056ad17eb216a6d30db8 Mon Sep 17 00:00:00 2001 From: Aaron Schrab Date: Tue, 20 Aug 2013 00:06:31 -0400 Subject: [PATCH 0765/1472] Option to suppress error in python-less vim If the g:powerline_no_python_error variable is set don't issue an error message even if vim doesn't have the required Python support. This allows a common set of configuration files to be used across systems where some of the copies of vim don't meet the requirements. --- docs/source/configuration.rst | 4 ++++ powerline/bindings/vim/plugin/powerline.vim | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 073afdde..4be7bea5 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -455,6 +455,10 @@ Vim configuration can be overridden using the following options: directory which will be searched for configuration. When this option is present, none of the other locations are searched. +``g:powerline_no_python_error`` + If this variable is set to a true value it will prevent Powerline from reporting + an error when loaded in a copy of vim without the necessary Python support. + Powerline script overrides -------------------------- diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index ad2c60fc..a6497d3f 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -10,8 +10,10 @@ function! s:CriticalError(message) endfunction if ! has('python') && ! has('python3') - call s:CriticalError('You need vim compiled with Python 2.6+ or 3.2+ support - \ for Powerline to work. Please consult the documentation for more details.') + if !exists('g:powerline_no_python_error') + call s:CriticalError('You need vim compiled with Python 2.6+ or 3.2+ support + \ for Powerline to work. Please consult the documentation for more details.') + endif finish endif From ed28bb50e64968b64718b9f613d263c01f733a80 Mon Sep 17 00:00:00 2001 From: Aaron Schrab Date: Tue, 20 Aug 2013 09:35:42 -0400 Subject: [PATCH 0766/1472] ignore trailing whitespace errors in .rst files Use a .gitattributes file to tell git to not highlight trailing whitespace as an error in .rst files when showing diffs. A trailing space is used on many lines in those files to indicate that the paragraph continues onto the next line. --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..e3074859 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.rst whitespace=-blank-at-eol From 2a127cdb145e6815d506ea7b1c620c8ea6e2f35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 16:04:06 +0200 Subject: [PATCH 0767/1472] Add error/warning HL groups for vim --- powerline/config_files/colorschemes/vim/default.json | 4 +++- powerline/config_files/colorschemes/vim/solarized.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index cd7090a7..a97116cd 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -30,7 +30,9 @@ "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10" }, "col_current": { "fg": "gray6", "bg": "gray10" }, "modified_buffers": { "fg": "brightyellow", "bg": "gray2" }, - "environment": { "fg": "gray8", "bg": "gray2" } + "environment": { "fg": "gray8", "bg": "gray2" }, + "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, + "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] } }, "mode_translations": { "nc": { diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 28bc2bb3..7f175999 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -29,7 +29,9 @@ "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "gray10" }, "col_current": { "fg": "azure4", "bg": "lightyellow" }, - "environment": { "fg": "gray61", "bg": "royalblue5" } + "environment": { "fg": "gray61", "bg": "royalblue5" }, + "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, + "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } }, "mode_translations": { "nc": { From 7aee4c1dd7f1ed5f9001c2c383a008f285d046ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 16:05:10 +0200 Subject: [PATCH 0768/1472] Align default vim colorscheme attributes --- .../colorschemes/vim/default.json | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index a97116cd..f0067cd1 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -1,38 +1,38 @@ { "name": "Default color scheme", "groups": { - "background": { "fg": "white", "bg": "gray2" }, - "background:divider": { "fg": "gray6", "bg": "gray2" }, - "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, - "visual_range": { "fg": "brightestorange", "bg": "darkorange", "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" }, - "branch_dirty": { "fg": "brightyellow", "bg": "gray4" }, - "branch_clean": { "fg": "gray9", "bg": "gray4" }, - "branch:divider": { "fg": "gray7", "bg": "gray4" }, - "file_directory": { "fg": "gray9", "bg": "gray4" }, - "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, - "file_size": { "fg": "gray8", "bg": "gray2" }, - "file_name_no_file": { "fg": "gray9", "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" }, - "file_vcs_status": { "fg": "brightestred", "bg": "gray4" }, - "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4" }, - "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, - "line_percent": { "fg": "gray9", "bg": "gray4" }, - "line_percent_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, - "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, + "background": { "fg": "white", "bg": "gray2" }, + "background:divider": { "fg": "gray6", "bg": "gray2" }, + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, + "visual_range": { "fg": "brightestorange", "bg": "darkorange", "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" }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray4" }, + "branch_clean": { "fg": "gray9", "bg": "gray4" }, + "branch:divider": { "fg": "gray7", "bg": "gray4" }, + "file_directory": { "fg": "gray9", "bg": "gray4" }, + "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, + "file_size": { "fg": "gray8", "bg": "gray2" }, + "file_name_no_file": { "fg": "gray9", "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" }, + "file_vcs_status": { "fg": "brightestred", "bg": "gray4" }, + "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4" }, + "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, + "line_percent": { "fg": "gray9", "bg": "gray4" }, + "line_percent_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, + "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, + "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10" }, - "col_current": { "fg": "gray6", "bg": "gray10" }, - "modified_buffers": { "fg": "brightyellow", "bg": "gray2" }, - "environment": { "fg": "gray8", "bg": "gray2" }, - "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, - "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] } + "col_current": { "fg": "gray6", "bg": "gray10" }, + "modified_buffers": { "fg": "brightyellow", "bg": "gray2" }, + "environment": { "fg": "gray8", "bg": "gray2" }, + "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, + "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] } }, "mode_translations": { "nc": { @@ -70,9 +70,9 @@ "green_yellow_red": "gray5" }, "groups": { - "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }, + "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }, "background:divider": { "fg": "darkcyan", "bg": "darkestblue" }, - "branch:divider": { "fg": "darkcyan", "bg": "darkblue" } + "branch:divider": { "fg": "darkcyan", "bg": "darkblue" } } }, "v": { From 6baf1f8e90f1d8483a5983ead8e2184b3d22c85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 16:05:56 +0200 Subject: [PATCH 0769/1472] Add Syntastic statusline Refs #376, #639. Closes #451. --- .../config_files/themes/vim/default.json | 5 ++++ powerline/segments/plugin/syntastic.py | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 powerline/segments/plugin/syntastic.py diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 1380fe13..14179448 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -58,6 +58,11 @@ "name": "modified_indicator", "before": " " }, + { + "exclude_modes": ["nc"], + "module": "powerline.segments.plugin.syntastic", + "name": "syntastic" + }, { "type": "string", "highlight_group": ["background"], diff --git a/powerline/segments/plugin/syntastic.py b/powerline/segments/plugin/syntastic.py new file mode 100644 index 00000000..4c470dd9 --- /dev/null +++ b/powerline/segments/plugin/syntastic.py @@ -0,0 +1,28 @@ +# vim:fileencoding=utf-8:noet + +import vim + +from powerline.segments.vim import window_cached + + +@window_cached +def syntastic(pl): + if not int(vim.eval('exists("g:SyntasticLoclist")')): + return + has_errors = int(vim.eval('g:SyntasticLoclist.current().hasErrorsOrWarningsToDisplay()')) + if not has_errors: + return + errors = vim.eval('g:SyntasticLoclist.current().errors()') + warnings = vim.eval('g:SyntasticLoclist.current().warnings()') + segments = [] + if errors: + segments.append({ + 'contents': 'ERR:  {line} ({num}) '.format(line=errors[0]['lnum'], num=len(errors)), + 'highlight_group': ['syntastic.error', 'error', 'background'], + }) + if warnings: + segments.append({ + 'contents': 'WARN:  {line} ({num}) '.format(line=warnings[0]['lnum'], num=len(warnings)), + 'highlight_group': ['syntastic.warning', 'warning', 'background'], + }) + return segments From e19a63aaa96f29f8eed6c5300cba20ce0dd029a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 16:48:36 +0200 Subject: [PATCH 0770/1472] Update plugin segments to use bindings --- powerline/matchers/plugin/ctrlp.py | 16 +++++----------- powerline/segments/plugin/ctrlp.py | 16 +++++----------- powerline/segments/plugin/nerdtree.py | 3 ++- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/powerline/matchers/plugin/ctrlp.py b/powerline/matchers/plugin/ctrlp.py index 525a0d95..50b374a7 100644 --- a/powerline/matchers/plugin/ctrlp.py +++ b/powerline/matchers/plugin/ctrlp.py @@ -3,23 +3,17 @@ import os import vim -vim.command(''' -function! Powerline_plugin_ctrlp_main(...) +vim.command('''function! Powerline_plugin_ctrlp_main(...) let b:powerline_ctrlp_type = 'main' let b:powerline_ctrlp_args = a:000 -endfunction -''') +endfunction''') -vim.command(''' -function! Powerline_plugin_ctrlp_prog(...) +vim.command('''function! Powerline_plugin_ctrlp_prog(...) let b:powerline_ctrlp_type = 'prog' let b:powerline_ctrlp_args = a:000 -endfunction -''') +endfunction''') -vim.command(''' -let g:ctrlp_status_func = { 'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog' } -''') +vim.command('''let g:ctrlp_status_func = { 'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog' }''') def ctrlp(matcher_info): diff --git a/powerline/segments/plugin/ctrlp.py b/powerline/segments/plugin/ctrlp.py index f696f233..6e624216 100644 --- a/powerline/segments/plugin/ctrlp.py +++ b/powerline/segments/plugin/ctrlp.py @@ -1,20 +1,14 @@ # vim:fileencoding=utf-8:noet import vim +from powerline.bindings.vim import getbufvar -def ctrlp_left(pl): - ctrlp_type = vim.eval('getbufvar("%", "powerline_ctrlp_type")') - ctrlp_args = vim.eval('getbufvar("%", "powerline_ctrlp_args")') +def ctrlp(pl, side): + ctrlp_type = getbufvar('%', 'powerline_ctrlp_type') + ctrlp_args = getbufvar('%', 'powerline_ctrlp_args') - return globals()['ctrlp_stl_left_' + ctrlp_type](pl, *ctrlp_args) - - -def ctrlp_right(pl): - ctrlp_type = vim.eval('getbufvar("%", "powerline_ctrlp_type")') - ctrlp_args = vim.eval('getbufvar("%", "powerline_ctrlp_args")') - - return globals()['ctrlp_stl_right_' + ctrlp_type](pl, *ctrlp_args) + return globals()['ctrlp_stl_{0}_{1}'.format(side, ctrlp_type)](pl, *ctrlp_args) def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): diff --git a/powerline/segments/plugin/nerdtree.py b/powerline/segments/plugin/nerdtree.py index 466aad72..c12ce777 100644 --- a/powerline/segments/plugin/nerdtree.py +++ b/powerline/segments/plugin/nerdtree.py @@ -2,12 +2,13 @@ import vim +from powerline.bindings.vim import getbufvar from powerline.segments.vim import window_cached @window_cached def nerdtree(pl): - ntr = vim.eval('getbufvar("%", "NERDTreeRoot")') + ntr = getbufvar('%', 'NERDTreeRoot') if not ntr: return path_str = vim.eval('getbufvar("%", "NERDTreeRoot").path.str()') From 74d4ee9669191b6e02ec9cf28424a0ba38803262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 16:48:49 +0200 Subject: [PATCH 0771/1472] Update tests Unit tests are not run on external segments. When external segments are moved out of the core repo the extra test code won't be needed. --- tests/test_configuration.py | 11 +++++++++-- tests/vim.py | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index f8053fb6..19b9bb04 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -19,9 +19,16 @@ class TestConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') - buffers = ((('bufoptions',), {'buftype': 'help'}), (('bufname', '[Command Line]'), {}), (('bufoptions',), {'buftype': 'quickfix'})) + buffers = ( + (('bufoptions',), {'buftype': 'help'}), + (('bufname', '[Command Line]'), {}), + (('bufoptions',), {'buftype': 'quickfix'}), + ) with open(os.path.join(cfg_path, 'config.json'), 'r') as f: - self.assertEqual(len(buffers), len(json.load(f)['ext']['vim']['local_themes'])) + local_themes_raw = json.load(f)['ext']['vim']['local_themes'] + # Don't run tests on external/plugin segments + local_themes = dict((k, v) for (k, v) in local_themes_raw.items() if not '.' in k) + self.assertEqual(len(buffers), len(local_themes)) outputs = {} i = 0 diff --git a/tests/vim.py b/tests/vim.py index beec40de..91a94886 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -156,6 +156,9 @@ def command(cmd): elif cmd.startswith('hi '): sp = cmd.split() _highlights[sp[1]] = sp[2:] + elif cmd.startswith('function! Powerline_plugin_ctrlp'): + # Ignore CtrlP updating functions + pass else: raise NotImplementedError @@ -169,6 +172,8 @@ def eval(expr): elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) return "0" + elif expr.startswith('exists('): + return '0' raise NotImplementedError From 1fe98e1c3d494231bd7fe1b5f69cd21bfc47dc6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 17:01:08 +0200 Subject: [PATCH 0772/1472] Update CtrlP config to match segment changes --- powerline/config_files/themes/vim/plugin/ctrlp.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/themes/vim/plugin/ctrlp.json b/powerline/config_files/themes/vim/plugin/ctrlp.json index da43736a..02015b78 100644 --- a/powerline/config_files/themes/vim/plugin/ctrlp.json +++ b/powerline/config_files/themes/vim/plugin/ctrlp.json @@ -3,7 +3,10 @@ "segments": { "left": [ { - "name": "ctrlp_left" + "name": "ctrlp", + "args": { + "side": "left" + } }, { "type": "string", @@ -15,7 +18,10 @@ ], "right": [ { - "name": "ctrlp_right" + "name": "ctrlp", + "args": { + "side": "right" + } } ] } From 3d3450d8c1502ff32059d00c623aee52b21fb1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 17:07:18 +0200 Subject: [PATCH 0773/1472] Add Gundo statuslines --- powerline/config_files/config.json | 4 +++- .../themes/vim/plugin/gundo-preview.json | 19 +++++++++++++++++++ .../config_files/themes/vim/plugin/gundo.json | 19 +++++++++++++++++++ powerline/matchers/plugin/gundo.py | 13 +++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 powerline/config_files/themes/vim/plugin/gundo-preview.json create mode 100644 powerline/config_files/themes/vim/plugin/gundo.json create mode 100644 powerline/matchers/plugin/gundo.py diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index aace2c63..e9a627e7 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -40,7 +40,9 @@ "quickfix": "quickfix", "powerline.matchers.plugin.nerdtree.nerdtree": "plugin/nerdtree", - "powerline.matchers.plugin.ctrlp.ctrlp": "plugin/ctrlp" + "powerline.matchers.plugin.ctrlp.ctrlp": "plugin/ctrlp", + "powerline.matchers.plugin.gundo.gundo": "plugin/gundo", + "powerline.matchers.plugin.gundo.gundo_preview": "plugin/gundo-preview" } }, "wm": { diff --git a/powerline/config_files/themes/vim/plugin/gundo-preview.json b/powerline/config_files/themes/vim/plugin/gundo-preview.json new file mode 100644 index 00000000..3ce202ea --- /dev/null +++ b/powerline/config_files/themes/vim/plugin/gundo-preview.json @@ -0,0 +1,19 @@ +{ + "default_module": "powerline.segments.plugin.gundo", + "segments": { + "left": [ + { + "type": "string", + "highlight_group": ["gundo.name", "file_name"], + "contents": "Undo diff" + }, + { + "type": "string", + "highlight_group": ["gundo.background", "background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline/config_files/themes/vim/plugin/gundo.json b/powerline/config_files/themes/vim/plugin/gundo.json new file mode 100644 index 00000000..0c1a336a --- /dev/null +++ b/powerline/config_files/themes/vim/plugin/gundo.json @@ -0,0 +1,19 @@ +{ + "default_module": "powerline.segments.plugin.gundo", + "segments": { + "left": [ + { + "type": "string", + "highlight_group": ["gundo.name", "file_name"], + "contents": "Undo tree" + }, + { + "type": "string", + "highlight_group": ["gundo.background", "background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline/matchers/plugin/gundo.py b/powerline/matchers/plugin/gundo.py new file mode 100644 index 00000000..1992f711 --- /dev/null +++ b/powerline/matchers/plugin/gundo.py @@ -0,0 +1,13 @@ +# vim:fileencoding=utf-8:noet + +import os + + +def gundo(matcher_info): + name = matcher_info['buffer'].name + return name and os.path.basename(name) == '__Gundo__' + + +def gundo_preview(matcher_info): + name = matcher_info['buffer'].name + return name and os.path.basename(name) == '__Gundo_Preview__' From 0fc79d2849e3252e68bacd4ed02feb4683280bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 17:14:24 +0200 Subject: [PATCH 0774/1472] Center align CtrlP current segment --- powerline/segments/plugin/ctrlp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/segments/plugin/ctrlp.py b/powerline/segments/plugin/ctrlp.py index 6e624216..d8b4e3d8 100644 --- a/powerline/segments/plugin/ctrlp.py +++ b/powerline/segments/plugin/ctrlp.py @@ -33,6 +33,7 @@ def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): 'highlight_group': ['ctrlp.item', 'file_name'], 'draw_inner_divider': True, 'width': 10, + 'align': 'c', }, { 'contents': ' ' + next, From d0429ac0235db4d07695ae0d4423e3df2c1b6919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 17:26:36 +0200 Subject: [PATCH 0775/1472] Add Tagbar current tag segment --- powerline/config_files/colorschemes/vim/default.json | 3 ++- .../config_files/colorschemes/vim/solarized.json | 1 + powerline/config_files/themes/vim/default.json | 10 +++++++++- powerline/segments/plugin/tagbar.py | 12 ++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 powerline/segments/plugin/tagbar.py diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index f0067cd1..254383a1 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -32,7 +32,8 @@ "modified_buffers": { "fg": "brightyellow", "bg": "gray2" }, "environment": { "fg": "gray8", "bg": "gray2" }, "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, - "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] } + "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] }, + "current_tag": { "fg": "gray9", "bg": "gray2" } }, "mode_translations": { "nc": { diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 7f175999..881953d7 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -32,6 +32,7 @@ "environment": { "fg": "gray61", "bg": "royalblue5" }, "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "current_tag": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] } }, "mode_translations": { "nc": { diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 14179448..5f10912d 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -61,7 +61,15 @@ { "exclude_modes": ["nc"], "module": "powerline.segments.plugin.syntastic", - "name": "syntastic" + "name": "syntastic", + "priority": 50 + }, + { + "exclude_modes": ["nc"], + "module": "powerline.segments.plugin.tagbar", + "name": "current_tag", + "draw_soft_divider": false, + "priority": 50 }, { "type": "string", diff --git a/powerline/segments/plugin/tagbar.py b/powerline/segments/plugin/tagbar.py new file mode 100644 index 00000000..07e260f8 --- /dev/null +++ b/powerline/segments/plugin/tagbar.py @@ -0,0 +1,12 @@ +# vim:fileencoding=utf-8:noet + +import vim + +from powerline.segments.vim import window_cached + + +@window_cached +def current_tag(pl): + if not int(vim.eval('exists(":Tagbar")')): + return + return vim.eval('tagbar#currenttag("%s", "")') From 6e8318ac526571961b88f2e6a5a09edce7b28116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 17:40:26 +0200 Subject: [PATCH 0776/1472] Remove long-running segments from tmux The segments will be re-added when the daemon is merged and the docs are updated. --- powerline/config_files/themes/tmux/default.json | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index eb5d7e6d..7d475c96 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -24,22 +24,10 @@ "name": "uptime", "priority": 50 }, - { - "name": "external_ip", - "priority": 50 - }, - { - "name": "network_load", - "priority": 50 - }, { "name": "system_load", "priority": 50 }, - { - "name": "weather", - "priority": 50 - }, { "name": "date" }, @@ -50,10 +38,6 @@ "istime": true } }, - { - "name": "email_imap_alert", - "priority": 10 - }, { "name": "hostname" } From f12bb70559d1f444b75d0a9f78a752ad18a29f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Aug 2013 17:59:41 +0200 Subject: [PATCH 0777/1472] Add links to Google Groups --- README.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 595bd29f..efc6d4a0 100644 --- a/README.rst +++ b/README.rst @@ -4,17 +4,22 @@ Powerline :Author: Kim Silkebækken (kim.silkebaekken+vim@gmail.com) :Source: https://github.com/Lokaltog/powerline :Version: beta -:Build status: - .. image:: https://api.travis-ci.org/Lokaltog/powerline.png?branch=develop - :target: `travis-build-status`_ - :alt: Build status - -.. _travis-build-status: https://travis-ci.org/Lokaltog/powerline **Powerline is a statusline plugin for vim, and provides statuslines and prompts for several other applications, including zsh, bash, tmux, IPython, Awesome and Qtile.** +* `Support forum`_ (powerline-support@googlegroups.com) +* `Development discussion`_ (powerline-dev@googlegroups.com) + +.. image:: https://api.travis-ci.org/Lokaltog/powerline.png?branch=develop + :target: `travis-build-status`_ + :alt: Build status + +.. _travis-build-status: https://travis-ci.org/Lokaltog/powerline +.. _`Support forum`: https://groups.google.com/forum/#!forum/powerline-support +.. _`Development discussion`: https://groups.google.com/forum/#!forum/powerline-dev + Features -------- From 9314ed99c593c56dcac9c78f8cea81894318c3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Aug 2013 10:02:26 +0200 Subject: [PATCH 0778/1472] Add missing comma in solarized colorscheme --- powerline/config_files/colorschemes/vim/solarized.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 881953d7..869ebf1d 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -31,7 +31,7 @@ "col_current": { "fg": "azure4", "bg": "lightyellow" }, "environment": { "fg": "gray61", "bg": "royalblue5" }, "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, - "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, "current_tag": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] } }, "mode_translations": { From 4f1c26aa2406cf8e915a8c7d811977176732ff35 Mon Sep 17 00:00:00 2001 From: Wang Xuerui Date: Wed, 21 Aug 2013 01:58:32 +0800 Subject: [PATCH 0779/1472] Make extra error info string Fixes #645 --- powerline/lib/inotify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index 320cd92c..0e8211eb 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -134,7 +134,7 @@ class INotify(object): extra = '' if eno == errno.ENOSPC: extra = 'You may need to increase the inotify limits on your system, via /proc/sys/inotify/max_user_*' - raise OSError(eno, self.os.strerror(eno) + extra) + raise OSError(eno, self.os.strerror(eno) + str(extra)) def __del__(self): # This method can be called during interpreter shutdown, which means we From 7423b55cc4352ae93af4ed04e469167efd2f8314 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 25 Sep 2013 10:27:48 +0530 Subject: [PATCH 0780/1472] Fix git branch name sometimes getting stuck with inotify If you try to checkout the already current branch in git, git creates HEAD.lock and renames it to HEAD. This causes the inode of HEAD to change and so the inotify file watcher stops tracking HEAD. The fix is to re-create the inotify watch when the file attributes change. This is a bit of a performance penalty as most of the time the attribute changes are simple last modified time/size changes, but since inotify provides no way to know specifically when the inode has changed, this is the best we can do. --- powerline/lib/file_watcher.py | 19 ++++++++++++++++++- powerline/lib/vcs/__init__.py | 11 ++++++++++- tests/test_lib.py | 12 ++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index bf78b7bb..41e20362 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -54,7 +54,24 @@ class INotifyWatch(INotify): self.modified.pop(path, None) self.last_query.pop(path, None) else: - self.modified[path] = True + if mask & self.ATTRIB: + # The watched file could have had its inode changed, in + # which case we will not get any more events for this + # file, so re-register the watch. For example by some + # other file being renamed as this file. + try: + self.unwatch(path) + except OSError: + pass + try: + self.watch(path) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + else: + self.modified[path] = True + else: + self.modified[path] = True def unwatch(self, path): ''' Remove the watch for path. Raises an OSError if removing the watch diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 8666f2bf..b5e00c9a 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -31,6 +31,15 @@ def file_watcher(): _file_watcher = create_file_watcher() return _file_watcher +_branch_watcher = None + +def branch_watcher(): + global _branch_watcher + if _branch_watcher is None: + from powerline.lib.file_watcher import create_file_watcher + _branch_watcher = create_file_watcher() + return _branch_watcher + branch_name_cache = {} branch_lock = Lock() file_status_lock = Lock() @@ -39,7 +48,7 @@ def get_branch_name(directory, config_file, get_func): global branch_name_cache with branch_lock: # Check if the repo directory was moved/deleted - fw = file_watcher() + fw = branch_watcher() is_watched = fw.is_watched(directory) try: changed = fw(directory) diff --git a/tests/test_lib.py b/tests/test_lib.py index 18326d72..f10ee293 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -54,10 +54,11 @@ class TestFilesystemWatchers(TestCase): w = create_file_watcher(use_stat=False) if w.is_stat_based: raise SkipTest('This test is not suitable for a stat based file watcher') - f1, f2 = os.path.join(INOTIFY_DIR, 'file1'), os.path.join(INOTIFY_DIR, 'file2') + f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) with open(f1, 'wb'): with open(f2, 'wb'): - pass + with open(f3, 'wb'): + pass ne = os.path.join(INOTIFY_DIR, 'notexists') self.assertRaises(OSError, w, ne) self.assertTrue(w(f1)) @@ -85,6 +86,13 @@ class TestFilesystemWatchers(TestCase): # Check that deleting a file registers as a change os.unlink(f1) self.do_test_for_change(w, f1) + # Test that changing the inode of a file does not cause it to stop + # being watched + os.rename(f3, f2) + self.do_test_for_change(w, f2) + self.assertFalse(w(f2), 'Spurious change detected') + os.utime(f2, None) + self.do_test_for_change(w, f2) def test_tree_watcher(self): from powerline.lib.tree_watcher import TreeWatcher From a810e37f0280c87bbaec48cf58711418630acda5 Mon Sep 17 00:00:00 2001 From: Jesse Date: Sat, 5 Oct 2013 03:31:34 -0400 Subject: [PATCH 0781/1472] workaround for _powerline_tmux_setenv() issue with bash-4.2.45 --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index cc0a22cc..9b4959ba 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -8,7 +8,7 @@ fi _powerline_tmux_setenv() { if [[ -n "$TMUX" ]]; then - tmux setenv -g TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" tmux refresh -S fi } From 1d385f875f70cd45015ed18750898a1628460bcf Mon Sep 17 00:00:00 2001 From: Peter Fern Date: Tue, 15 Oct 2013 21:21:12 +1100 Subject: [PATCH 0782/1472] Avoid infinite recursion in `POWERLINE_OLD_PROMPT_COMMAND` Fixes #678 --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index cc0a22cc..c853211f 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -38,6 +38,6 @@ _powerline_prompt() { trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns -[[ "$PROMPT_COMMAND" == "_powerline_prompt" ]] || +[[ "$PROMPT_COMMAND" != "${PROMPT_COMMAND/_powerline_prompt/}" ]] || POWERLINE_OLD_PROMPT_COMMAND="$PROMPT_COMMAND" export PROMPT_COMMAND="_powerline_prompt" From 561b71582437b048d6851df076ddb75d20c24225 Mon Sep 17 00:00:00 2001 From: Tore Sinding Bekkedal Date: Wed, 16 Oct 2013 01:42:07 +0200 Subject: [PATCH 0783/1472] Make clearer the naming conflict in PyPI (closes #681) --- docs/source/overview.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index dd4c06e9..458ab5de 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -82,6 +82,10 @@ Other applications Installation ============ +.. note:: This project is currently unavailable from PyPI due to a naming conflict + with an unrelated project. Please read the detailed instructions for your platform + below. + * :ref:`installation-linux` * :ref:`installation-osx` From c293b62132b3f9a9be563d96629d70314c0397c7 Mon Sep 17 00:00:00 2001 From: Evan Callicoat Date: Fri, 1 Nov 2013 00:57:33 -0500 Subject: [PATCH 0784/1472] Fix weather geoip lookups - _external_ip() isn't necessary with freegeoip.net as it does this for you when unspecified. - region_name/country_name make Yahoo's weather API unhappy whereas region_code/country_code work for me (TX/US) --- powerline/segments/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f13f6f8c..4a858d5b 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -335,10 +335,10 @@ class WeatherSegment(ThreadedSegment): # Do not lock attribute assignments in this branch: they are used # only in .update() if not self.location: - location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip())) + location_data = json.loads(urllib_read('http://freegeoip.net/json/')) self.location = ','.join([location_data['city'], - location_data['region_name'], - location_data['country_name']]) + location_data['region_code'], + location_data['country_code']]) query_data = { 'q': 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' From b5f051f71cc2493665b0f233d624415ff30f827d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Nov 2013 00:54:32 +0400 Subject: [PATCH 0785/1472] Fix powerline-lint tests Some notes on the commit: 1. As external_ip and email_imap_alert segments seem to be removed corresponding segment_data keys also were removed. 2. Various files that import vim module now have the usual workaround that sets vim local to dummy object on error. 3. Syntastic check was failing because it did not specify which highlighting groups it uses in documentation. I made it specify them and also moved format strings to keywords. Same for some other plugin-local themes. 4. powerline-lint script got --debug argument. Pretty useless currently though: it only makes it output traceback for ImportError when failing to import module to stderr. 5. Moved themes/vim/plugin/*.json to themes/vim/plugin_*.json. 6. Fixed powerline-lint that ignored problems from values. --- powerline/config_files/config.json | 8 +-- .../config_files/themes/tmux/default.json | 10 ---- .../{plugin/ctrlp.json => plugin_ctrlp.json} | 0 ...preview.json => plugin_gundo-preview.json} | 1 - .../{plugin/gundo.json => plugin_gundo.json} | 1 - .../nerdtree.json => plugin_nerdtree.json} | 0 powerline/lint/__init__.py | 59 +++++++++++++------ powerline/matchers/plugin/ctrlp.py | 23 ++++---- powerline/segments/plugin/ctrlp.py | 26 +++++++- powerline/segments/plugin/nerdtree.py | 9 ++- powerline/segments/plugin/syntastic.py | 29 ++++++--- powerline/segments/plugin/tagbar.py | 5 +- scripts/powerline-lint | 3 +- 13 files changed, 119 insertions(+), 55 deletions(-) rename powerline/config_files/themes/vim/{plugin/ctrlp.json => plugin_ctrlp.json} (100%) rename powerline/config_files/themes/vim/{plugin/gundo-preview.json => plugin_gundo-preview.json} (85%) rename powerline/config_files/themes/vim/{plugin/gundo.json => plugin_gundo.json} (85%) rename powerline/config_files/themes/vim/{plugin/nerdtree.json => plugin_nerdtree.json} (100%) diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index e9a627e7..4da4e9b1 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -39,10 +39,10 @@ "help": "help", "quickfix": "quickfix", - "powerline.matchers.plugin.nerdtree.nerdtree": "plugin/nerdtree", - "powerline.matchers.plugin.ctrlp.ctrlp": "plugin/ctrlp", - "powerline.matchers.plugin.gundo.gundo": "plugin/gundo", - "powerline.matchers.plugin.gundo.gundo_preview": "plugin/gundo-preview" + "powerline.matchers.plugin.nerdtree.nerdtree": "plugin_nerdtree", + "powerline.matchers.plugin.ctrlp.ctrlp": "plugin_ctrlp", + "powerline.matchers.plugin.gundo.gundo": "plugin_gundo", + "powerline.matchers.plugin.gundo.gundo_preview": "plugin_gundo-preview" } }, "wm": { diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 7d475c96..ecf6fbb1 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -4,18 +4,8 @@ "uptime": { "before": "⇑ " }, - "external_ip": { - "before": "ⓦ " - }, "date": { "before": "⌚ " - }, - "email_imap_alert": { - "before": "✉ ", - "args": { - "username": "", - "password": "" - } } }, "segments": { diff --git a/powerline/config_files/themes/vim/plugin/ctrlp.json b/powerline/config_files/themes/vim/plugin_ctrlp.json similarity index 100% rename from powerline/config_files/themes/vim/plugin/ctrlp.json rename to powerline/config_files/themes/vim/plugin_ctrlp.json diff --git a/powerline/config_files/themes/vim/plugin/gundo-preview.json b/powerline/config_files/themes/vim/plugin_gundo-preview.json similarity index 85% rename from powerline/config_files/themes/vim/plugin/gundo-preview.json rename to powerline/config_files/themes/vim/plugin_gundo-preview.json index 3ce202ea..cd3b00fe 100644 --- a/powerline/config_files/themes/vim/plugin/gundo-preview.json +++ b/powerline/config_files/themes/vim/plugin_gundo-preview.json @@ -1,5 +1,4 @@ { - "default_module": "powerline.segments.plugin.gundo", "segments": { "left": [ { diff --git a/powerline/config_files/themes/vim/plugin/gundo.json b/powerline/config_files/themes/vim/plugin_gundo.json similarity index 85% rename from powerline/config_files/themes/vim/plugin/gundo.json rename to powerline/config_files/themes/vim/plugin_gundo.json index 0c1a336a..0d6a448e 100644 --- a/powerline/config_files/themes/vim/plugin/gundo.json +++ b/powerline/config_files/themes/vim/plugin_gundo.json @@ -1,5 +1,4 @@ { - "default_module": "powerline.segments.plugin.gundo", "segments": { "left": [ { diff --git a/powerline/config_files/themes/vim/plugin/nerdtree.json b/powerline/config_files/themes/vim/plugin_nerdtree.json similarity index 100% rename from powerline/config_files/themes/vim/plugin/nerdtree.json rename to powerline/config_files/themes/vim/plugin_nerdtree.json diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 37c1c180..2570df41 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -43,9 +43,22 @@ def context_key(context): return key_sep.join((c[0] for c in context)) -class DelayedEchoErr(object): - def __init__(self, echoerr): +class EchoErr(object): + __slots__ = ('echoerr', 'logger',) + + def __init__(self, echoerr, logger): self.echoerr = echoerr + self.logger = logger + + def __call__(self, *args, **kwargs): + self.echoerr(*args, **kwargs) + + +class DelayedEchoErr(EchoErr): + __slots__ = ('echoerr', 'logger', 'errs') + + def __init__(self, echoerr): + super(DelayedEchoErr, self).__init__(echoerr, echoerr.logger) self.errs = [] def __call__(self, *args, **kwargs): @@ -331,7 +344,9 @@ class Spec(object): if khadproblem: hadproblem = True if proceed: - valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr) + proceed, vhadproblem = valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr) + if vhadproblem: + hadproblem = True break else: hadproblem = True @@ -603,7 +618,9 @@ def check_segment_module(module, data, context, echoerr): with WithPath(data['import_paths']): try: __import__(unicode(module)) - except ImportError: + except ImportError as e: + if echoerr.logger.level >= logging.DEBUG: + echoerr.logger.exception(e) echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), problem='failed to import module {0}'.format(module), problem_mark=module.mark) @@ -984,13 +1001,19 @@ theme_spec = (Spec( ).context_message('Error while loading theme')) -def check(path=None): +def check(path=None, debug=False): search_paths = [path] if path else Powerline.get_config_paths() + logger = logging.getLogger('powerline-lint') + logger.setLevel(logging.DEBUG if debug else logging.ERROR) + logger.addHandler(logging.StreamHandler()) + + ee = EchoErr(echoerr, logger) + dirs = { - 'themes': defaultdict(lambda: []), - 'colorschemes': defaultdict(lambda: []) - } + 'themes': defaultdict(lambda: []), + 'colorschemes': defaultdict(lambda: []) + } for path in reversed(search_paths): for subdir in ('themes', 'colorschemes'): d = os.path.join(path, subdir) @@ -1004,14 +1027,16 @@ def check(path=None): sys.stderr.write('Path {0} is supposed to be a directory, but it is not\n'.format(d)) configs = { - 'themes': defaultdict(lambda: {}), - 'colorschemes': defaultdict(lambda: {}) - } + 'themes': defaultdict(lambda: {}), + 'colorschemes': defaultdict(lambda: {}) + } for subdir in ('themes', 'colorschemes'): for ext in dirs[subdir]: for d in dirs[subdir][ext]: for config in os.listdir(d): - if config.endswith('.json'): + if os.path.isdir(os.path.join(d, config)): + dirs[subdir][ext].append(os.path.join(d, config)) + elif config.endswith('.json'): configs[subdir][ext][config[:-5]] = os.path.join(d, config) diff = set(configs['themes']) ^ set(configs['colorschemes']) @@ -1022,7 +1047,7 @@ def check(path=None): ext, 'configuration' if (ext in dirs['themes'] and ext in dirs['colorschemes']) else 'directory', 'themes' if ext in configs['themes'] else 'colorschemes', - )) + )) lhadproblem = [False] @@ -1044,7 +1069,7 @@ def check(path=None): sys.stderr.write(str(e) + '\n') hadproblem = True else: - if main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),))[1]: + if main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),), echoerr=ee)[1]: hadproblem = True import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] @@ -1060,7 +1085,7 @@ def check(path=None): sys.stderr.write(str(e) + '\n') hadproblem = True else: - if colors_spec.match(colors_config, context=(('', colors_config),))[1]: + if colors_spec.match(colors_config, context=(('', colors_config),), echoerr=ee)[1]: hadproblem = True if lhadproblem[0]: @@ -1084,7 +1109,7 @@ def check(path=None): spec = vim_colorscheme_spec else: spec = colorscheme_spec - if spec.match(config, context=(('', config),), data=data)[1]: + if spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: hadproblem = True theme_configs = defaultdict(lambda: {}) @@ -1105,6 +1130,6 @@ def check(path=None): 'main_config': main_config, 'ext_theme_configs': configs, 'colors_config': colors_config} for theme, config in configs.items(): data['theme'] = theme - if theme_spec.match(config, context=(('', config),), data=data)[1]: + if theme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: hadproblem = True return hadproblem diff --git a/powerline/matchers/plugin/ctrlp.py b/powerline/matchers/plugin/ctrlp.py index 50b374a7..d6754798 100644 --- a/powerline/matchers/plugin/ctrlp.py +++ b/powerline/matchers/plugin/ctrlp.py @@ -1,19 +1,22 @@ # vim:fileencoding=utf-8:noet import os -import vim +try: + import vim -vim.command('''function! Powerline_plugin_ctrlp_main(...) - let b:powerline_ctrlp_type = 'main' - let b:powerline_ctrlp_args = a:000 -endfunction''') + vim.command('''function! Powerline_plugin_ctrlp_main(...) + let b:powerline_ctrlp_type = 'main' + let b:powerline_ctrlp_args = a:000 + endfunction''') -vim.command('''function! Powerline_plugin_ctrlp_prog(...) - let b:powerline_ctrlp_type = 'prog' - let b:powerline_ctrlp_args = a:000 -endfunction''') + vim.command('''function! Powerline_plugin_ctrlp_prog(...) + let b:powerline_ctrlp_type = 'prog' + let b:powerline_ctrlp_args = a:000 + endfunction''') -vim.command('''let g:ctrlp_status_func = { 'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog' }''') + vim.command('''let g:ctrlp_status_func = { 'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog' }''') +except ImportError: + vim = object() # NOQA def ctrlp(matcher_info): diff --git a/powerline/segments/plugin/ctrlp.py b/powerline/segments/plugin/ctrlp.py index d8b4e3d8..e5893d7d 100644 --- a/powerline/segments/plugin/ctrlp.py +++ b/powerline/segments/plugin/ctrlp.py @@ -1,10 +1,18 @@ # vim:fileencoding=utf-8:noet -import vim +try: + import vim +except ImportError: + vim = object() # NOQA + from powerline.bindings.vim import getbufvar def ctrlp(pl, side): + ''' + + Highlight groups used: ``ctrlp.regex`` or ``background``, ``ctrlp.prev`` or ``background``, ``ctrlp.item`` or ``file_name``, ``ctrlp.next`` or ``background``, ``ctrlp.marked`` or ``background``, ``ctrlp.focus`` or ``background``, ``ctrlp.byfname`` or ``background``, ``ctrlp.progress`` or ``file_name``, ``ctrlp.progress`` or ``file_name``. + ''' ctrlp_type = getbufvar('%', 'powerline_ctrlp_type') ctrlp_args = getbufvar('%', 'powerline_ctrlp_args') @@ -12,6 +20,10 @@ def ctrlp(pl, side): def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): + ''' + + Highlight groups used: ``ctrlp.regex`` or ``background``, ``ctrlp.prev`` or ``background``, ``ctrlp.item`` or ``file_name``, ``ctrlp.next`` or ``background``, ``ctrlp.marked`` or ``background``. + ''' marked = marked[2:-1] segments = [] @@ -54,6 +66,10 @@ def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): def ctrlp_stl_right_main(pl, focus, byfname, regex, prev, item, next, marked): + ''' + + Highlight groups used: ``ctrlp.focus`` or ``background``, ``ctrlp.byfname`` or ``background``. + ''' segments = [ { 'contents': focus, @@ -72,6 +88,10 @@ def ctrlp_stl_right_main(pl, focus, byfname, regex, prev, item, next, marked): def ctrlp_stl_left_prog(pl, progress): + ''' + + Highlight groups used: ``ctrlp.progress`` or ``file_name``. + ''' return [ { 'contents': 'Loading...', @@ -81,6 +101,10 @@ def ctrlp_stl_left_prog(pl, progress): def ctrlp_stl_right_prog(pl, progress): + ''' + + Highlight groups used: ``ctrlp.progress`` or ``file_name``. + ''' return [ { 'contents': progress, diff --git a/powerline/segments/plugin/nerdtree.py b/powerline/segments/plugin/nerdtree.py index c12ce777..39eb5aec 100644 --- a/powerline/segments/plugin/nerdtree.py +++ b/powerline/segments/plugin/nerdtree.py @@ -1,6 +1,9 @@ # vim:fileencoding=utf-8:noet -import vim +try: + import vim +except ImportError: + vim = object() # NOQA from powerline.bindings.vim import getbufvar from powerline.segments.vim import window_cached @@ -8,6 +11,10 @@ from powerline.segments.vim import window_cached @window_cached def nerdtree(pl): + '''Return directory that is shown by the current buffer. + + Highlight groups used: ``nerdtree.path`` or ``file_name``. + ''' ntr = getbufvar('%', 'NERDTreeRoot') if not ntr: return diff --git a/powerline/segments/plugin/syntastic.py b/powerline/segments/plugin/syntastic.py index 4c470dd9..71a411fa 100644 --- a/powerline/segments/plugin/syntastic.py +++ b/powerline/segments/plugin/syntastic.py @@ -1,12 +1,25 @@ # vim:fileencoding=utf-8:noet -import vim +try: + import vim +except ImportError: + vim = object() # NOQA from powerline.segments.vim import window_cached @window_cached -def syntastic(pl): +def syntastic(pl, err_format='ERR:  {first_line} ({num}) ', warn_format='WARN:  {first_line} ({num}) '): + '''Show whether syntastic has found any errors or warnings + + :param str err_format: + Format string for errors. + + :param str warn_format: + Format string for warnings. + + Highlight groups used: ``syntastic.warning`` or ``warning``, ``syntastic.error`` or ``error``. + ''' if not int(vim.eval('exists("g:SyntasticLoclist")')): return has_errors = int(vim.eval('g:SyntasticLoclist.current().hasErrorsOrWarningsToDisplay()')) @@ -17,12 +30,12 @@ def syntastic(pl): segments = [] if errors: segments.append({ - 'contents': 'ERR:  {line} ({num}) '.format(line=errors[0]['lnum'], num=len(errors)), - 'highlight_group': ['syntastic.error', 'error', 'background'], - }) + 'contents': err_format.format(first_line=errors[0]['lnum'], num=len(errors)), + 'highlight_group': ['syntastic.error', 'error'], + }) if warnings: segments.append({ - 'contents': 'WARN:  {line} ({num}) '.format(line=warnings[0]['lnum'], num=len(warnings)), - 'highlight_group': ['syntastic.warning', 'warning', 'background'], - }) + 'contents': warn_format.format(first_line=warnings[0]['lnum'], num=len(warnings)), + 'highlight_group': ['syntastic.warning', 'warning'], + }) return segments diff --git a/powerline/segments/plugin/tagbar.py b/powerline/segments/plugin/tagbar.py index 07e260f8..24e8db52 100644 --- a/powerline/segments/plugin/tagbar.py +++ b/powerline/segments/plugin/tagbar.py @@ -1,6 +1,9 @@ # vim:fileencoding=utf-8:noet -import vim +try: + import vim +except ImportError: + vim = object() # NOQA from powerline.segments.vim import window_cached diff --git a/scripts/powerline-lint b/scripts/powerline-lint index 6d20e38f..d8a05ca3 100755 --- a/scripts/powerline-lint +++ b/scripts/powerline-lint @@ -8,7 +8,8 @@ import sys parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-p', '--config_path', metavar='PATH') +parser.add_argument('-d', '--debug', action='store_const', const=True) if __name__ == '__main__': args = parser.parse_args() - sys.exit(check(args.config_path)) + sys.exit(check(args.config_path, args.debug)) From 9446ac91f813e79ff5cb83ef7d5f5517f06c1c13 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Nov 2013 02:15:24 +0400 Subject: [PATCH 0786/1472] Make sure flake8 does not show any errors in powerline/lint/**/*.py files --- .local.vimrc | 3 ++- powerline/lint/__init__.py | 1 - powerline/lint/inspect.py | 1 + powerline/lint/markedjson/error.py | 8 ++++---- powerline/lint/markedjson/resolver.py | 4 ++-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.local.vimrc b/.local.vimrc index afd2178e..68bf2234 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -1,2 +1,3 @@ setlocal noexpandtab -let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225,W291' +let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225,W291,E126' +let b:syntastic_checkers = ['flake8'] diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 2570df41..9d7a009b 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -6,7 +6,6 @@ from powerline.lib.config import load_json_config from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes from powerline.lint.inspect import getconfigargspec -from powerline.lint.markedjson.markedvalue import gen_marked_value from powerline.lib.threaded import ThreadedSegment import itertools import sys diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index b0f0c7af..294f2f59 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -4,6 +4,7 @@ from inspect import ArgSpec, getargspec from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from itertools import count + def getconfigargspec(obj): if isinstance(obj, ThreadedSegment): args = ['interval'] diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index d1466675..781b912b 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -53,13 +53,13 @@ class Mark: break snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer + 1:end]] snippet = [strtrans(s) for s in snippet] - return ' ' * indent + head + ''.join(snippet) + tail + '\n' \ - + ' ' * (indent + len(head) + len(snippet[0])) + '^' + return (' ' * indent + head + ''.join(snippet) + tail + '\n' + + ' ' * (indent + len(head) + len(snippet[0])) + '^') def __str__(self): snippet = self.get_snippet() - where = " in \"%s\", line %d, column %d" \ - % (self.name, self.line + 1, self.column + 1) + where = (" in \"%s\", line %d, column %d" + % (self.name, self.line + 1, self.column + 1)) if snippet is not None: where += ":\n" + snippet if type(where) is str: diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py index f628a872..e453f61e 100644 --- a/powerline/lint/markedjson/resolver.py +++ b/powerline/lint/markedjson/resolver.py @@ -71,8 +71,8 @@ class BaseResolver: return if index_check is True and current_index is not None: return - if (index_check is False or index_check is None) \ - and current_index is None: + if ((index_check is False or index_check is None) + and current_index is None): return if isinstance(index_check, str): if not (isinstance(current_index, ScalarNode) From 8d36f4221ace4e4a6f666a2d259711b5b432db02 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Nov 2013 23:15:51 +0400 Subject: [PATCH 0787/1472] Set TMUX_PWD_* on startup Ref #672 --- powerline/bindings/zsh/powerline.zsh | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index e5b8d6d8..9baed0ff 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -42,6 +42,7 @@ _powerline_install_precmd() { trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns +_powerline_tmux_set_pwd setopt promptpercent setopt promptsubst From 37465484184355d6fc31eeaee034871719f40eb8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Nov 2013 23:16:45 +0400 Subject: [PATCH 0788/1472] Do not mention zpython branch It is not usable due to some bugs, not maintained and replaced by powerline-client --- docs/source/overview.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index dd4c06e9..a336a276 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -146,9 +146,6 @@ absolute path to your Powerline installation directory: . {repository_root}/powerline/bindings/zsh/powerline.zsh -If you are not satisfied with powerline speed in this case, compile zpython -branch from https://bitbucket.org/ZyX_I/zsh. - Tmux statusline --------------- From f0476f99685e7a19a301e322d2e60b095496e9e7 Mon Sep 17 00:00:00 2001 From: William Snyders Date: Fri, 15 Nov 2013 11:55:55 -0800 Subject: [PATCH 0789/1472] Update dependanceies for Arch packages. --- packages/archlinux/python-powerline-git/PKGBUILD | 2 +- packages/archlinux/python2-powerline-git/PKGBUILD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD index b49dffbf..f818b77b 100644 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ b/packages/archlinux/python-powerline-git/PKGBUILD @@ -10,7 +10,7 @@ pkgrel=1 url='https://github.com/Lokaltog/powerline' license=('MIT') arch=('any') -makedepends=('git' 'python-distribute') +makedepends=('git' 'python-setuptools') provides=('powerline') conflicts=('python2-powerline-git' 'powerline-git') diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD index e01e70ff..cc262dd5 100644 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ b/packages/archlinux/python2-powerline-git/PKGBUILD @@ -10,7 +10,7 @@ pkgrel=1 url='https://github.com/Lokaltog/powerline' license=('MIT') arch=('any') -makedepends=('git' 'python2-distribute') +makedepends=('git' 'python2-setuptools') provides=('powerline') conflicts=('python-powerline-git') replaces=('powerline-git') From a06fe3ad5f52a3254df9fccb1683318447439c9c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Nov 2013 16:40:15 +0400 Subject: [PATCH 0790/1472] Do not double pl.exception() call, record used pl in variable instead --- powerline/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 53ffd932..629c5302 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -488,8 +488,5 @@ class Powerline(object): def exception(self, msg, *args, **kwargs): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' - try: - return self.pl.exception(msg, *args, **kwargs) - except AttributeError: - pl = _get_fallback_logger() - return pl.exception(msg, *args, **kwargs) + pl = getattr(self, 'pl', None) or _get_fallback_logger() + return pl.exception(msg, *args, **kwargs) From 2ef0d4412d6ed39f62317c30aa23356e11573e12 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Nov 2013 01:01:49 +0400 Subject: [PATCH 0791/1472] Add jobnum segment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces #596. Differences: - Tests and metavar. - Uses “jobnum” name in place of “jobs”. - Does not use subshell for zsh. Also counts jobs correctly in zsh. - Adds an option to force showing jobnum segment even if there are no jobs. --- powerline/bindings/bash/powerline.sh | 2 +- powerline/bindings/zsh/powerline.zsh | 18 ++++++++++++++++-- .../colorschemes/shell/default.json | 1 + .../colorschemes/shell/solarized.json | 1 + .../config_files/themes/shell/default.json | 4 ++++ powerline/segments/shell.py | 15 +++++++++++++++ powerline/shell.py | 1 + tests/test_segments.py | 11 +++++++++++ 8 files changed, 50 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index cc0a22cc..fe9f68a5 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -30,7 +30,7 @@ _powerline_prompt() { local last_exit_code=$? [[ -z "$POWERLINE_OLD_PROMPT_COMMAND" ]] || eval $POWERLINE_OLD_PROMPT_COMMAND - PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code)" + PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs|wc-l)")" _powerline_tmux_set_pwd return $last_exit_code } diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index e5b8d6d8..6ed683a9 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -35,8 +35,22 @@ _powerline_install_precmd() { zpython 'powerline_setup()' zpython 'del powerline_setup' else - PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' - RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt --last_exit_code=$? --last_pipe_status="$pipestatus")' + local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' + # If you are wondering why I am not using the same code as I use for + # bash ($(jobs|wc -l)): consider the following test: + # echo abc | less + # + # . This way jobs will print + # [1] + done echo abc | + # suspended less -M + # ([ is in first column). You see: any line counting thingie will return + # wrong number of jobs. You need to filter the lines first. Or not use + # jobs built-in at all. + # + # This and above variants also do not use subshell. + add_args+=' --jobnum=${(%):-%j}' + PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args')' + RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' fi } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 9a5c5883..61fecdad 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -1,6 +1,7 @@ { "name": "Default color scheme for shell prompts", "groups": { + "jobnum": { "fg": "brightyellow", "bg": "mediumorange" }, "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "virtualenv": { "fg": "white", "bg": "darkcyan" }, diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index 70093ba7..121dd16b 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,6 +1,7 @@ { "name": "Solarized Dark", "groups": { + "jobnum": { "fg": "oldlace", "bg": "darkgreencopper" }, "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, "virtualenv": { "fg": "oldlace", "bg": "green" }, diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 6246a964..89dfa33a 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -30,6 +30,10 @@ "args": { "dir_limit_depth": 3 } + }, + { + "module": "powerline.segments.shell", + "name": "jobnum" } ], "right": [ diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index e870048a..07212059 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -3,6 +3,21 @@ from powerline.theme import requires_segment_info +@requires_segment_info +def jobnum(pl, segment_info, show_zero=False): + '''Return the number of jobs. + + :param bool show_zero: + If False (default) shows nothing if there are no jobs. Otherwise shows + zero for no jobs. + ''' + jobnum = segment_info['args'].jobnum + if jobnum is None or (not show_zero and jobnum == 0): + return None + else: + return str(jobnum) + + @requires_segment_info def last_status(pl, segment_info): '''Return last exit code. diff --git a/powerline/shell.py b/powerline/shell.py index 5ec2355b..b887d323 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -50,6 +50,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-w', '--width', type=int) p.add_argument('--last_exit_code', metavar='INT', type=int) p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) + p.add_argument('--jobnum', metavar='INT', type=int) p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') p.add_argument('-p', '--config_path', metavar='PATH') diff --git a/tests/test_segments.py b/tests/test_segments.py index d9d12527..d74125b9 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -35,6 +35,17 @@ class TestShell(TestCase): {'contents': '0', 'highlight_group': 'exit_success', 'draw_inner_divider': True} ]) + def test_jobnum(self): + pl = Pl() + segment_info = {'args': Args(jobnum=0)} + self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info), None) + self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info, show_zero=False), None) + self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info, show_zero=True), '0') + segment_info = {'args': Args(jobnum=1)} + self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info), '1') + self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info, show_zero=False), '1') + self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info, show_zero=True), '1') + class TestCommon(TestCase): def test_hostname(self): From 6a4b8bc49cf46fe1ac57c32ad2cc3d402cc610e5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Nov 2013 23:51:26 +0400 Subject: [PATCH 0792/1472] Pass run_once to ConfigLoader, make it respect run_once It only respects run_once by using DummyWatcher instead of a watcher doing something potentially useful Ref #711 --- powerline/__init__.py | 2 +- powerline/lib/config.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 1689f1da..4e148f05 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -121,7 +121,7 @@ class Powerline(object): 'load_theme': True, } self.shutdown_event = shutdown_event or Event() - self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event) + self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event, run_once=run_once) self.run_loader_update = False self.renderer_options = {} diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 9cf6ee4a..e826e3bf 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -20,11 +20,22 @@ def load_json_config(config_file_path, load=json.load, open_file=open_file): return load(config_file_fp) +class DummyWatcher(object): + def __call__(self, *args, **kwargs): + return False + + def watch(self, *args, **kwargs): + pass + + class ConfigLoader(MultiRunnedThread): - def __init__(self, shutdown_event=None, watcher=None, load=load_json_config): + def __init__(self, shutdown_event=None, watcher=None, load=load_json_config, run_once=False): super(ConfigLoader, self).__init__() self.shutdown_event = shutdown_event or Event() - self.watcher = watcher or create_file_watcher() + if run_once: + self.watcher = DummyWatcher() + else: + self.watcher = watcher or create_file_watcher() self._load = load self.pl = None From 95d13a334a3057cbe3a896a00bc1a30b898def79 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Nov 2013 00:18:34 +0400 Subject: [PATCH 0793/1472] Test that nothing is reloaded with run_once=True --- tests/lib/config_mock.py | 18 +++++++++++++++++- tests/test_config_reload.py | 20 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 7243fcb3..82d1d18a 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -4,6 +4,7 @@ from powerline.renderer import Renderer from powerline.lib.config import ConfigLoader from powerline import Powerline from copy import deepcopy +from functools import wraps access_log = [] @@ -34,13 +35,26 @@ def pop_events(): return r +def log_call(func): + @wraps(func) + def ret(self, *args, **kwargs): + self._calls.append((func.__name__, args, kwargs)) + return func(self, *args, **kwargs) + return ret + + class Watcher(object): events = set() lock = Lock() + def __init__(self): + self._calls = [] + + @log_call def watch(self, file): pass + @log_call def __call__(self, file): with self.lock: if file in self.events: @@ -48,11 +62,13 @@ class Watcher(object): return True return False + @log_call def _reset(self, files): with self.lock: self.events.clear() self.events.update(files) + @log_call def unsubscribe(self): pass @@ -100,7 +116,7 @@ def get_powerline(**kwargs): ext='test', renderer_module='tests.lib.config_mock', logger=Logger(), - config_loader=ConfigLoader(load=load_json_config, watcher=Watcher()), + config_loader=ConfigLoader(load=load_json_config, watcher=Watcher(), run_once=kwargs.get('run_once')), **kwargs ) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index b58eaff8..7b0bb55b 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -97,7 +97,10 @@ def sleep(interval): def add_watcher_events(p, *args, **kwargs): - p.config_loader.watcher._reset(args) + try: + p.config_loader.watcher._reset(args) + except AttributeError: + return while not p._will_create_renderer(): sleep(kwargs.get('interval', 0.000001)) if not kwargs.get('wait', True): @@ -118,7 +121,6 @@ class TestConfigReload(TestCase): # When running once thread should not start self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents() - self.assertEqual(p.logger._pop_msgs(), []) # Without the following assertion test_reload_colors may fail for # unknown reason (with AssertionError telling about “config” accessed # one more time then needed) @@ -253,6 +255,20 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents('themes/test/default') self.assertEqual(p.logger._pop_msgs(), []) + self.assertTrue(p.config_loader.watcher._calls) + pop_events() + + def test_run_once_no_theme_reload(self): + with replace_item(globals(), 'config', deepcopy(config)): + config['config']['common']['interval'] = None + with get_powerline(run_once=True) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + + config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' + add_watcher_events(p, 'themes/test/default', wait=False) + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents() pop_events() From bb0d9028f13b33c28e27380c57bae795643e047c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Nov 2013 00:23:25 +0400 Subject: [PATCH 0794/1472] Make sure watcher is not used --- tests/lib/config_mock.py | 8 +++++--- tests/test_config_reload.py | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 82d1d18a..a0c083df 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -62,7 +62,6 @@ class Watcher(object): return True return False - @log_call def _reset(self, files): with self.lock: self.events.clear() @@ -112,13 +111,16 @@ renderer = SimpleRenderer def get_powerline(**kwargs): - return TestPowerline( + watcher = Watcher() + pl = TestPowerline( ext='test', renderer_module='tests.lib.config_mock', logger=Logger(), - config_loader=ConfigLoader(load=load_json_config, watcher=Watcher(), run_once=kwargs.get('run_once')), + config_loader=ConfigLoader(load=load_json_config, watcher=watcher, run_once=kwargs.get('run_once')), **kwargs ) + pl._watcher = watcher + return pl config_container = None diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 7b0bb55b..f74aa411 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -97,10 +97,7 @@ def sleep(interval): def add_watcher_events(p, *args, **kwargs): - try: - p.config_loader.watcher._reset(args) - except AttributeError: - return + p._watcher._reset(args) while not p._will_create_renderer(): sleep(kwargs.get('interval', 0.000001)) if not kwargs.get('wait', True): @@ -121,6 +118,7 @@ class TestConfigReload(TestCase): # When running once thread should not start self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents() + self.assertEqual(p.logger._pop_msgs(), []) # Without the following assertion test_reload_colors may fail for # unknown reason (with AssertionError telling about “config” accessed # one more time then needed) @@ -255,7 +253,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents('themes/test/default') self.assertEqual(p.logger._pop_msgs(), []) - self.assertTrue(p.config_loader.watcher._calls) + self.assertTrue(p._watcher._calls) pop_events() def test_run_once_no_theme_reload(self): @@ -269,6 +267,8 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'themes/test/default', wait=False) self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents() + self.assertEqual(p.logger._pop_msgs(), []) + self.assertEqual(p._watcher._calls, []) pop_events() From 1e0f13aedeb32cc6dff2da226fdecdaef1fd5b3a Mon Sep 17 00:00:00 2001 From: Tao Wu Date: Thu, 21 Nov 2013 14:23:14 +0100 Subject: [PATCH 0795/1472] add Solarized Light colour theme for Vim --- .../colorschemes/vim/solarizedlight.json | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 powerline/config_files/colorschemes/vim/solarizedlight.json diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json new file mode 100644 index 00000000..b044cd48 --- /dev/null +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -0,0 +1,94 @@ +{ + "name": "Solarized Dark", + "groups": { + "background": { "fg": "gray13", "bg": "darkgreencopper" }, + "background:divider": { "fg": "azure4", "bg": "darkgreencopper" }, + "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, + "paste_indicator": { "fg": "red", "bg": "lightyellow", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "lightyellow" }, + "branch": { "fg": "royalblue5", "bg": "lightyellow" }, + "branch_dirty": { "fg": "yellow", "bg": "lightyellow" }, + "branch_clean": { "fg": "royalblue5", "bg": "lightyellow" }, + "branch:divider": { "fg": "gray61", "bg": "lightyellow" }, + "file_directory": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_name": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, + "file_size": { "fg": "gray13", "bg": "lightyellow" }, + "file_name_no_file": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, + "file_name_empty": { "fg": "gray13", "bg": "lightyellow" }, + "file_format": { "fg": "gray61", "bg": "darkgreencopper" }, + "file_encoding": { "fg": "gray61", "bg": "darkgreencopper" }, + "file_type": { "fg": "gray61", "bg": "darkgreencopper" }, + "file_vcs_status": { "fg": "red", "bg": "lightyellow" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow" }, + "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, + "line_percent": { "fg": "gray13", "bg": "lightyellow" }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow" }, + "line_current": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] }, + "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5" }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5" }, + "col_current": { "fg": "lightskyblue4", "bg": "royalblue5" }, + "environment": { "fg": "gray61", "bg": "darkgreencopper" } + }, + "mode_translations": { + "nc": { + "colors": { + "lightyellow": "darkgreencopper", + "azure4": "darkgreencopper", + "lightskyblue4": "lightyellow", + "gray61": "azure4", + "royalblue5": "lightskyblue4", + "gray13": "gray61" + } + }, + "i": { + "groups": { + "background": { "fg": "gray13", "bg": "lightyellow" }, + "background:divider": { "fg": "royalblue5", "bg": "lightyellow" }, + "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, + "modified_indicator": { "fg": "yellow", "bg": "royalblue5", "attr": ["bold"] }, + "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "red", "bg": "royalblue5" }, + "branch": { "fg": "lightyellow", "bg": "royalblue5" }, + "branch:divider": { "fg": "azure4", "bg": "royalblue5" }, + "file_directory": { "fg": "lightyellow", "bg": "royalblue5" }, + "file_name": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, + "file_size": { "fg": "darkgreencopper", "bg": "royalblue5" }, + "file_name_no_file": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, + "file_name_empty": { "fg": "darkgreencopper", "bg": "royalblue5" }, + "file_format": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_encoding": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_type": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_vcs_status": { "fg": "red", "bg": "royalblue5" }, + "file_vcs_status_M": { "fg": "yellow", "bg": "royalblue5" }, + "file_vcs_status_A": { "fg": "green", "bg": "royalblue5" }, + "line_percent": { "fg": "gray13", "bg": "gray61" }, + "line_percent_gradient": { "fg": "gray13", "bg": "gray61" }, + "line_current": { "fg": "oldlace", "bg": "gray13", "attr": ["bold"] }, + "line_current_symbol": { "fg": "oldlace", "bg": "gray13" }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "gray13" }, + "col_current": { "fg": "lightskyblue4", "bg": "gray13" } + } + }, + "v": { + "groups": { + "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + } + }, + "V": { + "groups": { + "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + } + }, + "^V": { + "groups": { + "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + } + }, + "R": { + "groups": { + "mode": { "fg": "oldlace", "bg": "red", "attr": ["bold"] } + } + } + } +} From 4836cbb43e2c91a8e8cf5783e4e29708d803bf0c Mon Sep 17 00:00:00 2001 From: Tao Wu Date: Thu, 21 Nov 2013 16:03:58 +0100 Subject: [PATCH 0796/1472] Update solarizedlight.json Update name to "Solarized Light". --- powerline/config_files/colorschemes/vim/solarizedlight.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index b044cd48..51ea0fb3 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -1,5 +1,5 @@ { - "name": "Solarized Dark", + "name": "Solarized Light", "groups": { "background": { "fg": "gray13", "bg": "darkgreencopper" }, "background:divider": { "fg": "azure4", "bg": "darkgreencopper" }, From ccf070cbcd6b934f5e867b66507d712dbdb64373 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Dec 2013 14:23:29 +0400 Subject: [PATCH 0797/1472] Remove Gentoo ebuild and reference raiagent overlay --- docs/source/installation/linux.rst | 2 +- packages/gentoo/app-misc/powerline/Manifest | 1 - .../app-misc/powerline/powerline-9999.ebuild | 139 ------------------ 3 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 packages/gentoo/app-misc/powerline/Manifest delete mode 100644 packages/gentoo/app-misc/powerline/powerline-9999.ebuild diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index fd25d649..74e6f74f 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -13,7 +13,7 @@ still skim through this guide so you know how the plugin works. * `Arch Linux (AUR), Python 2 version `_ * `Arch Linux (AUR), Python 3 version `_ -* Gentoo Live ebuild (:file:`packages/gentoo/app-misc/powerline/`) +* Gentoo Live ebuild in `raiagent `_ overlay If you're running a distribution without an official package you'll have to follow the installation guide below: diff --git a/packages/gentoo/app-misc/powerline/Manifest b/packages/gentoo/app-misc/powerline/Manifest deleted file mode 100644 index cca6d4dd..00000000 --- a/packages/gentoo/app-misc/powerline/Manifest +++ /dev/null @@ -1 +0,0 @@ -EBUILD powerline-9999.ebuild 4107 SHA256 721f3360196aa4caa3656ff6e051add2c70cbfc37e965fce9830ecb668148b81 SHA512 27660689e5a0cf86d17268a18fdd917abfb8b2d88b33049fe66f3394c9a85211f24e50af8df1f39d93d11e8bfd442e4636ed92821441daddb56ae9d34162296a WHIRLPOOL 602a2d0d36b1e80da9d1f00c019872369010cd413c4761c1f92a8f53ee5bfb63122d8c13e896700406bf71d99db5994bcd3c7ae46c07218d173bd7084f1a13f1 diff --git a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild b/packages/gentoo/app-misc/powerline/powerline-9999.ebuild deleted file mode 100644 index be065566..00000000 --- a/packages/gentoo/app-misc/powerline/powerline-9999.ebuild +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 1999-2013 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/gentoo-x86/dev-python/setuptools/setuptools-9999.ebuild,v 1.1 2013/01/11 09:59:31 mgorny Exp $ - -EAPI="5" -PYTHON_COMPAT=( python{2_6,2_7,3_2,3_3} ) - -#if LIVE -EGIT_REPO_URI="https://github.com/Lokaltog/${PN}" -EGIT_BRANCH="develop" -inherit git -#endif - -inherit distutils-r1 eutils font -DESCRIPTION="The ultimate statusline/prompt utility." -HOMEPAGE="http://github.com/Lokaltog/powerline" -SRC_URI="" - -LICENSE="MIT" -SLOT="0" -KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~amd64-fbsd ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris" -IUSE="vim zsh doc awesome tmux bash ipython test git" - -#if LIVE -SRC_URI= -KEYWORDS= -#endif - -S="${WORKDIR}/${PN}" - -COMMON_DEPEND=" - virtual/python-argparse -" -RDEPEND=" - ${COMMON_DEPEND} - vim? ( || ( app-editors/vim[python] app-editors/gvim[python] ) ) - awesome? ( >=x11-wm/awesome-3.5 ) - git? ( || ( >=dev-vcs/git-1.7.2 >=dev-python/pygit2-0.17 ) ) -" -DEPEND=" - ${COMMON_DEPEND} - doc? ( dev-python/sphinx dev-python/docutils ) - test? ( - python_targets_python2_6? ( virtual/python-unittest2 ) - || ( >=dev-vcs/git-1.7.2 >=dev-python/pygit2-0.17 ) - python_targets_python2_6? ( - dev-vcs/mercurial - dev-vcs/bzr - ) - python_targets_python2_7? ( - dev-vcs/mercurial - dev-vcs/bzr - ) - ) -" - -FONT_SUFFIX="otf" -FONT_S="${S}/font" - -FONT_CONF=( - "${FONT_S}/10-powerline-symbols.conf" -) - -python_test() { - PYTHON="${PYTHON}" tests/test.sh || die "Tests fail with ${EPYTHON}" -} - -src_compile() { - distutils-r1_src_compile - if use doc ; then - einfo "Generating documentation" - sphinx-build -b html docs/source docs_output - fi -} - -src_install() { - unset DOCS - font_src_install - if use vim ; then - insinto /usr/share/vim/vimfiles/plugin - # Don't do sys.path.append, it points to wrong location - sed -i -e '/sys\.path\.append/d' powerline/bindings/vim/plugin/powerline.vim - doins powerline/bindings/vim/plugin/powerline.vim - fi - rm powerline/bindings/vim/plugin/powerline.vim - if use zsh ; then - insinto /usr/share/zsh/site-contrib - doins powerline/bindings/zsh/powerline.zsh - elog "" - elog "To enable powerline prompt in zsh add" - elog " . /usr/share/zsh/site-contrib/powerline.zsh" - elog "to your .zshrc." - fi - rm powerline/bindings/zsh/powerline.zsh - if use awesome ; then - elog "" - elog "To enable powerline statusline in awesome add" - elog " require(\"powerline\")" - elog "and" - elog " right_layout:add(powerline_widget)" - elog "to your .config/awesome/rc.lua. Assuming you were using" - elog "/etc/xdg/awesome/rc.lua as a template for you own configuration." - insinto /usr/share/awesome/lib/powerline - mv powerline/bindings/awesome/powerline.lua init.lua - doins init.lua - rm init.lua - exeinto /usr/share/awesome/lib/powerline - doexe powerline/bindings/awesome/powerline-awesome.py - else - rm powerline/bindings/awesome/powerline.lua - fi - rm powerline/bindings/awesome/powerline-awesome.py - # There are no standard location for this, thus using /usr/share/powerline - if use tmux ; then - elog "" - elog "To enable powerline statusline in tmux add" - elog " source /usr/share/powerline/tmux/powerline.conf" - elog "to your .tmux.conf." - insinto /usr/share/powerline/tmux - doins powerline/bindings/tmux/powerline.conf - fi - rm powerline/bindings/tmux/powerline.conf - if use bash ; then - insinto /usr/share/powerline/bash - doins powerline/bindings/bash/powerline.sh - elog "" - elog "To enable powerline prompt in bash add" - elog " . /usr/share/powerline/bash/powerline.sh" - elog "to your .bashrc/.profile." - fi - rm powerline/bindings/bash/powerline.sh - elog "" - insinto /etc/xdg/powerline - doins -r powerline/config_files/* - rm -r powerline/config_files - sed -i -e "/DEFAULT_SYSTEM_CONFIG_DIR/ s@None@'/etc/xdg'@" powerline/__init__.py - distutils-r1_src_install - use doc && dohtml -r docs_output/* -} From fb7eec298588ba199f6689ad029675e69da2157b Mon Sep 17 00:00:00 2001 From: Glandos Date: Sun, 1 Dec 2013 12:11:53 +0100 Subject: [PATCH 0798/1472] Don't show '//' when using path separator Currently, the cwd segment is showing //etc when we are in /etc and / when we are in the root file. This small patch fix this --- powerline/segments/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f13f6f8c..d77da305 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -115,7 +115,7 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s for part in cwd: if not part: continue - if use_path_separator: + if use_path_separator and not part == os.sep: part += os.sep ret.append({ 'contents': part, @@ -123,7 +123,7 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s 'draw_inner_divider': draw_inner_divider, }) ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] - if use_path_separator: + if use_path_separator and len(ret) > 1: ret[-1]['contents'] = ret[-1]['contents'][:-1] return ret From c97bc8249fa0b1045c1b7fe92835383088504f9b Mon Sep 17 00:00:00 2001 From: Glandos Date: Sun, 1 Dec 2013 12:35:12 +0100 Subject: [PATCH 0799/1472] Fix display for home directory --- powerline/segments/common.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index d77da305..63f17169 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -115,7 +115,7 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s for part in cwd: if not part: continue - if use_path_separator and not part == os.sep: + if use_path_separator: part += os.sep ret.append({ 'contents': part, @@ -123,8 +123,10 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s 'draw_inner_divider': draw_inner_divider, }) ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] - if use_path_separator and len(ret) > 1: + if use_path_separator: ret[-1]['contents'] = ret[-1]['contents'][:-1] + if len(ret) > 1 and ret[0]['contents'][0] == os.sep: + ret[0]['contents'] = ret[0]['contents'][1:] return ret From 24d4d74e64fc53da5da30c598e38e4c6ca563c3d Mon Sep 17 00:00:00 2001 From: Brice Waegeneire Date: Thu, 12 Dec 2013 20:32:06 +0100 Subject: [PATCH 0800/1472] Add Meslo fonts to the fontconfig file --- font/10-powerline-symbols.conf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/font/10-powerline-symbols.conf b/font/10-powerline-symbols.conf index 7c23e0b7..6f689ea6 100644 --- a/font/10-powerline-symbols.conf +++ b/font/10-powerline-symbols.conf @@ -66,4 +66,28 @@ Ubuntu Mono PowerlineSymbols + + Meslo LG L + PowerlineSymbols + + + Meslo LG L DZ + PowerlineSymbols + + + Meslo LG M + PowerlineSymbols + + + Meslo LG M DZ + PowerlineSymbols + + + Meslo LG S + PowerlineSymbols + + + Meslo LG S DZ + PowerlineSymbols + From e56bf3606b975c828ae20aae085933ad92820699 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 10 Jan 2014 23:01:32 +0400 Subject: [PATCH 0801/1472] Add Args.jobnum property Fixes #742 --- powerline/bindings/zsh/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index ede6870a..5acdc39f 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -53,6 +53,11 @@ class Args(object): except IndexError: return None + @property + def jobnum(self): + zsh.eval('integer POWERLINE_JOBNUM=${(%):-%j}') + return zsh.getvalue('POWERLINE_JOBNUM') + def string(s): if type(s) is bytes: From 4f804686451d256bcb2e8211bd9a157143c3f2db Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 10 Jan 2014 23:17:06 +0400 Subject: [PATCH 0802/1472] Fix tests broken due to jobnum introduction --- tests/test_configuration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 19b9bb04..93857ec4 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -76,7 +76,7 @@ class TestConfig(TestCase): def test_zsh(self): from powerline.shell import ShellPowerline - args = Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt') + args = Args(last_pipe_status=[1, 0], jobnum=0, ext=['shell'], renderer_module='zsh_prompt') with ShellPowerline(args, run_once=False) as powerline: powerline.render(segment_info={'args': args}) with ShellPowerline(args, run_once=False) as powerline: @@ -84,7 +84,7 @@ class TestConfig(TestCase): def test_bash(self): from powerline.shell import ShellPowerline - args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config={'ext': {'shell': {'theme': 'default_leftonly'}}}) + args = Args(last_exit_code=1, jobnum=0, ext=['shell'], renderer_module='bash_prompt', config={'ext': {'shell': {'theme': 'default_leftonly'}}}) with ShellPowerline(args, run_once=False) as powerline: powerline.render(segment_info={'args': args}) with ShellPowerline(args, run_once=False) as powerline: From e152ecfa9a91ccdeb8d28a42d28c8ecc1c9a1b8c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 10 Jan 2014 23:22:54 +0400 Subject: [PATCH 0803/1472] Add missing groups to solarizedlight --- powerline/config_files/colorschemes/vim/solarizedlight.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index 51ea0fb3..f91f5175 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -4,6 +4,7 @@ "background": { "fg": "gray13", "bg": "darkgreencopper" }, "background:divider": { "fg": "azure4", "bg": "darkgreencopper" }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, + "visual_range": { "fg": "green", "bg": "oldlace", "attr": ["bold"] }, "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, "paste_indicator": { "fg": "red", "bg": "lightyellow", "attr": ["bold"] }, "readonly_indicator": { "fg": "red", "bg": "lightyellow" }, @@ -28,7 +29,10 @@ "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5" }, "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5" }, "col_current": { "fg": "lightskyblue4", "bg": "royalblue5" }, - "environment": { "fg": "gray61", "bg": "darkgreencopper" } + "environment": { "fg": "gray61", "bg": "darkgreencopper" }, + "error": { "fg": "gray13", "bg": "red", "attr": ["bold"] }, + "warning": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, + "current_tag": { "fg": "gray13", "bg": "darkgreencopper", "attr": ["bold"] } }, "mode_translations": { "nc": { From 01f445bd677d95f4e4a186caf114d48854e6bb36 Mon Sep 17 00:00:00 2001 From: cyberdork33 Date: Fri, 10 Jan 2014 15:25:20 -0600 Subject: [PATCH 0804/1472] Update powerline.sh I think this was a typo. broke my system. --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 57f6e794..4e4d52de 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -33,7 +33,7 @@ _powerline_prompt() { local last_exit_code=$? [[ -z "$POWERLINE_OLD_PROMPT_COMMAND" ]] || eval $POWERLINE_OLD_PROMPT_COMMAND - PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs|wc-l)")" + PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs|wc -l)")" _powerline_tmux_set_pwd return $last_exit_code } From 54e7fe91ba569fd3bee00364a7310417334e8f79 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 10 Jan 2014 14:13:25 -0800 Subject: [PATCH 0805/1472] Fix common.user test The username was being cached from previous tests, so clear it before running this test. --- tests/test_segments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index d74125b9..fea6e55d 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -73,6 +73,7 @@ class TestCommon(TestCase): new_psutil = new_module('psutil', Process=lambda pid: Args(username='def')) pl = Pl() with replace_env('USER', 'def') as segment_info: + common.username = False with replace_attr(common, 'os', new_os): with replace_attr(common, 'psutil', new_psutil): with replace_attr(common, '_geteuid', lambda: 5): From 64a32a910bca5ea6490a66a5c1660fe675c8dcad Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 11:50:28 +0400 Subject: [PATCH 0806/1472] Add window_cached decorator for ctrlp segment --- powerline/segments/plugin/ctrlp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/plugin/ctrlp.py b/powerline/segments/plugin/ctrlp.py index e5893d7d..3c6af70d 100644 --- a/powerline/segments/plugin/ctrlp.py +++ b/powerline/segments/plugin/ctrlp.py @@ -6,8 +6,10 @@ except ImportError: vim = object() # NOQA from powerline.bindings.vim import getbufvar +from powerline.segments.vim import window_cached +@window_cached def ctrlp(pl, side): ''' From e8b2054868a9ef80183c217e71f933c56c8cf617 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 11:51:41 +0400 Subject: [PATCH 0807/1472] Make nerdtree segment use bufvar_exists --- powerline/bindings/vim/__init__.py | 12 ++++++++++++ powerline/segments/plugin/nerdtree.py | 7 +++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 6705222d..ef8643ff 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -66,6 +66,18 @@ else: else: raise KeyError(varname) +if hasattr(vim, 'vars') and vim.vvars['version'] > 703: + def bufvar_exists(buffer, varname): + buffer = buffer or vim.current.buffer + return varname in buffer.vars +else: + def bufvar_exists(buffer, varname): # NOQA + if not buffer or buffer.number == vim.current.buffer.number: + return vim.eval('exists("b:{0}")'.format(varname)) + else: + return vim.eval('has_key(getbufvar({0}, ""), {1})' + .format(buffer.number, varname)) + if hasattr(vim, 'options'): def vim_getbufoption(info, option): return info['buffer'].options[option] diff --git a/powerline/segments/plugin/nerdtree.py b/powerline/segments/plugin/nerdtree.py index 39eb5aec..a8e6ad85 100644 --- a/powerline/segments/plugin/nerdtree.py +++ b/powerline/segments/plugin/nerdtree.py @@ -5,7 +5,7 @@ try: except ImportError: vim = object() # NOQA -from powerline.bindings.vim import getbufvar +from powerline.bindings.vim import bufvar_exists from powerline.segments.vim import window_cached @@ -15,9 +15,8 @@ def nerdtree(pl): Highlight groups used: ``nerdtree.path`` or ``file_name``. ''' - ntr = getbufvar('%', 'NERDTreeRoot') - if not ntr: - return + if not bufvar_exists(None, 'NERDTreeRoot'): + return None path_str = vim.eval('getbufvar("%", "NERDTreeRoot").path.str()') return [{ 'contents': path_str, From 73d7b0db08288d0576a59721f450f2bf34e336a5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 11:51:58 +0400 Subject: [PATCH 0808/1472] Add functional tests for plugin themes --- tests/test_configuration.py | 10 +++++++++- tests/vim.py | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 93857ec4..672c31b7 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -23,11 +23,15 @@ class TestConfig(TestCase): (('bufoptions',), {'buftype': 'help'}), (('bufname', '[Command Line]'), {}), (('bufoptions',), {'buftype': 'quickfix'}), + (('bufname', 'NERD_tree_1'), {}), + (('bufname', '__Gundo__'), {}), + (('bufname', '__Gundo_Preview__'), {}), + (('bufname', 'ControlP'), {}), ) with open(os.path.join(cfg_path, 'config.json'), 'r') as f: local_themes_raw = json.load(f)['ext']['vim']['local_themes'] # Don't run tests on external/plugin segments - local_themes = dict((k, v) for (k, v) in local_themes_raw.items() if not '.' in k) + local_themes = dict((k, v) for (k, v) in local_themes_raw.items()) self.assertEqual(len(buffers), len(local_themes)) outputs = {} i = 0 @@ -58,6 +62,10 @@ class TestConfig(TestCase): i += 1 if mode in exclude: continue + if mode == 'nc' and args == ('bufname', 'ControlP'): + # ControlP window is not supposed to not + # be in the focus + continue with vim_module._with(*args, **kwargs): check_output(mode, args, kwargs) finally: diff --git a/tests/vim.py b/tests/vim.py index 91a94886..bcc549b6 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -171,9 +171,13 @@ def eval(expr): return options[expr[1:]] elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) - return "0" + return '0' elif expr.startswith('exists('): return '0' + elif expr == 'getbufvar("%", "NERDTreeRoot").path.str()': + import os + assert os.path.basename(buffers[_buffer()].name).startswith('NERD_tree_') + return '/usr/include' raise NotImplementedError @@ -205,6 +209,7 @@ def _emul_mode(*args): @_vim @_str_func def _emul_getbufvar(bufnr, varname): + import re if varname[0] == '&': if bufnr == '%': bufnr = buffers[_buffer()].number @@ -217,6 +222,12 @@ def _emul_getbufvar(bufnr, varname): return options[varname[1:]] except KeyError: return '' + elif re.match('^[a-zA-Z_]+$', varname): + if bufnr == '%': + bufnr = buffers[_buffer()].number + if bufnr not in buffers: + return '' + return buffers[bufnr].vars[varname] raise NotImplementedError @@ -625,10 +636,14 @@ class _WithBufName(object): self.new = new def __enter__(self): + import os buffer = buffers[_buffer()] self.buffer = buffer self.old = buffer.name buffer.name = self.new + if buffer.name and os.path.basename(buffer.name) == 'ControlP': + buffer.vars['powerline_ctrlp_type'] = 'main' + buffer.vars['powerline_ctrlp_args'] = ['focus', 'byfname', '0', 'prev', 'item', 'next', 'marked'] def __exit__(self, *args): self.buffer.name = self.old From bd4173eb1fdff221c338930d0b53fa55e9a8726c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 12:43:13 +0400 Subject: [PATCH 0809/1472] Add jobnum segment to default_leftonly theme --- powerline/config_files/themes/shell/default_leftonly.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 16af9755..5060e1ea 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -34,6 +34,10 @@ "dir_limit_depth": 3 } }, + { + "module": "powerline.segments.shell", + "name": "jobnum" + }, { "name": "last_status", "module": "powerline.segments.shell" From fb00a9586b37d23d7f9525c66e22040c5a6a92d7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 12:50:50 +0400 Subject: [PATCH 0810/1472] Fix copy-paste typo in shell.py --- powerline/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/shell.py b/powerline/shell.py index b887d323..aa04dc3d 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -62,6 +62,6 @@ def finish_args(args): if args.config: args.config = mergeargs((parsedotval(v) for v in args.config)) if args.theme_option: - args.theme_option = mergeargs((parsedotval(v) for v in args.config)) + args.theme_option = mergeargs((parsedotval(v) for v in args.theme_option)) else: args.theme_option = {} From f20792bb380154236daa0114912a98cd2b204ac8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 13:16:25 +0400 Subject: [PATCH 0811/1472] Add bash functional tests --- tests/test.sh | 6 +++++ tests/test_shells.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100755 tests/test_shells.sh diff --git a/tests/test.sh b/tests/test.sh index e5b137ff..47b3a00e 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -4,10 +4,12 @@ FAILED=0 export PYTHONPATH="${PYTHONPATH}:`realpath .`" for file in tests/test_*.py ; do if ! ${PYTHON} $file --verbose --catch ; then + echo "Failed test(s) from $file" FAILED=1 fi done if ! ${PYTHON} scripts/powerline-lint -p powerline/config_files ; then + echo "Failed powerline-lint" FAILED=1 fi for script in tests/*.vim ; do @@ -18,4 +20,8 @@ for script in tests/*.vim ; do FAILED=1 fi done +if ! sh tests/test_shells.sh ; then + echo "Failed shells" + FAILED=1 +fi exit $FAILED diff --git a/tests/test_shells.sh b/tests/test_shells.sh new file mode 100755 index 00000000..5da7ffac --- /dev/null +++ b/tests/test_shells.sh @@ -0,0 +1,56 @@ +#!/bin/sh +FAILED=0 +if [ "$(echo '\e')" != '\e' ] ; then + safe_echo() { + echo -E "$@" + } +else + safe_echo() { + echo "$@" + } +fi +mkdir tests/shell +git init tests/shell/bash +git --git-dir=tests/shell/bash/.git checkout -b BRANCH +INPUT=' +POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +VIRTUAL_ENV= +COLUMNS=80 +source powerline/bindings/bash/powerline.sh ; cd tests/shell/bash +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +cd .git +cd .. +VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +VIRTUAL_ENV= +bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +false +kill `cat pid` ; sleep 1s +false +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +exit +' +OUTPUT="`safe_echo "$INPUT" | LANG=C bash -i 2>&1 | sed 's/ \+\x08\+//g' | tail -n +6`" +OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/bash/pid)/PID/g -e 's/\x1b/\\\\e/g'`" +EXPECTED_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mcd .git +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mbash \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m.git \e[0;38;5;240;49;22m \e[0mcd .. +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;74;22m \e[0;38;5;231;48;5;74mⓔ  some-virtual-environment \e[0;38;5;74;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV= +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mbash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +[1] PID +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;49;22m \e[0mfalse +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mkill `cat pid` ; sleep 1s +[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mfalse +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +\e[0;38;5;220;48;5;166m  zyx-desktop \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mexit +exit' +if [ "b$EXPECTED_OUTPUT" != "b$OUTPUT" ] ; then + safe_echo "$EXPECTED_OUTPUT" > tests/shell/expected + safe_echo "$OUTPUT" > tests/shell/actual + diff -u tests/shell/expected tests/shell/actual + rm tests/shell/expected tests/shell/actual + FAILED=1 +fi +rm -r tests/shell +exit $FAILED From 301dbf2bd3eb80acef7850cea02f4218d403a711 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 13:31:57 +0400 Subject: [PATCH 0812/1472] Add a few empty lines, rename bash directory to 3rd --- tests/test_shells.sh | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/tests/test_shells.sh b/tests/test_shells.sh index 5da7ffac..9b6b09e3 100755 --- a/tests/test_shells.sh +++ b/tests/test_shells.sh @@ -1,5 +1,6 @@ #!/bin/sh FAILED=0 + if [ "$(echo '\e')" != '\e' ] ; then safe_echo() { echo -E "$@" @@ -9,14 +10,16 @@ else echo "$@" } fi + mkdir tests/shell -git init tests/shell/bash -git --git-dir=tests/shell/bash/.git checkout -b BRANCH +git init tests/shell/3rd +git --git-dir=tests/shell/3rd/.git checkout -b BRANCH + INPUT=' POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" VIRTUAL_ENV= COLUMNS=80 -source powerline/bindings/bash/powerline.sh ; cd tests/shell/bash +source powerline/bindings/bash/powerline.sh ; cd tests/shell/3rd POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" cd .git cd .. @@ -29,22 +32,25 @@ false POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" exit ' + OUTPUT="`safe_echo "$INPUT" | LANG=C bash -i 2>&1 | sed 's/ \+\x08\+//g' | tail -n +6`" -OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/bash/pid)/PID/g -e 's/\x1b/\\\\e/g'`" -EXPECTED_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mcd .git -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mbash \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m.git \e[0;38;5;240;49;22m \e[0mcd .. -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;74;22m \e[0;38;5;231;48;5;74mⓔ  some-virtual-environment \e[0;38;5;74;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV= -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mbash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/3rd/pid)/PID/g -e 's/\x1b/\\\\e/g'`" + +EXPECTED_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mcd .git +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240m3rd \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m.git \e[0;38;5;240;49;22m \e[0mcd .. +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;74;22m \e[0;38;5;231;48;5;74mⓔ  some-virtual-environment \e[0;38;5;74;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV= +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mbash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & [1] PID -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;49;22m \e[0mfalse -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mkill `cat pid` ; sleep 1s +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;49;22m \e[0mfalse +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mkill `cat pid` ; sleep 1s [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mfalse -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" -\e[0;38;5;220;48;5;166m  zyx-desktop \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1mbash \e[0;38;5;240;49;22m \e[0mexit +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mfalse +\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +\e[0;38;5;220;48;5;166m  zyx-desktop \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mexit exit' + if [ "b$EXPECTED_OUTPUT" != "b$OUTPUT" ] ; then safe_echo "$EXPECTED_OUTPUT" > tests/shell/expected safe_echo "$OUTPUT" > tests/shell/actual @@ -52,5 +58,7 @@ if [ "b$EXPECTED_OUTPUT" != "b$OUTPUT" ] ; then rm tests/shell/expected tests/shell/actual FAILED=1 fi + rm -r tests/shell + exit $FAILED From ed99b09e8630f6bee14a6073f3436e812eccf6f9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 13:36:02 +0400 Subject: [PATCH 0813/1472] Use get_output function --- tests/test_shells.sh | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/test_shells.sh b/tests/test_shells.sh index 9b6b09e3..9a237ce8 100755 --- a/tests/test_shells.sh +++ b/tests/test_shells.sh @@ -11,6 +11,12 @@ else } fi +get_output() { + OUTPUT="`safe_echo "$INPUT" | LANG=C $@ 2>&1 | sed 's/ \+\x08\+//g' | tail -n +6`" + OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/3rd/pid)/PID/g -e 's/\x1b/\\\\e/g'`" + safe_echo "$OUTPUT" +} + mkdir tests/shell git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH @@ -33,10 +39,9 @@ POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname. exit ' -OUTPUT="`safe_echo "$INPUT" | LANG=C bash -i 2>&1 | sed 's/ \+\x08\+//g' | tail -n +6`" -OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/3rd/pid)/PID/g -e 's/\x1b/\\\\e/g'`" +BASH_OUTPUT="`get_output bash -i`" -EXPECTED_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +EXPECTED_BASH_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mcd .git \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240m3rd \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m.git \e[0;38;5;240;49;22m \e[0mcd .. \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" @@ -50,15 +55,12 @@ EXPECTED_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" \e[0;38;5;220;48;5;166m  zyx-desktop \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mexit exit' - -if [ "b$EXPECTED_OUTPUT" != "b$OUTPUT" ] ; then - safe_echo "$EXPECTED_OUTPUT" > tests/shell/expected - safe_echo "$OUTPUT" > tests/shell/actual +if [ "b$EXPECTED_BASH_OUTPUT" != "b$BASH_OUTPUT" ] ; then + safe_echo "$EXPECTED_BASH_OUTPUT" > tests/shell/expected + safe_echo "$BASH_OUTPUT" > tests/shell/actual diff -u tests/shell/expected tests/shell/actual rm tests/shell/expected tests/shell/actual FAILED=1 fi - rm -r tests/shell - exit $FAILED From bb9034adcf084b099a99c6ea4dae0815cea5c038 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 11 Jan 2014 13:52:07 +0400 Subject: [PATCH 0814/1472] Replace zyx-desktop with HOSTNAME --- tests/test_shells.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_shells.sh b/tests/test_shells.sh index 9a237ce8..771ebca6 100755 --- a/tests/test_shells.sh +++ b/tests/test_shells.sh @@ -13,7 +13,7 @@ fi get_output() { OUTPUT="`safe_echo "$INPUT" | LANG=C $@ 2>&1 | sed 's/ \+\x08\+//g' | tail -n +6`" - OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/3rd/pid)/PID/g -e 's/\x1b/\\\\e/g'`" + OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/3rd/pid)/PID/g -e 's/\x1b/\\\\e/g' | python -c 'import socket, sys; hostname=socket.gethostname(); exec ("""for line in sys.stdin:\n sys.stdout.write(line.replace(hostname, "HOSTNAME"))""")'`" safe_echo "$OUTPUT" } @@ -39,7 +39,7 @@ POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname. exit ' -BASH_OUTPUT="`get_output bash -i`" +BASH_OUTPUT="`get_output bash --noprofile -i`" EXPECTED_BASH_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mcd .git @@ -53,7 +53,7 @@ EXPECTED_BASH_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mfalse \e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" -\e[0;38;5;220;48;5;166m  zyx-desktop \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mexit +\e[0;38;5;220;48;5;166m  HOSTNAME \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mexit exit' if [ "b$EXPECTED_BASH_OUTPUT" != "b$BASH_OUTPUT" ] ; then safe_echo "$EXPECTED_BASH_OUTPUT" > tests/shell/expected From 55957ec3cb4a6a4b15e9fe7dc482d2c38029d440 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Jan 2014 23:45:17 +0400 Subject: [PATCH 0815/1472] Move shell testing to tests/test_shells/test.sh --- tests/test.sh | 2 +- tests/{test_shells.sh => test_shells/test.sh} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{test_shells.sh => test_shells/test.sh} (100%) diff --git a/tests/test.sh b/tests/test.sh index 47b3a00e..8d03f548 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -20,7 +20,7 @@ for script in tests/*.vim ; do FAILED=1 fi done -if ! sh tests/test_shells.sh ; then +if ! sh tests/test_shells/test.sh ; then echo "Failed shells" FAILED=1 fi diff --git a/tests/test_shells.sh b/tests/test_shells/test.sh similarity index 100% rename from tests/test_shells.sh rename to tests/test_shells/test.sh From 6996896b22f336ec2ed3125e9b95163e45cbc55e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 00:53:38 +0400 Subject: [PATCH 0816/1472] Made it use screen for testing --- tests/test_shells/bash.ok | 14 +++++++++ tests/test_shells/input.sh | 14 +++++++++ tests/test_shells/screenrc | 3 ++ tests/test_shells/test.sh | 64 +++++++++++++------------------------- 4 files changed, 52 insertions(+), 43 deletions(-) create mode 100644 tests/test_shells/bash.ok create mode 100644 tests/test_shells/input.sh create mode 100644 tests/test_shells/screenrc diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok new file mode 100644 index 00000000..0576d112 --- /dev/null +++ b/tests/test_shells/bash.ok @@ -0,0 +1,14 @@ + zyx  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" + zyx   BRANCH  ⋯  tests  shell  3rd  cd .git + zyx   BRANCH  ⋯  shell  3rd  .git  cd .. + zyx   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" + zyx  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= + zyx   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +[1] PID + zyx   BRANCH  ⋯  tests  shell  3rd  1  false + zyx   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" + zyx   BRANCH  ⋯  tests  shell  3rd  false + zyx   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +  HOSTNAME  zyx   BRANCH  ⋯  tests  shell  3rd  exit +exit diff --git a/tests/test_shells/input.sh b/tests/test_shells/input.sh new file mode 100644 index 00000000..44e4de9a --- /dev/null +++ b/tests/test_shells/input.sh @@ -0,0 +1,14 @@ +POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +VIRTUAL_ENV= +source powerline/bindings/bash/powerline.sh ; cd tests/shell/3rd +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +cd .git +cd .. +VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +VIRTUAL_ENV= +bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +false +kill `cat pid` ; sleep 1s +false +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +exit diff --git a/tests/test_shells/screenrc b/tests/test_shells/screenrc new file mode 100644 index 00000000..ad5a1466 --- /dev/null +++ b/tests/test_shells/screenrc @@ -0,0 +1,3 @@ +width 1024 +height 1 +logfile "tests/shell/screen.log" diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 771ebca6..b08f6de7 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -11,55 +11,33 @@ else } fi -get_output() { - OUTPUT="`safe_echo "$INPUT" | LANG=C $@ 2>&1 | sed 's/ \+\x08\+//g' | tail -n +6`" - OUTPUT="`safe_echo "$OUTPUT" | sed -e s/$(cat tests/shell/3rd/pid)/PID/g -e 's/\x1b/\\\\e/g' | python -c 'import socket, sys; hostname=socket.gethostname(); exec ("""for line in sys.stdin:\n sys.stdout.write(line.replace(hostname, "HOSTNAME"))""")'`" - safe_echo "$OUTPUT" +run_test() { + SESNAME="powerline-shell-test-$$" + screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" env LANG=C "$@" + screen -S "$SESNAME" -X readreg a tests/test_shells/input.sh + sleep 5s + screen -S "$SESNAME" -p 0 -X width 300 1 + screen -S "$SESNAME" -p 0 -X logfile tests/shell/screen.log + screen -S "$SESNAME" -p 0 -X paste a + while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do + sleep 1s + done + sed -i -e "1,3 d" \ + -e s/$(cat tests/shell/3rd/pid)/PID/g \ + -e "s/$(python -c 'import re, socket; print (re.escape(socket.gethostname()))')/HOSTNAME/g" \ + tests/shell/screen.log + if ! diff -u tests/test_shells/${1}.ok tests/shell/screen.log ; then + return 1 + fi + return 0 } mkdir tests/shell git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH -INPUT=' -POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" -VIRTUAL_ENV= -COLUMNS=80 -source powerline/bindings/bash/powerline.sh ; cd tests/shell/3rd -POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" -cd .git -cd .. -VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -VIRTUAL_ENV= -bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & -false -kill `cat pid` ; sleep 1s -false -POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" -exit -' - -BASH_OUTPUT="`get_output bash --noprofile -i`" - -EXPECTED_BASH_OUTPUT='\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mcd .git -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240m3rd \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m.git \e[0;38;5;240;49;22m \e[0mcd .. -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;74;22m \e[0;38;5;231;48;5;74mⓔ  some-virtual-environment \e[0;38;5;74;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mVIRTUAL_ENV= -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mbash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & -[1] PID -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;49;22m \e[0mfalse -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;166;22m \e[0;38;5;220;48;5;166m1 \e[0;38;5;166;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mkill `cat pid` ; sleep 1s -[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mfalse -\e[0;38;5;231;48;5;31;1m zyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;48;5;52;22m \e[0;38;5;231;48;5;52m1 \e[0;38;5;52;49;22m \e[0mPOWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" -\e[0;38;5;220;48;5;166m  HOSTNAME \e[0;38;5;166;48;5;31;22m \e[0;38;5;231;48;5;31;1mzyx \e[0;38;5;31;48;5;236;22m \e[0;38;5;250;48;5;236m BRANCH \e[0;38;5;236;48;5;240;22m \e[0;38;5;250;48;5;240m⋯ \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mtests \e[0;38;5;245;48;5;240;22m \e[0;38;5;250;48;5;240mshell \e[0;38;5;245;48;5;240;22m \e[0;38;5;252;48;5;240;1m3rd \e[0;38;5;240;49;22m \e[0mexit -exit' -if [ "b$EXPECTED_BASH_OUTPUT" != "b$BASH_OUTPUT" ] ; then - safe_echo "$EXPECTED_BASH_OUTPUT" > tests/shell/expected - safe_echo "$BASH_OUTPUT" > tests/shell/actual - diff -u tests/shell/expected tests/shell/actual - rm tests/shell/expected tests/shell/actual +if ! run_test bash --norc --noprofile -i ; then + echo "Failed bash" FAILED=1 fi rm -r tests/shell From 6c275062703d160991a15328fac318f318d5b465 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 01:07:13 +0400 Subject: [PATCH 0817/1472] Move tests/test_shells/input.sh to tests/test_shells/input.bash --- tests/test_shells/{input.sh => input.bash} | 0 tests/test_shells/test.sh | 10 +++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) rename tests/test_shells/{input.sh => input.bash} (100%) diff --git a/tests/test_shells/input.sh b/tests/test_shells/input.bash similarity index 100% rename from tests/test_shells/input.sh rename to tests/test_shells/input.bash diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index b08f6de7..52a87a86 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -12,9 +12,11 @@ else fi run_test() { + SH="$1" SESNAME="powerline-shell-test-$$" - screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" env LANG=C "$@" - screen -S "$SESNAME" -X readreg a tests/test_shells/input.sh + screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ + env LANG=C BINDFILE="$BINDFILE" "$@" + screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH sleep 5s screen -S "$SESNAME" -p 0 -X width 300 1 screen -S "$SESNAME" -p 0 -X logfile tests/shell/screen.log @@ -26,7 +28,7 @@ run_test() { -e s/$(cat tests/shell/3rd/pid)/PID/g \ -e "s/$(python -c 'import re, socket; print (re.escape(socket.gethostname()))')/HOSTNAME/g" \ tests/shell/screen.log - if ! diff -u tests/test_shells/${1}.ok tests/shell/screen.log ; then + if ! diff -u tests/test_shells/${SH}.ok tests/shell/screen.log ; then return 1 fi return 0 @@ -40,5 +42,7 @@ if ! run_test bash --norc --noprofile -i ; then echo "Failed bash" FAILED=1 fi +rm tests/shell/screen.log + rm -r tests/shell exit $FAILED From d9c62d4796c73d8f7bd88c58c24f93b01e6adfd3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 19:47:58 +0400 Subject: [PATCH 0818/1472] Add zsh functional tests --- tests/install.sh | 1 + tests/test_shells/input.zsh | 14 ++++++++++++++ tests/test_shells/test.sh | 8 +++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/test_shells/input.zsh diff --git a/tests/install.sh b/tests/install.sh index 138da22a..4172395b 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,4 +9,5 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi +apt-get install zsh true diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh new file mode 100644 index 00000000..f3506642 --- /dev/null +++ b/tests/test_shells/input.zsh @@ -0,0 +1,14 @@ +unsetopt promptsp transientrprompt +POWERLINE_COMMAND=( $PWD/scripts/powerline -p $PWD/powerline/config_files ) ; VIRTUAL_ENV= +source powerline/bindings/zsh/powerline.zsh ; cd tests/shell/3rd +POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) +cd .git +cd .. +VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +VIRTUAL_ENV= +bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +false +kill `cat pid` ; sleep 1s +false +POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) +exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 52a87a86..75ef1752 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -15,7 +15,7 @@ run_test() { SH="$1" SESNAME="powerline-shell-test-$$" screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ - env LANG=C BINDFILE="$BINDFILE" "$@" + env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH sleep 5s screen -S "$SESNAME" -p 0 -X width 300 1 @@ -44,5 +44,11 @@ if ! run_test bash --norc --noprofile -i ; then fi rm tests/shell/screen.log +if ! run_test zsh -f -i ; then + echo "Failed zsh" + FAILED=1 +fi +rm tests/shell/screen.log + rm -r tests/shell exit $FAILED From b55c5da33683e582eb2a49fee1f08884d3aed168 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 19:48:12 +0400 Subject: [PATCH 0819/1472] Also install screen --- tests/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/install.sh b/tests/install.sh index 4172395b..0a084d2a 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,5 +9,5 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -apt-get install zsh +apt-get install zsh screen true From d1793fac61ea2ded98b76a10da563f1a53950b06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 21:48:09 +0400 Subject: [PATCH 0820/1472] Run apt-get with sudo and -qq like suggested in travis documentation Documentation though suggests using before_install. Not sure why. --- tests/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/install.sh b/tests/install.sh index 0a084d2a..24ad4a22 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,5 +9,5 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -apt-get install zsh screen +sudo apt-get install -qq zsh screen true From b16943292b5f7e1af838eb990722194c3998b605 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 22:04:44 +0400 Subject: [PATCH 0821/1472] Add missing zsh.ok --- tests/test_shells/zsh.ok | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/test_shells/zsh.ok diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok new file mode 100644 index 00000000..bc3c9e75 --- /dev/null +++ b/tests/test_shells/zsh.ok @@ -0,0 +1,13 @@ +  zyx  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) +  zyx   BRANCH  ⋯  tests  shell  3rd  cd .git +  zyx   BRANCH  ⋯  shell  3rd  .git  cd .. +  zyx   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  zyx  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  zyx   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +[1] PID +  zyx   BRANCH  ⋯  tests  shell  3rd  false +  zyx   BRANCH  ⋯  tests  shell  3rd  1  kill `cat pid` ; sleep 1s +[1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +  zyx   BRANCH  ⋯  tests  shell  3rd  false +  zyx   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) +   HOSTNAME  zyx   BRANCH  ⋯  tests  shell  3rd  exit From 03245f192b93b714e162fe8328235ac64bc01d00 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 22:11:52 +0400 Subject: [PATCH 0822/1472] Also replace $USER with USER --- tests/test_shells/bash.ok | 22 +++++++++++----------- tests/test_shells/test.sh | 1 + tests/test_shells/zsh.ok | 22 +++++++++++----------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index 0576d112..b644e615 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -1,14 +1,14 @@ - zyx  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" - zyx   BRANCH  ⋯  tests  shell  3rd  cd .git - zyx   BRANCH  ⋯  shell  3rd  .git  cd .. - zyx   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" - zyx  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= - zyx   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & + USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" + USER   BRANCH  ⋯  tests  shell  3rd  cd .git + USER   BRANCH  ⋯  shell  3rd  .git  cd .. + USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" + USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= + USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & [1] PID - zyx   BRANCH  ⋯  tests  shell  3rd  1  false - zyx   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s + USER   BRANCH  ⋯  tests  shell  3rd  1  false + USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" - zyx   BRANCH  ⋯  tests  shell  3rd  false - zyx   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" -  HOSTNAME  zyx   BRANCH  ⋯  tests  shell  3rd  exit + USER   BRANCH  ⋯  tests  shell  3rd  false + USER   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  exit exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 75ef1752..ca83d864 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -27,6 +27,7 @@ run_test() { sed -i -e "1,3 d" \ -e s/$(cat tests/shell/3rd/pid)/PID/g \ -e "s/$(python -c 'import re, socket; print (re.escape(socket.gethostname()))')/HOSTNAME/g" \ + -e "s/$(python -c 'import os, re; print (re.escape(os.environ["USER"]))')/USER/g" \ tests/shell/screen.log if ! diff -u tests/test_shells/${SH}.ok tests/shell/screen.log ; then return 1 diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index bc3c9e75..a986ed4d 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -1,13 +1,13 @@ -  zyx  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) -  zyx   BRANCH  ⋯  tests  shell  3rd  cd .git -  zyx   BRANCH  ⋯  shell  3rd  .git  cd .. -  zyx   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  zyx  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  zyx   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) +  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & [1] PID -  zyx   BRANCH  ⋯  tests  shell  3rd  false -  zyx   BRANCH  ⋯  tests  shell  3rd  1  kill `cat pid` ; sleep 1s +  USER   BRANCH  ⋯  tests  shell  3rd  false +  USER   BRANCH  ⋯  tests  shell  3rd  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  zyx   BRANCH  ⋯  tests  shell  3rd  false -  zyx   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) -   HOSTNAME  zyx   BRANCH  ⋯  tests  shell  3rd  exit +  USER   BRANCH  ⋯  tests  shell  3rd  false +  USER   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  exit From 0f0a5e92d35b609fa5630ffd4eb51f67bc01053d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Jan 2014 22:37:33 +0400 Subject: [PATCH 0823/1472] Replace sed with python script sed escaping is wrong: e.g. re.escape() will escape `+`, but with BRE escaped plus is quantifier --- tests/test_shells/postproc.py | 32 ++++++++++++++++++++++++++++++++ tests/test_shells/test.sh | 8 ++------ tests/test_shells/zsh.ok | 1 + 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100755 tests/test_shells/postproc.py diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py new file mode 100755 index 00000000..8cd037d0 --- /dev/null +++ b/tests/test_shells/postproc.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +from __future__ import unicode_literals + +import os +import socket +import sys +import codecs + + +fname = sys.argv[1] +new_fname = fname + '.new' +pid_fname = 'tests/shell/3rd/pid' + +with open(pid_fname, 'r') as P: + pid = P.read().strip() +hostname = socket.gethostname() +user = os.environ['USER'] + +with codecs.open(fname, 'r', encoding='utf-8') as R: + with codecs.open(new_fname, 'w', encoding='utf-8') as W: + found_cd = False + for line in R: + if not found_cd: + found_cd = ('cd tests/shell/3rd' in line) + continue + line = line.replace(pid, 'PID') + line = line.replace(hostname, 'HOSTNAME') + line = line.replace(user, 'USER') + W.write(line) + +os.rename(new_fname, fname) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index ca83d864..1459da5e 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -24,12 +24,8 @@ run_test() { while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do sleep 1s done - sed -i -e "1,3 d" \ - -e s/$(cat tests/shell/3rd/pid)/PID/g \ - -e "s/$(python -c 'import re, socket; print (re.escape(socket.gethostname()))')/HOSTNAME/g" \ - -e "s/$(python -c 'import os, re; print (re.escape(os.environ["USER"]))')/USER/g" \ - tests/shell/screen.log - if ! diff -u tests/test_shells/${SH}.ok tests/shell/screen.log ; then + ./tests/test_shells/postproc.py tests/shell/screen.log + if ! diff -u tests/test_shells/${SH}.ok tests/shell/screen.log | cat -v ; then return 1 fi return 0 diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index a986ed4d..74d885cf 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -1,3 +1,4 @@ +  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly )  USER   BRANCH  ⋯  tests  shell  3rd  cd .git  USER   BRANCH  ⋯  shell  3rd  .git  cd .. From 2e713269aee0cf5e3f8d3f2ba2753235bd50ff09 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 14 Jan 2014 00:18:34 +0400 Subject: [PATCH 0824/1472] Always show hostname (travis is ssh), fix jobnum in zsh --- powerline/bindings/zsh/powerline.zsh | 33 +++++++++++++++------------- tests/test_shells/bash.ok | 20 ++++++++--------- tests/test_shells/input.bash | 4 ++-- tests/test_shells/input.zsh | 7 +++--- tests/test_shells/test.sh | 4 +++- tests/test_shells/zsh.ok | 20 ++++++++--------- 6 files changed, 45 insertions(+), 43 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 494160e0..917f5a8b 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -24,13 +24,28 @@ _powerline_tmux_set_columns() { _powerline_tmux_setenv COLUMNS "$COLUMNS" } -_powerline_install_precmd() { +_powerline_precmd() { + # If you are wondering why I am not using the same code as I use for bash + # ($(jobs|wc -l)): consider the following test: + # echo abc | less + # + # . This way jobs will print + # [1] + done echo abc | + # suspended less -M + # ([ is in first column). You see: any line counting thingie will return + # wrong number of jobs. You need to filter the lines first. Or not use + # jobs built-in at all. + _POWERLINE_JOBNUM=${(%):-%j} +} + +_powerline_setup_prompt() { emulate -L zsh for f in "${precmd_functions[@]}"; do if [[ "$f" = "_powerline_precmd" ]]; then return fi done + precmd_functions+=( _powerline_precmd ) chpwd_functions+=( _powerline_tmux_set_pwd ) if zmodload zsh/zpython &>/dev/null ; then zpython 'from powerline.bindings.zsh import setup as powerline_setup' @@ -38,19 +53,7 @@ _powerline_install_precmd() { zpython 'del powerline_setup' else local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' - # If you are wondering why I am not using the same code as I use for - # bash ($(jobs|wc -l)): consider the following test: - # echo abc | less - # - # . This way jobs will print - # [1] + done echo abc | - # suspended less -M - # ([ is in first column). You see: any line counting thingie will return - # wrong number of jobs. You need to filter the lines first. Or not use - # jobs built-in at all. - # - # This and above variants also do not use subshell. - add_args+=' --jobnum=${(%):-%j}' + add_args+=' --jobnum=$_POWERLINE_JOBNUM' PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args')' RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' fi @@ -62,4 +65,4 @@ _powerline_tmux_set_pwd setopt promptpercent setopt promptsubst -_powerline_install_precmd +_powerline_setup_prompt diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index b644e615..123b9fd6 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -1,14 +1,12 @@ - USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" - USER   BRANCH  ⋯  tests  shell  3rd  cd .git - USER   BRANCH  ⋯  shell  3rd  .git  cd .. - USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" - USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= - USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & [1] PID - USER   BRANCH  ⋯  tests  shell  3rd  1  false - USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" - USER   BRANCH  ⋯  tests  shell  3rd  false - USER   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  exit +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit exit diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 44e4de9a..9b97dac0 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -1,7 +1,8 @@ POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" VIRTUAL_ENV= source powerline/bindings/bash/powerline.sh ; cd tests/shell/3rd -POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" @@ -10,5 +11,4 @@ bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s false -POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" exit diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index f3506642..a19bdff4 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -1,7 +1,9 @@ unsetopt promptsp transientrprompt -POWERLINE_COMMAND=( $PWD/scripts/powerline -p $PWD/powerline/config_files ) ; VIRTUAL_ENV= -source powerline/bindings/zsh/powerline.zsh ; cd tests/shell/3rd +POWERLINE_COMMAND=( $PWD/scripts/powerline -p $PWD/powerline/config_files ) +POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) +VIRTUAL_ENV= +source powerline/bindings/zsh/powerline.zsh ; cd tests/shell/3rd cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" @@ -10,5 +12,4 @@ bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s false -POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 1459da5e..e5febb8f 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -17,7 +17,7 @@ run_test() { screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH - sleep 5s + sleep 0.3s screen -S "$SESNAME" -p 0 -X width 300 1 screen -S "$SESNAME" -p 0 -X logfile tests/shell/screen.log screen -S "$SESNAME" -p 0 -X paste a @@ -39,12 +39,14 @@ if ! run_test bash --norc --noprofile -i ; then echo "Failed bash" FAILED=1 fi +cp tests/shell/screen.log tests/bash.log rm tests/shell/screen.log if ! run_test zsh -f -i ; then echo "Failed zsh" FAILED=1 fi +cp tests/shell/screen.log tests/zsh.log rm tests/shell/screen.log rm -r tests/shell diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index 74d885cf..2f7256b4 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -1,14 +1,12 @@ -  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) -  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & [1] PID -  USER   BRANCH  ⋯  tests  shell  3rd  false -  USER   BRANCH  ⋯  tests  shell  3rd  1  kill `cat pid` ; sleep 1s +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  USER   BRANCH  ⋯  tests  shell  3rd  false -  USER   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  exit +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit From c33d56e73cfd447bd261b60da185b4750a68ed28 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 10 Jan 2014 13:00:25 -0800 Subject: [PATCH 0825/1472] Add arg to optionally suppress user segment This adds the option to common.user to suppress display if the username matches the given string in the new "hide_user" argument. --- powerline/segments/common.py | 7 ++++++- tests/test_segments.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 4a858d5b..5eda22ca 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -591,9 +591,12 @@ username = False _geteuid = getattr(os, 'geteuid', lambda: 1) -def user(pl, segment_info=None): +def user(pl, segment_info=None, hide_user=None): '''Return the current user. + :param str hide_user: + will suppress display of username if it matches the given string + Highlights the user with the ``superuser`` if the effective user ID is 0. Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups. @@ -604,6 +607,8 @@ def user(pl, segment_info=None): if username is None: pl.warn('Failed to get username') return None + if username == hide_user: + return None euid = _geteuid() return [{ 'contents': username, diff --git a/tests/test_segments.py b/tests/test_segments.py index fea6e55d..03767749 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -80,6 +80,10 @@ class TestCommon(TestCase): self.assertEqual(common.user(pl=pl, segment_info=segment_info), [ {'contents': 'def', 'highlight_group': 'user'} ]) + self.assertEqual(common.user(pl=pl, segment_info=segment_info, hide_user='abc'), [ + {'contents': 'def', 'highlight_group': 'user'} + ]) + self.assertEqual(common.user(pl=pl, segment_info=segment_info, hide_user='def'), None) with replace_attr(common, '_geteuid', lambda: 0): self.assertEqual(common.user(pl=pl, segment_info=segment_info), [ {'contents': 'def', 'highlight_group': ['superuser', 'user']} From aa3e86b9d9351359cf9c94a1d94f074695b8b6aa Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 14 Jan 2014 23:38:34 +0400 Subject: [PATCH 0826/1472] Rewrite documentation string --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5eda22ca..f6572ce7 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -595,7 +595,7 @@ def user(pl, segment_info=None, hide_user=None): '''Return the current user. :param str hide_user: - will suppress display of username if it matches the given string + Omit showing segment for users with names equal to this string. Highlights the user with the ``superuser`` if the effective user ID is 0. From 7aa978fdba78bb28c5f51f649f3b1834f994b027 Mon Sep 17 00:00:00 2001 From: EinfachToll Date: Wed, 15 Jan 2014 10:32:04 +0100 Subject: [PATCH 0827/1472] New fixed segment showing position of current view Solves #539 Fixed version of #540 and #579 Works now for split windows and wrapped lines. --- .../colorschemes/vim/default.json | 2 + .../colorschemes/vim/solarized.json | 4 ++ .../colorschemes/vim/solarizedlight.json | 4 ++ powerline/segments/vim.py | 38 +++++++++++++++++++ tests/test_segments.py | 13 +++++++ tests/vim.py | 4 ++ 6 files changed, 65 insertions(+) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 254383a1..984ac606 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -25,6 +25,8 @@ "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, "line_percent": { "fg": "gray9", "bg": "gray4" }, "line_percent_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, + "position": { "fg": "gray9", "bg": "gray4" }, + "position_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10" }, diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 869ebf1d..3adeb259 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -25,6 +25,8 @@ "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper" }, "line_percent": { "fg": "oldlace", "bg": "lightskyblue4" }, "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, + "position": { "fg": "oldlace", "bg": "lightskyblue4" }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "gray10" }, @@ -68,6 +70,8 @@ "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, "line_percent": { "fg": "oldlace", "bg": "gray61" }, "line_percent_gradient": { "fg": "oldlace", "bg": "gray61" }, + "position": { "fg": "oldlace", "bg": "gray61" }, + "position_gradient": { "fg": "oldlace", "bg": "gray61" }, "line_current": { "fg": "gray13", "bg": "oldlace", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray13", "bg": "oldlace" }, "col_current": { "fg": "azure4", "bg": "oldlace" } diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index f91f5175..70d8c4c5 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -25,6 +25,8 @@ "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, "line_percent": { "fg": "gray13", "bg": "lightyellow" }, "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow" }, + "position": { "fg": "gray13", "bg": "lightyellow" }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow" }, "line_current": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] }, "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5" }, "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5" }, @@ -68,6 +70,8 @@ "file_vcs_status_A": { "fg": "green", "bg": "royalblue5" }, "line_percent": { "fg": "gray13", "bg": "gray61" }, "line_percent_gradient": { "fg": "gray13", "bg": "gray61" }, + "position": { "fg": "gray13", "bg": "gray61" }, + "position_gradient": { "fg": "gray13", "bg": "gray61" }, "line_current": { "fg": "oldlace", "bg": "gray13", "attr": ["bold"] }, "line_current_symbol": { "fg": "oldlace", "bg": "gray13" }, "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "gray13" }, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index b41fb5e5..b3cd41ed 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -265,6 +265,44 @@ def line_percent(pl, segment_info, gradient=False): }] +@window_cached +def position(pl, position_strings=('Top', 'Bot', 'All'), gradient=False): + '''Return the position of the current view in the file as a percentage. + + :param tuple position_strings: + use these three strings to indicate whether we are at top or bottom or see the complete file + + :param bool gradient: + highlight the percentage with a color gradient (by default a green to red gradient) + + Highlight groups used: ``position_gradient`` (gradient), ``position``. + ''' + line_last = len(vim.current.buffer) + + winline_first = int(vim.eval('line("w0")')) + winline_last = int(vim.eval('line("w$")')) + if winline_first == 1 and winline_last == line_last: + percentage = 0.0 + content = position_strings[2] + elif winline_first == 1: + percentage = 0.0 + content = position_strings[0] + elif winline_last == line_last: + percentage = 100.0 + content = position_strings[1] + else: + percentage = winline_first * 100.0 / (line_last - winline_last + winline_first) + content = str(int(round(percentage))) + '%' + + if not gradient: + return content + return [{ + 'contents': content, + 'highlight_group': ['position_gradient', 'position'], + 'gradient_level': percentage, + }] + + @requires_segment_info def line_current(pl, segment_info): '''Return the current cursor line.''' diff --git a/tests/test_segments.py b/tests/test_segments.py index 03767749..c3014ac7 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -501,6 +501,19 @@ class TestVim(TestCase): finally: vim_module._bw(segment_info['bufnr']) + def test_position(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + try: + segment_info['buffer'][:] = ['1'] + vim_module._set_cursor(1, 0) + self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'All') + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings=('Comienzo', 'Final', 'Todo')), 'Todo') + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), + [{'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0}]) + finally: + vim_module._bw(segment_info['bufnr']) + def test_cursor_current(self): pl = Pl() segment_info = vim_module._get_segment_info() diff --git a/tests/vim.py b/tests/vim.py index bcc549b6..da12c812 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -169,6 +169,10 @@ def eval(expr): return vars[expr[2:]] elif expr.startswith('&'): return options[expr[1:]] + elif expr == 'line("w0")': + return 1 + elif expr == 'line("w$")': + return 1 elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) return '0' From 40fe3cf9388a2c826bf4c0418f96fd03d40c2858 Mon Sep 17 00:00:00 2001 From: Ryan Collins Date: Fri, 17 Jan 2014 00:15:37 -0500 Subject: [PATCH 0828/1472] A lowercase f for the unit caused the weather segment to silently fail. Putting in a capital F works correctly --- docs/source/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 4be7bea5..4ea0e6b3 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -91,7 +91,7 @@ segments that you may want to customize right away: "name": "weather", "priority": 50, "args": { - "unit": "f", + "unit": "F", "location_query": "oslo, norway" } }, From a71c49d96b5ee92b1ec23bf0e07d54d21890c064 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Nov 2013 01:27:20 +0400 Subject: [PATCH 0829/1472] Add tests --- tests/lib/__init__.py | 30 ++++++----- tests/test_cmdline.py | 120 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 tests/test_cmdline.py diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index a28c4588..c6ac15d3 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -76,23 +76,29 @@ def new_module(name, **kwargs): class AttrReplace(object): - def __init__(self, obj, attr, new): + def __init__(self, obj, *args): self.obj = obj - self.attr = attr - self.new = new + self.attrs = args[::2] + self.new = args[1::2] def __enter__(self): - try: - self.old = getattr(self.obj, self.attr) - except AttributeError: - pass - setattr(self.obj, self.attr, self.new) + self.old = {} + for i, attr in enumerate(self.attrs): + try: + self.old[i] = getattr(self.obj, attr) + except AttributeError: + pass + for attr, new in zip(self.attrs, self.new): + setattr(self.obj, attr, new) def __exit__(self, *args): - try: - setattr(self.obj, self.attr, self.old) - except AttributeError: - delattr(self.obj, self.attr) + for i, attr in enumerate(self.attrs): + try: + old = self.old[i] + except KeyError: + delattr(self.obj, attr) + else: + setattr(self.obj, attr, old) replace_attr = AttrReplace diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py new file mode 100644 index 00000000..c4985f3b --- /dev/null +++ b/tests/test_cmdline.py @@ -0,0 +1,120 @@ +# vim:fileencoding=utf-8:noet + +'''Tests for shell.py parser''' + + +from powerline.shell import get_argparser, finish_args +from tests import TestCase +from tests.lib import replace_attr +import sys +if sys.version_info < (3,): + from io import BytesIO as StrIO +else: + from io import StringIO as StrIO # NOQA + + +class TestParser(TestCase): + def test_main_err(self): + parser = get_argparser() + out = StrIO() + err = StrIO() + def flush(): + out.truncate(0) + err.truncate(0) + with replace_attr(sys, 'stdout', out, 'stderr', err): + for raising_args, raising_reg in [ + ([], 'too few arguments|the following arguments are required: ext'), + (['-r'], 'expected one argument'), + (['shell', '-r'], 'expected one argument'), + (['shell', '-w'], 'expected one argument'), + (['shell', '-c'], 'expected one argument'), + (['shell', '-t'], 'expected one argument'), + (['shell', '-p'], 'expected one argument'), + (['shell', '-R'], 'expected one argument'), + (['shell', '--renderer_module'], 'expected one argument'), + (['shell', '--width'], 'expected one argument'), + (['shell', '--last_exit_code'], 'expected one argument'), + (['shell', '--last_pipe_status'], 'expected one argument'), + (['shell', '--config'], 'expected one argument'), + (['shell', '--theme_option'], 'expected one argument'), + (['shell', '--config_path'], 'expected one argument'), + (['shell', '--renderer_arg'], 'expected one argument'), + (['-r', 'zsh_prompt'], 'too few arguments|the following arguments are required: ext'), + (['shell', '--last_exit_code', 'i'], 'invalid int value'), + (['shell', '--last_pipe_status', '1 i'], 'invalid value'), + (['shell', '-R', 'abc'], 'invalid value'), + ]: + self.assertRaises(SystemExit, parser.parse_args, raising_args) + self.assertFalse(out.getvalue()) + self.assertRegexpMatches(err.getvalue(), raising_reg) + flush() + + def test_main_normal(self): + parser = get_argparser() + out = StrIO() + err = StrIO() + with replace_attr(sys, 'stdout', out, 'stderr', err): + for argv, expargs in [ + (['shell'], {'ext': ['shell']}), + (['shell', '-r', 'zsh_prompt'], {'ext': ['shell'], 'renderer_module': 'zsh_prompt'}), + ([ + 'shell', + 'left', + '-r', 'zsh_prompt', + '--last_exit_code', '10', + '--last_pipe_status', '10 20 30', + '-w', '100', + '-c', 'common.term_truecolor=true', + '-c', 'common.spaces=4', + '-t', 'default.segment_data.hostname.before=H:', + '-p', '.', + '-R', 'smth={"abc":"def"}' + ], { + 'ext': ['shell'], + 'side': 'left', + 'renderer_module': 'zsh_prompt', + 'last_exit_code': 10, + 'last_pipe_status': [10, 20, 30], + 'width': 100, + 'config': {'common': {'term_truecolor': True, 'spaces': 4}}, + 'theme_option': { + 'default': { + 'segment_data': { + 'hostname': { + 'before': 'H:' + } + } + } + }, + 'config_path': '.', + 'renderer_arg': {'smth': {'abc': 'def'}}, + }), + (['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}), + (['shell', '-t', 'default.segment_info={"hostname": {}}'], { + 'ext': ['shell'], + 'theme_option': { + 'default': { + 'segment_info': { + 'hostname': {} + } + } + }, + }), + (['shell', '-c', 'common={ }'], {'ext': ['shell'], 'config': {'common': {}}}), + ]: + args = parser.parse_args(argv) + finish_args(args) + for key, val in expargs.items(): + self.assertEqual(getattr(args, key), val) + for key, val in args.__dict__.items(): + if key not in expargs: + self.assertFalse(val, msg='key {0} is {1} while it should be something false'.format(key, val)) + self.assertFalse(err.getvalue() + out.getvalue(), msg='unexpected output: {0!r} {1!r}'.format( + err.getvalue(), + out.getvalue(), + )) + + +if __name__ == '__main__': + from tests import main + main() From ac9703e04cd65d27350202d8de366978259b9587 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 17 Jan 2014 20:54:47 +0400 Subject: [PATCH 0830/1472] Also check for jobnum --- tests/test_cmdline.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index c4985f3b..05db6ab2 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -39,6 +39,7 @@ class TestParser(TestCase): (['shell', '--theme_option'], 'expected one argument'), (['shell', '--config_path'], 'expected one argument'), (['shell', '--renderer_arg'], 'expected one argument'), + (['shell', '--jobnum'], 'expected one argument'), (['-r', 'zsh_prompt'], 'too few arguments|the following arguments are required: ext'), (['shell', '--last_exit_code', 'i'], 'invalid int value'), (['shell', '--last_pipe_status', '1 i'], 'invalid value'), @@ -63,6 +64,7 @@ class TestParser(TestCase): '-r', 'zsh_prompt', '--last_exit_code', '10', '--last_pipe_status', '10 20 30', + '--jobnum=10', '-w', '100', '-c', 'common.term_truecolor=true', '-c', 'common.spaces=4', @@ -75,6 +77,7 @@ class TestParser(TestCase): 'renderer_module': 'zsh_prompt', 'last_exit_code': 10, 'last_pipe_status': [10, 20, 30], + 'jobnum': 10, 'width': 100, 'config': {'common': {'term_truecolor': True, 'spaces': 4}}, 'theme_option': { From 56ef798218604aea11f57a9c16a056bb99d8b706 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 17 Jan 2014 21:34:18 +0400 Subject: [PATCH 0831/1472] On travis there are differences in CRs for some reason MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit strips CRs from *.ok files and test output. Test failure used to be not recognized due to pipe: `cat -v` always succeeds meaning that `if ! diff … | cat -v` always succeeds too. --- tests/test_shells/bash.ok | 24 ++++++++++++------------ tests/test_shells/postproc.py | 3 +++ tests/test_shells/test.sh | 15 +++++++++++++-- tests/test_shells/zsh.ok | 24 ++++++++++++------------ 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index 123b9fd6..00824923 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -1,12 +1,12 @@ -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & -[1] PID -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit -exit +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit +exit diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 8cd037d0..5ee721c9 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -24,6 +24,9 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: if not found_cd: found_cd = ('cd tests/shell/3rd' in line) continue + line = line.translate({ + ord('\r'): None + }) line = line.replace(pid, 'PID') line = line.replace(hostname, 'HOSTNAME') line = line.replace(user, 'USER') diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index e5febb8f..f47ab5a5 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -11,21 +11,32 @@ else } fi +check_screen_log() { + diff -u tests/test_shells/${1}.ok tests/shell/screen.log + # Explicit is better then implicit + return $? +} + run_test() { SH="$1" SESNAME="powerline-shell-test-$$" screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH + # Wait for screen to initialize sleep 0.3s screen -S "$SESNAME" -p 0 -X width 300 1 screen -S "$SESNAME" -p 0 -X logfile tests/shell/screen.log screen -S "$SESNAME" -p 0 -X paste a + # Wait for screen to exit (sending command to non-existing screen session + # fails; when launched instance exits corresponding session is deleted) while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do - sleep 1s + sleep 0.1s done ./tests/test_shells/postproc.py tests/shell/screen.log - if ! diff -u tests/test_shells/${SH}.ok tests/shell/screen.log | cat -v ; then + if ! check_screen_log ${SH} ; then + # Repeat the diff to make it better viewable in travis output + check_screen_log ${SH} | cat -v return 1 fi return 0 diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index 2f7256b4..9a7b0502 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -1,12 +1,12 @@ - -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & -[1] PID -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false -   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit + +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit From 2b171cff67ecf93f150ef1299282c515dfea8ec6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 18 Jan 2014 01:59:19 +0400 Subject: [PATCH 0832/1472] Try to workaround problems with last line in travis --- tests/test_shells/bash.ok | 2 -- tests/test_shells/input.bash | 1 + tests/test_shells/input.zsh | 1 + tests/test_shells/postproc.py | 2 ++ tests/test_shells/zsh.ok | 1 - 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index 00824923..a88c8091 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -8,5 +8,3 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done"   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit -exit diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 9b97dac0..954d6e1a 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -11,4 +11,5 @@ bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s false +true is the last line exit diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index a19bdff4..bcd1a421 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -12,4 +12,5 @@ bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s false +true is the last line exit diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 5ee721c9..ae2e887a 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -24,6 +24,8 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: if not found_cd: found_cd = ('cd tests/shell/3rd' in line) continue + if 'true is the last line' in line: + break line = line.translate({ ord('\r'): None }) diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index 9a7b0502..47eda0f2 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -9,4 +9,3 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done"   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  exit From 9de9bc790797b8ce882362b56b2c59685b7e52fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 19 Jan 2014 11:20:15 +0100 Subject: [PATCH 0833/1472] Remove archlinux packages --- .../archlinux/python-powerline-git/.gitignore | 5 -- .../archlinux/python-powerline-git/PKGBUILD | 74 ------------------ .../python-powerline-git/fontpatcher.py.patch | 13 ---- .../python-powerline-git/powerline.install | 48 ------------ .../python2-powerline-git/.gitignore | 5 -- .../archlinux/python2-powerline-git/PKGBUILD | 75 ------------------- .../fontpatcher.py.patch | 13 ---- .../python2-powerline-git/powerline.install | 43 ----------- 8 files changed, 276 deletions(-) delete mode 100644 packages/archlinux/python-powerline-git/.gitignore delete mode 100644 packages/archlinux/python-powerline-git/PKGBUILD delete mode 100644 packages/archlinux/python-powerline-git/fontpatcher.py.patch delete mode 100644 packages/archlinux/python-powerline-git/powerline.install delete mode 100644 packages/archlinux/python2-powerline-git/.gitignore delete mode 100644 packages/archlinux/python2-powerline-git/PKGBUILD delete mode 100644 packages/archlinux/python2-powerline-git/fontpatcher.py.patch delete mode 100644 packages/archlinux/python2-powerline-git/powerline.install diff --git a/packages/archlinux/python-powerline-git/.gitignore b/packages/archlinux/python-powerline-git/.gitignore deleted file mode 100644 index cc7d5336..00000000 --- a/packages/archlinux/python-powerline-git/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -* -!.gitignore -!PKGBUILD -!*.install -!*.patch diff --git a/packages/archlinux/python-powerline-git/PKGBUILD b/packages/archlinux/python-powerline-git/PKGBUILD deleted file mode 100644 index f818b77b..00000000 --- a/packages/archlinux/python-powerline-git/PKGBUILD +++ /dev/null @@ -1,74 +0,0 @@ -# Maintainer: Kim Silkebækken - -_gitname=powerline -_gitbranch=develop -pkgname="python-${_gitname}-git" # Workaround for missing split package support in AUR -true && pkgname=("python-${_gitname}-git" "${_gitname}-fontpatcher-git") -pkgbase=powerline -pkgver=822.225ac48 -pkgrel=1 -url='https://github.com/Lokaltog/powerline' -license=('MIT') -arch=('any') -makedepends=('git' 'python-setuptools') -provides=('powerline') -conflicts=('python2-powerline-git' - 'powerline-git') -install="${_gitname}.install" -source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" - "fontpatcher.py.patch" - "${install}") -sha256sums=('SKIP' - '85576097662ab4203968b5fba1d59ec2653a390cdd4db9cee8ffa7bd4c5a7253' - '7b1257cdacce60e19280f7d918e5f3aa6f13b519dff16ecc6f732c881ef63ca1') - -pkgver() { - cd "${_gitname}" - echo "$(git rev-list --count ${_gitbranch}).$(git rev-parse --short ${_gitbranch})" -} - -prepare() { - cd "${srcdir}/${_gitname}" - patch -p1 < ../fontpatcher.py.patch -} - -package_powerline-fontpatcher-git() { - pkgdesc='OTF/TTF font patcher for Powerline symbols' - depends=('python2' 'fontforge') - - cd "${_gitname}" - - # Font patcher - install -Dm755 "font/fontpatcher.py" "${pkgdir}/usr/bin/powerline-fontpatcher" - install -Dm644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/$_gitname/fontpatcher-symbols.sfd" -} - -package_python-powerline-git() { - pkgdesc='The ultimate statusline/prompt utility.' - depends=('python>=3.2') - optdepends=('python-psutil: improved system information' - 'python-pygit2: improved git support' - 'zsh: better shell prompt' - 'gvim: vim compiled with Python support') - - cd "${_gitname}" - python setup.py install --root="${pkgdir}" --optimize=1 - - # Fonts - install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -Dm644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" - install -Dm644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" - ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - - # Vim Plugin - install -Dm644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" - - # Zsh Plugin - install -Dm644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" - - # Tmux Configuration - install -Dm644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" - - # License - install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" -} diff --git a/packages/archlinux/python-powerline-git/fontpatcher.py.patch b/packages/archlinux/python-powerline-git/fontpatcher.py.patch deleted file mode 100644 index fb394ce2..00000000 --- a/packages/archlinux/python-powerline-git/fontpatcher.py.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/font/fontpatcher.py b/font/fontpatcher.py -index e2bbf2a..aa172f6 100755 ---- a/font/fontpatcher.py -+++ b/font/fontpatcher.py -@@ -21,7 +21,7 @@ except ImportError: - parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') - parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) - parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') --parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) -+parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='/usr/share/powerline/fontpatcher-symbols.sfd', type=argparse.FileType('rb')) - args = parser.parse_args() - - diff --git a/packages/archlinux/python-powerline-git/powerline.install b/packages/archlinux/python-powerline-git/powerline.install deleted file mode 100644 index d6644b17..00000000 --- a/packages/archlinux/python-powerline-git/powerline.install +++ /dev/null @@ -1,48 +0,0 @@ -post_install() { - echo "Updating font cache..." - fc-cache -f - - echo " -IMPORTANT -========= - -Powerline requires custom glyphs to work properly. A font with these glyphs has -been installed along with a fontconfig file which enables the glyphs for many -common coding fonts. - -If Powerline doesn't work out of the box on your system, please submit an issue -on GitHub: https://github.com/Lokaltog/powerline/issues - -Consult the documentation for detailed installation instructions and -troubleshooting information: https://powerline.readthedocs.org/en/latest/ - -You may need to update your PYTHONPATH environment variable by adding the -following line to your /etc/profile, .zshrc, etc.: - - export PYTHONPATH=/usr/lib/python3.3/site-packages - -Vim installation ----------------- - -The plugin has been installed and is enabled by default. - -Zsh installation ----------------- - -Add the following line to your ~/.zshrc: - - . /usr/share/zsh/site-contrib/powerline.zsh - -Tmux installation ------------------ - -Add the following line to your ~/.tmux.conf: - - source '/usr/share/tmux/powerline.conf' -" -} - -post_remove() { - echo "Updating font cache..." - fc-cache -f -} diff --git a/packages/archlinux/python2-powerline-git/.gitignore b/packages/archlinux/python2-powerline-git/.gitignore deleted file mode 100644 index cc7d5336..00000000 --- a/packages/archlinux/python2-powerline-git/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -* -!.gitignore -!PKGBUILD -!*.install -!*.patch diff --git a/packages/archlinux/python2-powerline-git/PKGBUILD b/packages/archlinux/python2-powerline-git/PKGBUILD deleted file mode 100644 index cc262dd5..00000000 --- a/packages/archlinux/python2-powerline-git/PKGBUILD +++ /dev/null @@ -1,75 +0,0 @@ -# Maintainer: Kim Silkebækken - -_gitname=powerline -_gitbranch=develop -pkgname="python2-${_gitname}-git" # Workaround for missing split package support in AUR -true && pkgname=("python2-${_gitname}-git" "${_gitname}-fontpatcher-git") -pkgbase=powerline -pkgver=822.225ac48 -pkgrel=1 -url='https://github.com/Lokaltog/powerline' -license=('MIT') -arch=('any') -makedepends=('git' 'python2-setuptools') -provides=('powerline') -conflicts=('python-powerline-git') -replaces=('powerline-git') -install="${_gitname}.install" -source=("${_gitname}::git://github.com/Lokaltog/${_gitname}.git#branch=${_gitbranch}" - "fontpatcher.py.patch" - "${install}") -sha256sums=('SKIP' - '85576097662ab4203968b5fba1d59ec2653a390cdd4db9cee8ffa7bd4c5a7253' - 'e8ab7fb51ac7244bfad973a999c9333ba4334fa391aa890489cf8c8f1211c94f') - -pkgver() { - cd "${_gitname}" - echo "$(git rev-list --count ${_gitbranch}).$(git rev-parse --short ${_gitbranch})" -} - -prepare() { - cd "${srcdir}/${_gitname}" - patch -p1 < ../fontpatcher.py.patch -} - -package_powerline-fontpatcher-git() { - pkgdesc='OTF/TTF font patcher for Powerline symbols' - depends=('python2' 'fontforge') - - cd "${_gitname}" - - # Font patcher - install -Dm755 "font/fontpatcher.py" "${pkgdir}/usr/bin/powerline-fontpatcher" - install -Dm644 "font/fontpatcher-symbols.sfd" "${pkgdir}/usr/share/${_gitname}/fontpatcher-symbols.sfd" -} - -package_python2-powerline-git() { - pkgdesc='The ultimate statusline/prompt utility.' - depends=('python2>=2.6') - optdepends=('python2-psutil: improved system information' - 'python2-pygit2: improved git support' - 'mercurial: improved mercurial support' - 'zsh: better shell prompt' - 'gvim: vim compiled with Python support') - - cd "${_gitname}" - python2 setup.py install --root="${pkgdir}" --optimize=1 - - # Fonts - install -dm755 "${pkgdir}/etc/fonts/conf.d" - install -Dm644 "font/PowerlineSymbols.otf" "${pkgdir}/usr/share/fonts/OTF/PowerlineSymbols.otf" - install -Dm644 "font/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.avail/10-powerline-symbols.conf" - ln -s "../conf.avail/10-powerline-symbols.conf" "${pkgdir}/etc/fonts/conf.d/10-powerline-symbols.conf" - - # Vim Plugin - install -Dm644 "powerline/bindings/vim/plugin/powerline.vim" "${pkgdir}/usr/share/vim/vimfiles/plugin/powerline.vim" - - # Zsh Plugin - install -Dm644 "powerline/bindings/zsh/powerline.zsh" "${pkgdir}/usr/share/zsh/site-contrib/powerline.zsh" - - # Tmux Configuration - install -Dm644 "powerline/bindings/tmux/powerline.conf" "${pkgdir}/usr/share/tmux/powerline.conf" - - # License - install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" -} diff --git a/packages/archlinux/python2-powerline-git/fontpatcher.py.patch b/packages/archlinux/python2-powerline-git/fontpatcher.py.patch deleted file mode 100644 index fb394ce2..00000000 --- a/packages/archlinux/python2-powerline-git/fontpatcher.py.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/font/fontpatcher.py b/font/fontpatcher.py -index e2bbf2a..aa172f6 100755 ---- a/font/fontpatcher.py -+++ b/font/fontpatcher.py -@@ -21,7 +21,7 @@ except ImportError: - parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') - parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) - parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') --parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) -+parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='/usr/share/powerline/fontpatcher-symbols.sfd', type=argparse.FileType('rb')) - args = parser.parse_args() - - diff --git a/packages/archlinux/python2-powerline-git/powerline.install b/packages/archlinux/python2-powerline-git/powerline.install deleted file mode 100644 index d7938d2b..00000000 --- a/packages/archlinux/python2-powerline-git/powerline.install +++ /dev/null @@ -1,43 +0,0 @@ -post_install() { - echo "Updating font cache..." - fc-cache -f - - echo " -IMPORTANT -========= - -Powerline requires custom glyphs to work properly. A font with these glyphs has -been installed along with a fontconfig file which enables the glyphs for many -common coding fonts. - -If Powerline doesn't work out of the box on your system, please submit an issue -on GitHub: https://github.com/Lokaltog/powerline/issues - -Consult the documentation for detailed installation instructions and -troubleshooting information: https://powerline.readthedocs.org/en/latest/ - -Vim installation ----------------- - -The plugin has been installed and is enabled by default. - -Zsh installation ----------------- - -Add the following line to your ~/.zshrc: - - . /usr/share/zsh/site-contrib/powerline.zsh - -Tmux installation ------------------ - -Add the following line to your ~/.tmux.conf: - - source '/usr/share/tmux/powerline.conf' -" -} - -post_remove() { - echo "Updating font cache..." - fc-cache -f -} From 43e199b4a751cab46d3302d66b3ec83176ff5734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sun, 19 Jan 2014 11:56:48 +0100 Subject: [PATCH 0834/1472] Remove fontpatcher and symbol font The package has been moved to https://github.com/Lokaltog/powerline-fontpatcher --- font/fontpatcher-symbols.sfd | 235 ----------------------------------- font/fontpatcher.py | 115 ----------------- 2 files changed, 350 deletions(-) delete mode 100644 font/fontpatcher-symbols.sfd delete mode 100755 font/fontpatcher.py diff --git a/font/fontpatcher-symbols.sfd b/font/fontpatcher-symbols.sfd deleted file mode 100644 index c65c6490..00000000 --- a/font/fontpatcher-symbols.sfd +++ /dev/null @@ -1,235 +0,0 @@ -SplineFontDB: 3.0 -FontName: PowerlineSymbols -FullName: PowerlineSymbols -FamilyName: PowerlineSymbols -Weight: Medium -Copyright: Created with FontForge 2.0 (http://fontforge.sf.net) -UComments: "2012-12-17: Created." -Version: 001.000 -ItalicAngle: 0 -UnderlinePosition: -100 -UnderlineWidth: 50 -Ascent: 1638 -Descent: 410 -LayerCount: 2 -Layer: 0 0 "Back" 1 -Layer: 1 0 "Fore" 0 -XUID: [1021 211 26716215 11183012] -OS2Version: 0 -OS2_WeightWidthSlopeOnly: 0 -OS2_UseTypoMetrics: 1 -CreationTime: 1355758773 -ModificationTime: 1355865048 -OS2TypoAscent: 0 -OS2TypoAOffset: 1 -OS2TypoDescent: 0 -OS2TypoDOffset: 1 -OS2TypoLinegap: 0 -OS2WinAscent: 0 -OS2WinAOffset: 1 -OS2WinDescent: 0 -OS2WinDOffset: 1 -HheadAscent: 0 -HheadAOffset: 1 -HheadDescent: 0 -HheadDOffset: 1 -OS2Vendor: 'PfEd' -MarkAttachClasses: 1 -DEI: 91125 -Encoding: UnicodeBmp -Compacted: 1 -UnicodeInterp: none -NameList: Adobe Glyph List -DisplaySize: -24 -AntiAlias: 1 -FitToEm: 1 -WinInfo: 0 31 16 -BeginPrivate: 0 -EndPrivate -BeginChars: 65536 8 - -StartChar: uniE0A0 -Encoding: 57504 57504 0 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -426 -365 m 1 - 150 -365 l 1 - 150 117 l 2 - 150 236.333333333 176.333333333 333.666666667 229 409 c 1 - 265 461.666666667 321.333333333 513 398 563 c 2 - 550 662 l 2 - 610 701.333333333 652.833333333 747.5 678.5 800.5 c 128 - 704.166666667 853.5 717 923.333333333 717 1010 c 2 - 717 1339 l 1 - 508 1339 l 1 - 800 1681 l 1 - 1092 1339 l 1 - 883 1339 l 1 - 883 954 l 2 - 883 826 865.166666667 727.833333333 829.5 659.5 c 128 - 793.833333333 591.166666667 740.333333333 533.666666667 669 487 c 1 - 594 437 l 2 - 532 396.333333333 489.333333333 355 466 313 c 0 - 439.333333333 265.666666667 426 200.333333333 426 117 c 2 - 426 -365 l 1 -426 820 m 1 - 150 642 l 1 - 150 1950 l 1 - 426 1950 l 1 - 426 820 l 1 -EndSplineSet -EndChar - -StartChar: uniE0A1 -Encoding: 57505 57505 1 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -700 963 m 1 - 700 831 l 1 - 194 831 l 1 - 194 1731 l 1 - 349 1731 l 1 - 349 963 l 1 - 700 963 l 1 -706 750 m 1 - 865 750 l 1 - 865 -150 l 1 - 698 -150 l 1 - 453 435 l 1 - 472 45 l 1 - 472 -150 l 1 - 315 -150 l 1 - 315 750 l 1 - 480 750 l 1 - 727 163 l 1 - 706 592 l 1 - 706 750 l 1 -EndSplineSet -EndChar - -StartChar: uniE0A2 -Encoding: 57506 57506 2 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -265 0 m 2 - 171 0 124 47 124 141 c 2 - 124 830 l 2 - 124 930.666666667 164.333333333 981 245 981 c 1 - 245 1287 l 2 - 245 1378.33333333 273 1454.33333333 329 1515 c 0 - 383 1573 450 1602 530 1602 c 256 - 610 1602 677 1573 731 1515 c 0 - 787 1454.33333333 815 1378.33333333 815 1287 c 2 - 815 981 l 1 - 895.666666667 981 936 930.666666667 936 830 c 2 - 936 141 l 2 - 936 47 889 0 795 0 c 2 - 265 0 l 2 -530 1472 m 256 - 485.333333333 1472 449.5 1455.33333333 422.5 1422 c 128 - 395.5 1388.66666667 382 1343.66666667 382 1287 c 2 - 382 981 l 1 - 678 981 l 1 - 678 1287 l 2 - 678 1343.66666667 664.5 1388.66666667 637.5 1422 c 128 - 610.5 1455.33333333 574.666666667 1472 530 1472 c 256 -586 236 m 1 - 586 559 l 1 - 631.333333333 582.333333333 654 619.333333333 654 670 c 0 - 654 704 641.833333333 733.166666667 617.5 757.5 c 128 - 593.166666667 781.833333333 564 794 530 794 c 256 - 496 794 466.833333333 781.833333333 442.5 757.5 c 128 - 418.166666667 733.166666667 406 704 406 670 c 0 - 406 619.333333333 428.666666667 582.333333333 474 559 c 1 - 474 236 l 1 - 586 236 l 1 -EndSplineSet -EndChar - -StartChar: uniE0B0 -Encoding: 57520 57520 3 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -0 1950 m 1 - 1060 788 l 1 - 0 -375 l 1 - 0 1950 l 1 -EndSplineSet -EndChar - -StartChar: uniE0B1 -Encoding: 57521 57521 4 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet --57 1812 m 1 - 39 1907 l 1 - 1060 788 l 1 - 39 -331 l 1 - -57 -236 l 1 - 875 788 l 1 - -57 1812 l 1 -EndSplineSet -EndChar - -StartChar: uniE0B2 -Encoding: 57522 57522 5 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -1060 -375 m 1 - 0 788 l 1 - 1060 1950 l 1 - 1060 -375 l 1 -EndSplineSet -EndChar - -StartChar: uniE0B3 -Encoding: 57523 57523 6 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -185 788 m 1 - 1117 -236 l 1 - 1021 -331 l 1 - 0 788 l 1 - 1021 1907 l 1 - 1117 1812 l 1 - 185 788 l 1 -EndSplineSet -EndChar - -StartChar: block -Encoding: 9608 9608 7 -Width: 1060 -Flags: HW -LayerCount: 2 -Fore -SplineSet -0 1950 m 1 - 1060 1950 l 1 - 1060 -375 l 1 - 0 -375 l 1 - 0 1950 l 1 -EndSplineSet -EndChar -EndChars -EndSplineFont diff --git a/font/fontpatcher.py b/font/fontpatcher.py deleted file mode 100755 index e2bbf2a0..00000000 --- a/font/fontpatcher.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python2 -# vim:fileencoding=utf-8:noet - -import argparse -import sys -import re -import os.path - -try: - import fontforge - import psMat -except ImportError: - sys.stderr.write('The required FontForge modules could not be loaded.\n\n') - if sys.version_info.major > 2: - sys.stderr.write('FontForge only supports Python 2. Please run this script with the Python 2 executable - e.g. "python2 {0}"\n'.format(sys.argv[0])) - else: - sys.stderr.write('You need FontForge with Python bindings for this script to work.\n') - sys.exit(1) - -# Handle command-line arguments -parser = argparse.ArgumentParser(description='Font patcher for Powerline. Requires FontForge with Python bindings. Stores the patched font as a new, renamed font file by default.') -parser.add_argument('target_fonts', help='font files to patch', metavar='font', nargs='+', type=argparse.FileType('rb')) -parser.add_argument('--no-rename', help='don\'t add " for Powerline" to the font name', default=True, action='store_false', dest='rename_font') -parser.add_argument('--source-font', help='source symbol font', metavar='font', dest='source_font', default='{0}/fontpatcher-symbols.sfd'.format(sys.path[0]), type=argparse.FileType('rb')) -args = parser.parse_args() - - -class FontPatcher(object): - def __init__(self, source_font, target_fonts, rename_font=True): - self.source_font = fontforge.open(source_font.name) - self.target_fonts = (fontforge.open(target_font.name) for target_font in target_fonts) - self.rename_font = rename_font - - def patch(self): - for target_font in self.target_fonts: - source_font = self.source_font - target_font_em_original = target_font.em - target_font.em = 2048 - target_font.encoding = 'ISO10646' - - # Rename font - if self.rename_font: - target_font.familyname += ' for Powerline' - target_font.fullname += ' for Powerline' - fontname, style = re.match("^([^-]*)(?:(-.*))?$", target_font.fontname).groups() - target_font.fontname = fontname + 'ForPowerline' - if style is not None: - target_font.fontname += style - target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) - target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) - - source_bb = source_font['block'].boundingBox() - target_bb = [0, 0, 0, 0] - target_font_width = 0 - - # Find the biggest char width and height in the Latin-1 extended range and the box drawing range - # This isn't ideal, but it works fairly well - some fonts may need tuning after patching - for cp in range(0x00, 0x17f) + range(0x2500, 0x2600): - try: - bbox = target_font[cp].boundingBox() - except TypeError: - continue - if not target_font_width: - target_font_width = target_font[cp].width - if bbox[0] < target_bb[0]: - target_bb[0] = bbox[0] - if bbox[1] < target_bb[1]: - target_bb[1] = bbox[1] - if bbox[2] > target_bb[2]: - target_bb[2] = bbox[2] - if bbox[3] > target_bb[3]: - target_bb[3] = bbox[3] - - # Find source and target size difference for scaling - x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) - y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) - scale = psMat.scale(x_ratio, y_ratio) - - # Find source and target midpoints for translating - x_diff = target_bb[0] - source_bb[0] - y_diff = target_bb[1] - source_bb[1] - translate = psMat.translate(x_diff, y_diff) - transform = psMat.compose(scale, translate) - - # Create new glyphs from symbol font - for source_glyph in source_font.glyphs(): - if source_glyph == source_font['block']: - # Skip the symbol font block glyph - continue - - # Select and copy symbol from its encoding point - source_font.selection.select(source_glyph.encoding) - source_font.copy() - - # Select and paste symbol to its unicode code point - target_font.selection.select(source_glyph.unicode) - target_font.paste() - - # Transform the glyph - target_font.transform(transform) - - # Reset the font's glyph width so it's still considered monospaced - target_font[source_glyph.unicode].width = target_font_width - - target_font.em = target_font_em_original - - # Generate patched font - extension = os.path.splitext(target_font.path)[1] - if extension.lower() not in ['.ttf', '.otf']: - # Default to OpenType if input is not TrueType/OpenType - extension = '.otf' - target_font.generate('{0}{1}'.format(target_font.fullname, extension)) - -fp = FontPatcher(args.source_font, args.target_fonts, args.rename_font) -fp.patch() From 1a841a4ac8e68f2967e9369a03b38cb0d9a6a5ee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Jan 2014 16:15:40 +0400 Subject: [PATCH 0835/1472] Replace POWERLINE_OLD_PROMPT_COMMAND with the code used by [z][1] Does not fix anything, but is three lines less and is probably the way other tools expect us to do. [1]: https://github.com/rupa/z --- powerline/bindings/bash/powerline.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 4e4d52de..f2fd7876 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -31,8 +31,6 @@ _powerline_tmux_set_columns() { _powerline_prompt() { local last_exit_code=$? - [[ -z "$POWERLINE_OLD_PROMPT_COMMAND" ]] || - eval $POWERLINE_OLD_PROMPT_COMMAND PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs|wc -l)")" _powerline_tmux_set_pwd return $last_exit_code @@ -42,5 +40,4 @@ trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns [[ "$PROMPT_COMMAND" != "${PROMPT_COMMAND/_powerline_prompt/}" ]] || - POWERLINE_OLD_PROMPT_COMMAND="$PROMPT_COMMAND" -export PROMPT_COMMAND="_powerline_prompt" + export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n'"_powerline_prompt;" From c6324afbc5b06b49d63b78f3b83a955ce37b98b4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Jan 2014 16:19:23 +0400 Subject: [PATCH 0836/1472] Replace `jobs` with `jobs -p` If some long shell code is run in the background it may be shown as more then one job: bash-4.2$ { > echo abc | while read line ; do > sleep 1s > sleep 1s > done > } & [1] 9401 bash-4.2$ jobs [1]+ Running { echo abc | while read line; do sleep 1s; sleep 1s; done; } & `jobs -p` print exactly one job in this case. --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index f2fd7876..16b295b2 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -31,7 +31,7 @@ _powerline_tmux_set_columns() { _powerline_prompt() { local last_exit_code=$? - PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs|wc -l)")" + PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs -p|wc -l)")" _powerline_tmux_set_pwd return $last_exit_code } From 49d837684519c537587f2c7206c85e02c6f8f8b1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Jan 2014 16:27:12 +0400 Subject: [PATCH 0837/1472] Document problem found when exploring #749 --- docs/source/installation/troubleshooting-common.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index a0142e38..c5b4b2e2 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -60,3 +60,15 @@ My vim statusline is not displayed completely and has too much spaces * Be sure you have ``ambiwidth`` option set to ``single``. * Alternative: set :ref:`ambiwidth ` to 2, remove fancy dividers (they suck when ``ambiwidth`` is set to double). + +When using `z ` powerline shows wrong number of jobs +------------------------------------------------------------------------------- + +This happens because `z ` is launching some jobs in +the background from ``$POWERLINE_COMMAND`` and these jobs fail to finish before +powerline prompt is run. + +Solution to this problem is simple: be sure that :file:`z.sh` is sourced +strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background +jobs are spawned by `z ` after powerline has done its +job. From a590e100a23a0c225467b34b7c4481ece45905ad Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Jan 2014 17:26:31 +0400 Subject: [PATCH 0838/1472] Fix functional shell tests in travis Hostname in travis contains random numbers meaning that it occasionally may contain a PID as well. Thus it must be replaced first. --- tests/test_shells/postproc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index ae2e887a..5c1832f2 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -29,9 +29,9 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: line = line.translate({ ord('\r'): None }) - line = line.replace(pid, 'PID') line = line.replace(hostname, 'HOSTNAME') line = line.replace(user, 'USER') + line = line.replace(pid, 'PID') W.write(line) os.rename(new_fname, fname) From 38f24852eb92ff41c1dcab5edc1e9d2bffea58c6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 19:18:34 +0400 Subject: [PATCH 0839/1472] Add powerline/bindings/fish/powerline.fish --- powerline/bindings/fish/powerline.fish | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 powerline/bindings/fish/powerline.fish diff --git a/powerline/bindings/fish/powerline.fish b/powerline/bindings/fish/powerline.fish new file mode 100644 index 00000000..5053a5b4 --- /dev/null +++ b/powerline/bindings/fish/powerline.fish @@ -0,0 +1,20 @@ +function powerline + if test -z "$POWERLINE_COMMAND" + if which powerline-client >/dev/null + set -g -x POWERLINE_COMMAND powerline-client + else + set -g -x POWERLINE_COMMAND powerline + end + end + function _powerline_update -v POWERLINE_COMMAND + eval " + function fish_prompt + $POWERLINE_COMMAND shell left --last_exit_code=\$status --last_pipe_status=\$status + end + function fish_right_prompt + $POWERLINE_COMMAND shell right --last_exit_code=\$status --last_pipe_status=\$status + end + " + end + _powerline_update +end From 972bd45c21a369bb934b83842c482afcc7061eef Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Nov 2013 23:07:49 +0400 Subject: [PATCH 0840/1472] Make fish bindings feature complete, add documentation Added features: - Updating TMUX_PWD_* when PWD variable changes - Updating TMUX_COLUMNS_* when WINCH signal is received --- docs/source/overview.rst | 10 ++++++++++ powerline/bindings/fish/powerline.fish | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index c4ede1af..770e769c 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -150,6 +150,16 @@ absolute path to your Powerline installation directory: . {repository_root}/powerline/bindings/zsh/powerline.zsh +Fish prompt +^^^^^^^^^^^ + +Add the following line to your :file:`config.fish`, where ``{repository_root}`` +is the absolute path to your Powerline installation directory: + +.. code-block:: bash + + . {repository_root}/powerline/bindings/fish/powerline.fish + Tmux statusline --------------- diff --git a/powerline/bindings/fish/powerline.fish b/powerline/bindings/fish/powerline.fish index 5053a5b4..8707e744 100644 --- a/powerline/bindings/fish/powerline.fish +++ b/powerline/bindings/fish/powerline.fish @@ -6,7 +6,7 @@ function powerline set -g -x POWERLINE_COMMAND powerline end end - function _powerline_update -v POWERLINE_COMMAND + function --on-variable POWERLINE_COMMAND _powerline_update eval " function fish_prompt $POWERLINE_COMMAND shell left --last_exit_code=\$status --last_pipe_status=\$status @@ -17,4 +17,19 @@ function powerline " end _powerline_update + function _powerline_tmux_setenv + if test -n "$TMUX" + tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" + tmux refresh -S + end + end + function --on-variable PWD _powerline_tmux_set_pwd + _powerline_tmux_setenv PWD "$PWD" + end + function --on-signal WINCH _powerline_tmux_set_columns + _powerline_tmux_setenv COLUMNS "$COLUMNS" + end + _powerline_tmux_set_columns + _powerline_tmux_set_pwd end +powerline From 461c4f4d51f93f3c5b577c9d5d790745b34ab0e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Jan 2014 17:51:48 +0400 Subject: [PATCH 0841/1472] Add support for number of jobs to fish bindings --- powerline/bindings/fish/powerline.fish | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/fish/powerline.fish b/powerline/bindings/fish/powerline.fish index 8707e744..196e9c81 100644 --- a/powerline/bindings/fish/powerline.fish +++ b/powerline/bindings/fish/powerline.fish @@ -7,12 +7,13 @@ function powerline end end function --on-variable POWERLINE_COMMAND _powerline_update + set -l addargs "--last_exit_code=\$status --last_pipe_status=\$status --jobnum=(jobs -p | wc -l)" eval " function fish_prompt - $POWERLINE_COMMAND shell left --last_exit_code=\$status --last_pipe_status=\$status + $POWERLINE_COMMAND shell left $addargs end function fish_right_prompt - $POWERLINE_COMMAND shell right --last_exit_code=\$status --last_pipe_status=\$status + $POWERLINE_COMMAND shell right $addargs end " end From 90132fe386d2cf7e4d677ddf283f436fb6926e88 Mon Sep 17 00:00:00 2001 From: EinfachToll Date: Tue, 21 Jan 2014 15:48:05 +0100 Subject: [PATCH 0842/1472] Small fixes for new position segment - use vim_funcs - use a dictionary instead of a tuple for translation strings --- powerline/segments/vim.py | 17 +++++++++-------- tests/test_segments.py | 2 +- tests/vim.py | 13 +++++++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index b3cd41ed..07da7e37 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -23,6 +23,7 @@ vim_funcs = { 'expand': vim_get_func('expand', rettype=str), 'bufnr': vim_get_func('bufnr', rettype=int), 'line2byte': vim_get_func('line2byte', rettype=int), + 'line': vim_get_func('line', rettype=int), } vim_modes = { @@ -266,11 +267,11 @@ def line_percent(pl, segment_info, gradient=False): @window_cached -def position(pl, position_strings=('Top', 'Bot', 'All'), gradient=False): +def position(pl, position_strings={'top':'Top', 'bottom':'Bot', 'all':'All'}, gradient=False): '''Return the position of the current view in the file as a percentage. - :param tuple position_strings: - use these three strings to indicate whether we are at top or bottom or see the complete file + :param dict position_strings: + dict for translation of the position strings, e.g. ``{"top":"Oben", "bottom":"Unten", "all":"Alles"}`` :param bool gradient: highlight the percentage with a color gradient (by default a green to red gradient) @@ -279,17 +280,17 @@ def position(pl, position_strings=('Top', 'Bot', 'All'), gradient=False): ''' line_last = len(vim.current.buffer) - winline_first = int(vim.eval('line("w0")')) - winline_last = int(vim.eval('line("w$")')) + winline_first = vim_funcs['line']('w0') + winline_last = vim_funcs['line']('w$') if winline_first == 1 and winline_last == line_last: percentage = 0.0 - content = position_strings[2] + content = position_strings['all'] elif winline_first == 1: percentage = 0.0 - content = position_strings[0] + content = position_strings['top'] elif winline_last == line_last: percentage = 100.0 - content = position_strings[1] + content = position_strings['bottom'] else: percentage = winline_first * 100.0 / (line_last - winline_last + winline_first) content = str(int(round(percentage))) + '%' diff --git a/tests/test_segments.py b/tests/test_segments.py index c3014ac7..7759c9f2 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -508,7 +508,7 @@ class TestVim(TestCase): segment_info['buffer'][:] = ['1'] vim_module._set_cursor(1, 0) self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'All') - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings=('Comienzo', 'Final', 'Todo')), 'Todo') + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top':'Comienzo', 'bottom':'Final', 'all':'Todo'}), 'Todo') self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [{'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0}]) finally: diff --git a/tests/vim.py b/tests/vim.py index da12c812..a7ee45ca 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -169,10 +169,6 @@ def eval(expr): return vars[expr[2:]] elif expr.startswith('&'): return options[expr[1:]] - elif expr == 'line("w0")': - return 1 - elif expr == 'line("w$")': - return 1 elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) return '0' @@ -306,6 +302,15 @@ def _emul_line2byte(line): raise NotImplementedError +@_vim +def _emul_line(expr): + if expr == 'w0': + return 1 + if expr == 'w$': + return 1 + raise NotImplementedError + + _window_ids = [None] _window_id = 0 From fee1b3b3b1b0cfb75d7064c07fc19699bb34c0d2 Mon Sep 17 00:00:00 2001 From: bricewge Date: Tue, 21 Jan 2014 21:25:29 +0100 Subject: [PATCH 0843/1472] Fix the identation of 10-powerline-symbols.conf --- font/10-powerline-symbols.conf | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/font/10-powerline-symbols.conf b/font/10-powerline-symbols.conf index 6f689ea6..2a0d7255 100644 --- a/font/10-powerline-symbols.conf +++ b/font/10-powerline-symbols.conf @@ -70,24 +70,24 @@ Meslo LG L PowerlineSymbols - - Meslo LG L DZ + + Meslo LG L DZ PowerlineSymbols - - - Meslo LG M - PowerlineSymbols - - - Meslo LG M DZ - PowerlineSymbols - - - Meslo LG S - PowerlineSymbols - - - Meslo LG S DZ - PowerlineSymbols - + + + Meslo LG M + PowerlineSymbols + + + Meslo LG M DZ + PowerlineSymbols + + + Meslo LG S + PowerlineSymbols + + + Meslo LG S DZ + PowerlineSymbols + From 11f86e77b06ac561802fed2a28559dbc7d3171f6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 22 Jan 2014 08:33:43 +0400 Subject: [PATCH 0844/1472] Add fish tests --- tests/install.sh | 2 +- tests/test_shells/fish.ok | 10 ++++++++++ tests/test_shells/input.fish | 15 +++++++++++++++ tests/test_shells/test.sh | 21 ++++++++++++++------- 4 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 tests/test_shells/fish.ok create mode 100644 tests/test_shells/input.fish diff --git a/tests/install.sh b/tests/install.sh index 24ad4a22..20ddf745 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,5 +9,5 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -sudo apt-get install -qq zsh screen +sudo apt-get install -qq screen zsh fish true diff --git a/tests/test_shells/fish.ok b/tests/test_shells/fish.ok new file mode 100644 index 00000000..428a9d9b --- /dev/null +++ b/tests/test_shells/fish.ok @@ -0,0 +1,10 @@ +]0;source /home/USER/.vam/powerline-dev]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +]0;cd /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd/.git⏎   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +]0;cd /home/USER/.vam/powerline-dev/tests/shell/3rd/.git]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" +]0;set /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  set VIRTUAL_ENV +]0;set /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +]0;bash /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +]0;false /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill (cat pid) ; sleep 1s +]0;kill /home/USER/.vam/powerline-dev/tests/shell/3rdfish: Job 1, “bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" &” terminated by signal SIGTERM (Polite quit request) +]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +]0;false /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎ \ No newline at end of file diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish new file mode 100644 index 00000000..658d4899 --- /dev/null +++ b/tests/test_shells/input.fish @@ -0,0 +1,15 @@ +set POWERLINE_COMMAND "$PWD/scripts/powerline -p $PWD/powerline/config_files" +set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +set VIRTUAL_ENV +source powerline/bindings/fish/powerline.fish ; cd tests/shell/3rd +cd .git +cd .. +set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" +set VIRTUAL_ENV +bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +false +kill (cat pid) ; sleep 1s +false +true is the last line +exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index f47ab5a5..d2350190 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -12,9 +12,13 @@ else fi check_screen_log() { - diff -u tests/test_shells/${1}.ok tests/shell/screen.log - # Explicit is better then implicit - return $? + if test -e tests/test_shells/${1}.ok ; then + diff -u tests/test_shells/${1}.ok tests/shell/screen.log + return $? + else + cat tests/shell/screen.log + return 1 + fi } run_test() { @@ -37,6 +41,7 @@ run_test() { if ! check_screen_log ${SH} ; then # Repeat the diff to make it better viewable in travis output check_screen_log ${SH} | cat -v + echo "Failed ${SH}" return 1 fi return 0 @@ -47,17 +52,19 @@ git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH if ! run_test bash --norc --noprofile -i ; then - echo "Failed bash" FAILED=1 fi -cp tests/shell/screen.log tests/bash.log rm tests/shell/screen.log if ! run_test zsh -f -i ; then - echo "Failed zsh" FAILED=1 fi -cp tests/shell/screen.log tests/zsh.log +rm tests/shell/screen.log + +export XDG_CONFIG_HOME=/dev/null +if ! run_test fish -i ; then + FAILED=1 +fi rm tests/shell/screen.log rm -r tests/shell From cab3362377beb176dd8de55d660a25c0c60ef10d Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Wed, 22 Jan 2014 15:35:15 -0500 Subject: [PATCH 0845/1472] Fix Syntastic deprecation warning `hasErrorsOrWarningsToDisplay` has been deprecated in Syntastic. See: https://github.com/scrooloose/syntastic/commit/d629be9 This fixes the warning that appears when opening a new file: "syntastic: warning: function hasErrorsOrWarningsToDisplay() is deprecated, please use !isEmpty() instead" --- powerline/segments/plugin/syntastic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/plugin/syntastic.py b/powerline/segments/plugin/syntastic.py index 71a411fa..97533f68 100644 --- a/powerline/segments/plugin/syntastic.py +++ b/powerline/segments/plugin/syntastic.py @@ -22,7 +22,7 @@ def syntastic(pl, err_format='ERR:  {first_line} ({num}) ', warn_format='WARN ''' if not int(vim.eval('exists("g:SyntasticLoclist")')): return - has_errors = int(vim.eval('g:SyntasticLoclist.current().hasErrorsOrWarningsToDisplay()')) + has_errors = not int(vim.eval('g:SyntasticLoclist.current().isEmpty()')) if not has_errors: return errors = vim.eval('g:SyntasticLoclist.current().errors()') From 166f827c9faa0ea2eb9014622a11d9847c760c48 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 07:36:08 +0400 Subject: [PATCH 0846/1472] Improve test_shells/test.sh: - Add argument that makes it run only one shell - Make this argument also force skipping removal of tests/shell directory - Make it copy logs to tests/shell before and after processing --- tests/test_shells/test.sh | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index d2350190..f2f07e71 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -1,15 +1,6 @@ #!/bin/sh FAILED=0 - -if [ "$(echo '\e')" != '\e' ] ; then - safe_echo() { - echo -E "$@" - } -else - safe_echo() { - echo "$@" - } -fi +ONLY_SHELL="$1" check_screen_log() { if test -e tests/test_shells/${1}.ok ; then @@ -23,12 +14,15 @@ check_screen_log() { run_test() { SH="$1" - SESNAME="powerline-shell-test-$$" + SESNAME="powerline-shell-test-${SH}-$$" + + test "x$ONLY_SHELL" = "x" || test "x$ONLY_SHELL" = "x$SH" || return 0 + screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH # Wait for screen to initialize - sleep 0.3s + sleep 1s screen -S "$SESNAME" -p 0 -X width 300 1 screen -S "$SESNAME" -p 0 -X logfile tests/shell/screen.log screen -S "$SESNAME" -p 0 -X paste a @@ -37,16 +31,21 @@ run_test() { while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do sleep 0.1s done + cp tests/shell/screen.log tests/shell/${SH}.full.log ./tests/test_shells/postproc.py tests/shell/screen.log + cp tests/shell/screen.log tests/shell/${SH}.log if ! check_screen_log ${SH} ; then # Repeat the diff to make it better viewable in travis output check_screen_log ${SH} | cat -v echo "Failed ${SH}" + rm tests/shell/screen.log return 1 fi + rm tests/shell/screen.log return 0 } +test -d tests/shell && rm -r tests/shell mkdir tests/shell git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH @@ -54,18 +53,15 @@ git --git-dir=tests/shell/3rd/.git checkout -b BRANCH if ! run_test bash --norc --noprofile -i ; then FAILED=1 fi -rm tests/shell/screen.log if ! run_test zsh -f -i ; then FAILED=1 fi -rm tests/shell/screen.log export XDG_CONFIG_HOME=/dev/null if ! run_test fish -i ; then FAILED=1 fi -rm tests/shell/screen.log -rm -r tests/shell +test "x$ONLY_SHELL" = "x" && rm -r tests/shell exit $FAILED From 2f98276ee0d930f4b20eacc22cedc1fa04cb6e24 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 07:36:54 +0400 Subject: [PATCH 0847/1472] Make postproc.py only leave fish prompt Should fix fish tests --- tests/test_shells/fish.ok | 18 ++++++++---------- tests/test_shells/postproc.py | 9 +++++++++ tests/test_shells/test.sh | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/test_shells/fish.ok b/tests/test_shells/fish.ok index 428a9d9b..9702a1c0 100644 --- a/tests/test_shells/fish.ok +++ b/tests/test_shells/fish.ok @@ -1,10 +1,8 @@ -]0;source /home/USER/.vam/powerline-dev]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -]0;cd /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd/.git⏎   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -]0;cd /home/USER/.vam/powerline-dev/tests/shell/3rd/.git]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" -]0;set /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  set VIRTUAL_ENV -]0;set /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & -]0;bash /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -]0;false /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill (cat pid) ; sleep 1s -]0;kill /home/USER/.vam/powerline-dev/tests/shell/3rdfish: Job 1, “bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" &” terminated by signal SIGTERM (Polite quit request) -]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false -]0;false /home/USER/.vam/powerline-dev/tests/shell/3rd]0;fish /home/USER/.vam/powerline-dev/tests/shell/3rd⏎ \ No newline at end of file +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 5c1832f2..d780ba76 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -12,6 +12,8 @@ fname = sys.argv[1] new_fname = fname + '.new' pid_fname = 'tests/shell/3rd/pid' +shell = sys.argv[2] + with open(pid_fname, 'r') as P: pid = P.read().strip() hostname = socket.gethostname() @@ -32,6 +34,13 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: line = line.replace(hostname, 'HOSTNAME') line = line.replace(user, 'USER') line = line.replace(pid, 'PID') + if shell == 'fish': + try: + start = line.index('\033[0;') + end = line.index('\033[0m', start) + line = line[start : end+4] + '\n' + except ValueError: + line = '' W.write(line) os.rename(new_fname, fname) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index f2f07e71..04aaab68 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -32,7 +32,7 @@ run_test() { sleep 0.1s done cp tests/shell/screen.log tests/shell/${SH}.full.log - ./tests/test_shells/postproc.py tests/shell/screen.log + ./tests/test_shells/postproc.py tests/shell/screen.log ${SH} cp tests/shell/screen.log tests/shell/${SH}.log if ! check_screen_log ${SH} ; then # Repeat the diff to make it better viewable in travis output From 6004403947e3c28513931bcfd790e204edff66af Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 07:45:35 +0400 Subject: [PATCH 0848/1472] Another attempt to fix fish on travis --- tests/test_shells/postproc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index d780ba76..1654bc7c 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -22,7 +22,7 @@ user = os.environ['USER'] with codecs.open(fname, 'r', encoding='utf-8') as R: with codecs.open(new_fname, 'w', encoding='utf-8') as W: found_cd = False - for line in R: + for line in (R if shell != 'fish' else R.read().split('\n')): if not found_cd: found_cd = ('cd tests/shell/3rd' in line) continue From 10cd7b78f0f4fe9d5a9864a610c3d401ba1b4fa3 Mon Sep 17 00:00:00 2001 From: EinfachToll Date: Thu, 23 Jan 2014 09:23:53 +0100 Subject: [PATCH 0849/1472] More test cases for position segment --- tests/test_segments.py | 14 +++++++++++--- tests/vim.py | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index 7759c9f2..1f039981 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -505,9 +505,17 @@ class TestVim(TestCase): pl = Pl() segment_info = vim_module._get_segment_info() try: - segment_info['buffer'][:] = ['1'] - vim_module._set_cursor(1, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'All') + segment_info['buffer'][0:-1] = [str(i) for i in range(99)] + vim_module._set_cursor(49, 0) + self.assertEqual(vim.position(pl=pl, segment_info=segment_info), '50%') + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), + [{'contents': '50%', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 50.0}]) + vim_module._set_cursor(0, 0) + self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'Top') + vim_module._set_cursor(97, 0) + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top':'Comienzo', 'bottom':'Final', 'all':'Todo'}), 'Final') + segment_info['buffer'][0:-1] = [str(i) for i in range(2)] + vim_module._set_cursor(0, 0) self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top':'Comienzo', 'bottom':'Final', 'all':'Todo'}), 'Todo') self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [{'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0}]) diff --git a/tests/vim.py b/tests/vim.py index a7ee45ca..3cde286f 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -304,10 +304,12 @@ def _emul_line2byte(line): @_vim def _emul_line(expr): + cursorline = windows[_window - 1].cursor[0] + 1 + numlines = len(_buf_lines[_buffer()]) if expr == 'w0': - return 1 + return max(cursorline-5, 1) if expr == 'w$': - return 1 + return min(cursorline+5, numlines) raise NotImplementedError From 12aa69a0c2ef4a60cbd95ea2dc4e0d6b8e10acb6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 15:58:52 +0400 Subject: [PATCH 0850/1472] Also output full.log Hope it will give some hints about why fish tests fail on travis --- tests/test_shells/test.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 04aaab68..0fbc417e 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -35,9 +35,20 @@ run_test() { ./tests/test_shells/postproc.py tests/shell/screen.log ${SH} cp tests/shell/screen.log tests/shell/${SH}.log if ! check_screen_log ${SH} ; then + echo '____________________________________________________________' # Repeat the diff to make it better viewable in travis output + echo "Diff (cat -v):" + echo '============================================================' check_screen_log ${SH} | cat -v - echo "Failed ${SH}" + echo '____________________________________________________________' + echo "Failed ${SH}. Full output:" + echo '============================================================' + cat tests/shell/${SH}.full.log + echo '____________________________________________________________' + echo "Full output (cat -v):" + echo '============================================================' + cat -v tests/shell/${SH}.full.log + echo '____________________________________________________________' rm tests/shell/screen.log return 1 fi From 8b341c622ed0a15d29ea43fd4530b843d0a682ef Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 16:14:32 +0400 Subject: [PATCH 0851/1472] Make XDG_CONFIG_HOME point to writeable location --- tests/test_shells/test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 0fbc417e..977b30e7 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -69,7 +69,8 @@ if ! run_test zsh -f -i ; then FAILED=1 fi -export XDG_CONFIG_HOME=/dev/null +mkdir tests/shell/fish_home +export XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" if ! run_test fish -i ; then FAILED=1 fi From d75f47a0c73611a9dc24e77a072e6f83a80de594 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 16:32:18 +0400 Subject: [PATCH 0852/1472] Use `.` for sourcing in fish It appears that travis uses fish version without this command. Though `.` is deprecated on new versions it is still supported for backwards compatibility, thus may be used here. --- tests/test_shells/input.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 658d4899..41f0ca40 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -2,7 +2,7 @@ set POWERLINE_COMMAND "$PWD/scripts/powerline -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" set VIRTUAL_ENV -source powerline/bindings/fish/powerline.fish ; cd tests/shell/3rd +. powerline/bindings/fish/powerline.fish ; cd tests/shell/3rd cd .git cd .. set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" From b37940235ce1ebd10d52b9df184a143718ef1e05 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 16:41:29 +0400 Subject: [PATCH 0853/1472] Also print shell version string --- tests/test_shells/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 977b30e7..c28b9827 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -49,6 +49,7 @@ run_test() { echo '============================================================' cat -v tests/shell/${SH}.full.log echo '____________________________________________________________' + ${SH} --version rm tests/shell/screen.log return 1 fi From 9fcfd54d90fa724ce052a83f444f5f54625b240c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 18:33:33 +0400 Subject: [PATCH 0854/1472] Disable fish tests on travis --- tests/install.sh | 4 +++- tests/test_shells/test.sh | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/install.sh b/tests/install.sh index 20ddf745..a448371f 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,5 +9,7 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -sudo apt-get install -qq screen zsh fish +sudo apt-get install -qq screen zsh +# Travis has too outdated fish. It cannot be used for tests. +# sudo apt-get install fish true diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index c28b9827..3d8b8cc2 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -18,6 +18,8 @@ run_test() { test "x$ONLY_SHELL" = "x" || test "x$ONLY_SHELL" = "x$SH" || return 0 + which "${SH}" || return 0 + screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH From b75d2b531f06d988947fa791cb8980076fc5fdc5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 18:45:09 +0400 Subject: [PATCH 0855/1472] Add tests --- tests/test_segments.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index d9d12527..6a52881c 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -145,6 +145,22 @@ class TestCommon(TestCase): {'contents': 'fo/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) + cwd[0] = '/etc' + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, use_path_separator=False), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'etc', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, use_path_separator=True), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'etc', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + cwd[0] = '/' + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, use_path_separator=False), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, use_path_separator=True), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) ose = OSError() ose.errno = 2 cwd[0] = ose From e489e549a6b8bc34a5c8f039b4b35bccd6921fe7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 19:32:19 +0400 Subject: [PATCH 0856/1472] Move setup to powerline-setup.fish This file is supposed to be placed in /usr/share/fish/functions. --- docs/source/overview.rst | 3 ++- .../bindings/fish/{powerline.fish => powerline-setup.fish} | 3 +-- tests/test_shells/input.fish | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) rename powerline/bindings/fish/{powerline.fish => powerline-setup.fish} (97%) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 770e769c..90a4a791 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -158,7 +158,8 @@ is the absolute path to your Powerline installation directory: .. code-block:: bash - . {repository_root}/powerline/bindings/fish/powerline.fish + set fish_function_path $fish_function_path "{repository_root}/powerline/bindings/fish" + powerline-setup Tmux statusline --------------- diff --git a/powerline/bindings/fish/powerline.fish b/powerline/bindings/fish/powerline-setup.fish similarity index 97% rename from powerline/bindings/fish/powerline.fish rename to powerline/bindings/fish/powerline-setup.fish index 196e9c81..e4dc1ace 100644 --- a/powerline/bindings/fish/powerline.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -1,4 +1,4 @@ -function powerline +function powerline-setup if test -z "$POWERLINE_COMMAND" if which powerline-client >/dev/null set -g -x POWERLINE_COMMAND powerline-client @@ -33,4 +33,3 @@ function powerline _powerline_tmux_set_columns _powerline_tmux_set_pwd end -powerline diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 41f0ca40..dda72ec5 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -2,7 +2,8 @@ set POWERLINE_COMMAND "$PWD/scripts/powerline -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" set VIRTUAL_ENV -. powerline/bindings/fish/powerline.fish ; cd tests/shell/3rd +set fish_function_path $fish_function_path "$PWD/powerline/bindings/fish" +powerline-setup ; cd tests/shell/3rd cd .git cd .. set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" From bb37e349556792bc394e17a746cad4cd241b3523 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Thu, 23 Jan 2014 13:50:30 -0500 Subject: [PATCH 0857/1472] Use hasErrorsOrWarningsToDisplay for Syntastic Reverts PR #759 based on feedback from @lcd047 in scrooloose/syntastic#950 --- powerline/segments/plugin/syntastic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/plugin/syntastic.py b/powerline/segments/plugin/syntastic.py index 97533f68..71a411fa 100644 --- a/powerline/segments/plugin/syntastic.py +++ b/powerline/segments/plugin/syntastic.py @@ -22,7 +22,7 @@ def syntastic(pl, err_format='ERR:  {first_line} ({num}) ', warn_format='WARN ''' if not int(vim.eval('exists("g:SyntasticLoclist")')): return - has_errors = not int(vim.eval('g:SyntasticLoclist.current().isEmpty()')) + has_errors = int(vim.eval('g:SyntasticLoclist.current().hasErrorsOrWarningsToDisplay()')) if not has_errors: return errors = vim.eval('g:SyntasticLoclist.current().errors()') From bf688d458c831c9d181b5517080acfb371108893 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 24 Jan 2014 10:38:18 +0400 Subject: [PATCH 0858/1472] Use nothing on right side of `_powerline_prompt` It should be more robust in case every PROMPT_COMMAND setter cares about previous commands Closes #765 --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 16b295b2..5b2af071 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -40,4 +40,4 @@ trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns [[ "$PROMPT_COMMAND" != "${PROMPT_COMMAND/_powerline_prompt/}" ]] || - export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n'"_powerline_prompt;" + export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n'"_powerline_prompt" From ba41b1f70cddfecc1f83f1842f6906a52441fe3a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Jan 2014 13:08:13 +0400 Subject: [PATCH 0859/1472] Add troubleshooting questions regarding updating --- .../installation/troubleshooting-common.rst | 69 +++++++++++++++++-- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index c5b4b2e2..64d433c1 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -19,6 +19,65 @@ I’m using tmux/screen and Powerline is colorless to ``"tmux"`` or ``"screen"``. Note that it is known to work perfectly in screen, but in tmux it may produce ugly spaces. +After an update something stopped working +----------------------------------------- + +Assuming powerline was working before update and stopped only after there are +two possible explanations: + +* You have more then one powerline installation (e.g. ``pip`` and ``Vundle`` + installations) and you have updated only one. +* Update brought some bug to powerline. + +In the second case you, of course, should report the bug to `powerline bug +tracker `_. In the first you should make +sure you either have only one powerline installation or you update all of them +simultaneously (beware that in the second case you are not supported). To +diagnose this problem you may do the following: + +#) If this problem is observed within the shell make sure that + + .. code-block:: shell + + python -c 'import powerline; print (powerline.__file__)' + + which should report something like + :file:`/usr/lib64/python2.7/site-packages/powerline/__init__.pyc` (if + powerline is installed system-wide) or + :file:`/home/USER/.../powerline/__init__.pyc` (if powerline was cloned + somewhere, e.g. in :file:`/home/USER/.vim/bundle/powerline`) reports the same + location you use to source in your shell configuration: in first case it + should be some location in :file:`/usr` (e.g. + :file:`/usr/share/zsh/site-contrib/powerline.zsh`), in the second it should + be something like + :file:`/home/USER/.../powerline/bindings/zsh/powerline.zsh`. If this is true + it may be a powerline bug, but if locations do not match you should not + report the bug until you observe it on configuration where locations do + match. +#) If this problem is observed within the vim instance you should check out the + output of the following Ex mode commands + + .. code-block:: vim + + python import powerline as pl ; print (pl.__file__) + python3 import powerline as pl ; print (pl.__file__) + + One (but not both) of them will most likely error out, this is OK. The same + rules apply as in the 1), but in place of sourcing you should seek for the + place where you modify `runtimepath` vim option. If you install powerline + using `VAM `_ then no + explicit modifications of runtimpath were performed in your vimrc + (runtimepath is modified by VAM in this case), but powerline will be placed + in :file:`{plugin_root_dir}/powerline` where `{plugin_root_dir}` is stored in + VAM settings dictionary: do `echo g:vim_addon_manager.plugin_root_dir`. + +There is a hint if you want to place powerline repository somewhere, but still +make powerline package importable anywhere: use + + .. code-block:: shell + + pip install --user --editable path/to/powerline + My vim statusline has strange characters like ``^B`` in it! ----------------------------------------------------------- @@ -61,14 +120,14 @@ My vim statusline is not displayed completely and has too much spaces * Alternative: set :ref:`ambiwidth ` to 2, remove fancy dividers (they suck when ``ambiwidth`` is set to double). -When using `z ` powerline shows wrong number of jobs -------------------------------------------------------------------------------- +When using z powerline shows wrong number of jobs +------------------------------------------------- -This happens because `z ` is launching some jobs in +This happens because `z `_ is launching some jobs in the background from ``$POWERLINE_COMMAND`` and these jobs fail to finish before powerline prompt is run. Solution to this problem is simple: be sure that :file:`z.sh` is sourced strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background -jobs are spawned by `z ` after powerline has done its -job. +jobs are spawned by `z `_ after powerline has done +its job. From ecff2813046ad7c8d297beef7d37e68a9517e535 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Dec 2013 15:42:57 +0400 Subject: [PATCH 0860/1472] Handle non-printable characters properly Fixes #723 --- powerline/renderer.py | 29 ++++++++++++++++++++++++----- powerline/renderers/bash_prompt.py | 8 +++++--- powerline/renderers/shell.py | 9 +++++---- powerline/renderers/tmux.py | 6 ++++++ powerline/renderers/vim.py | 15 ++++++++++----- powerline/renderers/zsh_prompt.py | 7 ++++--- 6 files changed, 54 insertions(+), 20 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 2117eeee..0555caef 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -4,12 +4,15 @@ from powerline.theme import Theme from unicodedata import east_asian_width, combining import os - try: NBSP = unicode(' ', 'utf-8') except NameError: NBSP = ' ' +try: + from __builtin__ import unichr as chr +except ImportError: + pass def construct_returned_value(rendered_highlighted, segments, output_raw): if output_raw: @@ -65,6 +68,22 @@ class Renderer(object): python-2) regular string or ``None``. ''' + character_translations = {ord(' '): NBSP} + '''Character translations for use in escape() function. + + See documentation of ``unicode.translate`` for details. + ''' + + np_character_translations = dict(((i, '^' + chr(i + 0x40)) for i in range(0x20))) + '''Non-printable character translations + + These are used to transform characters in range 0x00—0x1F into ``^@``, + ``^A`` and so on. Unilke with ``.escape()`` method (and + ``character_translations``) result is passed to ``.strwidth()`` method. + + Note: transforms tab into ``^I``. + ''' + def __init__(self, theme_config, local_themes, @@ -262,8 +281,8 @@ class Renderer(object): contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding # Replace spaces with no-break spaces - contents_raw = contents_raw.replace(' ', NBSP) divider_raw = divider_raw.replace(' ', NBSP) + contents_raw = contents_raw.translate(self.np_character_translations) # Apply highlighting to padded dividers and contents if render_highlighted: @@ -295,11 +314,11 @@ class Renderer(object): segment['_len'] = self.strwidth(segment['_rendered_raw']) yield segment - @staticmethod - def escape(string): + @classmethod + def escape(cls, string): '''Method that escapes segment contents. ''' - return string + return string.translate(cls.character_translations) def hlstyle(fg=None, bg=None, attr=None): '''Output highlight style string. diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index 01f42f74..58cf36d9 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals + from powerline.renderers.shell import ShellRenderer @@ -8,9 +10,9 @@ class BashPromptRenderer(ShellRenderer): escape_hl_start = '\[' escape_hl_end = '\]' - @staticmethod - def escape(string): - return string.replace('\\', '\\\\').replace('$', '\\$').replace('`', '\\`') + character_translations = ShellRenderer.character_translations + character_translations[ord('$')] = '\\$' + character_translations[ord('`')] = '\\`' renderer = BashPromptRenderer diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 945dc765..e261bb28 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals + from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE @@ -19,6 +21,9 @@ class ShellRenderer(Renderer): tmux_escape = False screen_escape = False + character_translations = Renderer.character_translations + character_translations[ord('\\')] = '\\\\' + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -62,9 +67,5 @@ class ShellRenderer(Renderer): r = '\033P' + r.replace('\033', '\033\033') + '\033\\' return self.escape_hl_start + r + self.escape_hl_end - @staticmethod - def escape(string): - return string.replace('\\', '\\\\') - renderer = ShellRenderer diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 655c1a6b..e8ab17f1 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -1,11 +1,17 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals + from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE class TmuxRenderer(Renderer): '''Powerline tmux segment renderer.''' + + character_translations = Renderer.character_translations + character_translations[ord('#')] = '##[]' + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment.''' # We don't need to explicitly reset attributes, so skip those calls diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 8e8fb0e9..f2e17c32 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals from powerline.bindings.vim import vim_get_func, environ from powerline.renderer import Renderer @@ -11,6 +11,12 @@ import vim import sys +try: + from __builtin__ import unichr as chr +except ImportError: + pass + + vim_mode = vim_get_func('mode', rettype=str) mode_translations = { chr(ord('V') - 0x40): '^V', @@ -21,6 +27,9 @@ mode_translations = { class VimRenderer(Renderer): '''Powerline vim segment renderer.''' + character_translations = Renderer.character_translations + character_translations[ord('%')] = '%%' + def __init__(self, *args, **kwargs): if not hasattr(vim, 'strwidth'): # Hope nobody want to change this at runtime @@ -97,10 +106,6 @@ class VimRenderer(Renderer): def reset_highlight(self): self.hl_groups.clear() - @staticmethod - def escape(string): - return string.replace('%', '%%') - def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 2e6e5e77..e30ac061 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals + from powerline.renderers.shell import ShellRenderer @@ -8,9 +10,8 @@ class ZshPromptRenderer(ShellRenderer): escape_hl_start = '%{' escape_hl_end = '%}' - @staticmethod - def escape(string): - return string.replace('%', '%%').replace('\\', '\\\\') + character_translations = ShellRenderer.character_translations + character_translations[ord('%')] = '%%' renderer = ZshPromptRenderer From 693ba8a55911033053875302c0fd62ca49372695 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Dec 2013 15:46:13 +0400 Subject: [PATCH 0861/1472] Use .copy() on character_translations before modifying --- powerline/renderers/bash_prompt.py | 2 +- powerline/renderers/shell.py | 2 +- powerline/renderers/tmux.py | 2 +- powerline/renderers/vim.py | 2 +- powerline/renderers/zsh_prompt.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index 58cf36d9..d3ac3f26 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -10,7 +10,7 @@ class BashPromptRenderer(ShellRenderer): escape_hl_start = '\[' escape_hl_end = '\]' - character_translations = ShellRenderer.character_translations + character_translations = ShellRenderer.character_translations.copy() character_translations[ord('$')] = '\\$' character_translations[ord('`')] = '\\`' diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index e261bb28..c7c57451 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -21,7 +21,7 @@ class ShellRenderer(Renderer): tmux_escape = False screen_escape = False - character_translations = Renderer.character_translations + character_translations = Renderer.character_translations.copy() character_translations[ord('\\')] = '\\\\' def hlstyle(self, fg=None, bg=None, attr=None): diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index e8ab17f1..c237ae1a 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -9,7 +9,7 @@ from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE class TmuxRenderer(Renderer): '''Powerline tmux segment renderer.''' - character_translations = Renderer.character_translations + character_translations = Renderer.character_translations.copy() character_translations[ord('#')] = '##[]' def hlstyle(self, fg=None, bg=None, attr=None): diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index f2e17c32..8ee1bf0b 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -27,7 +27,7 @@ mode_translations = { class VimRenderer(Renderer): '''Powerline vim segment renderer.''' - character_translations = Renderer.character_translations + character_translations = Renderer.character_translations.copy() character_translations[ord('%')] = '%%' def __init__(self, *args, **kwargs): diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index e30ac061..47e76cc3 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -10,7 +10,7 @@ class ZshPromptRenderer(ShellRenderer): escape_hl_start = '%{' escape_hl_end = '%}' - character_translations = ShellRenderer.character_translations + character_translations = ShellRenderer.character_translations.copy() character_translations[ord('%')] = '%%' From e7820efe5669eab117027e5b5d0a798908c65cf7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Dec 2013 19:25:19 +0400 Subject: [PATCH 0862/1472] Make powerline work with non-utf filenames Fixes #281 --- powerline/bindings/vim/__init__.py | 31 +++++++++++++++++++ powerline/segments/common.py | 2 +- powerline/segments/vim.py | 24 +++++++++------ tests/test_segments.py | 8 +++++ tests/vim.py | 49 +++++++++++++++++++++++++++--- 5 files changed, 98 insertions(+), 16 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index ef8643ff..32736fdf 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet import sys +import codecs try: import vim @@ -113,4 +114,34 @@ class VimEnviron(object): + value.replace('"', '\\"').replace('\\', '\\\\').replace('\n', '\\n').replace('\0', '') + '"') + +if sys.version_info < (3,): + def buffer_name(buf): + return buf.name +else: + vim_bufname = vim_get_func('bufname') + def buffer_name(buf): # NOQA + try: + name = buf.name + except UnicodeDecodeError: + return vim_bufname(buf.number) + else: + return name.encode('utf-8') if name else None + + +vim_strtrans = vim_get_func('strtrans') + + +def powerline_vim_strtrans_error(e): + if not isinstance(e, UnicodeDecodeError): + raise NotImplementedError + # Assuming &encoding is utf-8 strtrans should not return anything but ASCII + # under current circumstances + text = vim_strtrans(e.object[e.start:e.end]).decode() + return (text, e.end) + + +codecs.register_error('powerline_vim_strtrans_error', powerline_vim_strtrans_error) + + environ = VimEnviron() diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 4d4978bd..41e75b77 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import unicode_literals, absolute_import import os import sys diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 07da7e37..47441fe9 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, division +from __future__ import unicode_literals, absolute_import, division import os try: @@ -8,7 +8,8 @@ try: except ImportError: vim = {} # NOQA -from powerline.bindings.vim import vim_get_func, getbufvar, vim_getbufoption +from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, + buffer_name) from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -19,8 +20,8 @@ from collections import defaultdict vim_funcs = { 'virtcol': vim_get_func('virtcol', rettype=int), 'getpos': vim_get_func('getpos'), - 'fnamemodify': vim_get_func('fnamemodify', rettype=str), - 'expand': vim_get_func('expand', rettype=str), + 'fnamemodify': vim_get_func('fnamemodify'), + 'expand': vim_get_func('expand'), 'bufnr': vim_get_func('bufnr', rettype=int), 'line2byte': vim_get_func('line2byte', rettype=int), 'line': vim_get_func('line', rettype=int), @@ -157,14 +158,18 @@ def file_directory(pl, segment_info, shorten_user=True, shorten_cwd=True, shorte :param bool shorten_home: shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. ''' - name = segment_info['buffer'].name + name = buffer_name(segment_info['buffer']) if not name: return None + import sys file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') + (':.' if shorten_cwd else '') + ':h') + if not file_directory: + return None if shorten_home and file_directory.startswith('/home/'): - file_directory = '~' + file_directory[6:] - return file_directory + os.sep if file_directory else None + file_directory = b'~' + file_directory[6:] + file_directory = file_directory.decode('utf-8', 'powerline_vim_strtrans_error') + return file_directory + os.sep @requires_segment_info @@ -178,7 +183,7 @@ def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]') Highlight groups used: ``file_name_no_file`` or ``file_name``, ``file_name``. ''' - name = segment_info['buffer'].name + name = buffer_name(segment_info['buffer']) if not name: if display_no_file: return [{ @@ -187,8 +192,7 @@ def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]') }] else: return None - file_name = vim_funcs['fnamemodify'](name, ':~:.:t') - return file_name + return os.path.basename(name).decode('utf-8', 'powerline_vim_strtrans_error') @window_cached diff --git a/tests/test_segments.py b/tests/test_segments.py index a3249ac1..789d5089 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +from __future__ import unicode_literals + from powerline.segments import shell, common import tests.vim as vim_module import sys @@ -467,6 +469,10 @@ class TestVim(TestCase): segment_info = vim_module._get_segment_info() self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), None) with replace_env('HOME', '/home/foo', os.environ): + with vim_module._with('buffer', '/tmp/’’/abc') as segment_info: + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/’’/') + with vim_module._with('buffer', b'/tmp/\xFF\xFF/abc') as segment_info: + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp//') with vim_module._with('buffer', '/tmp/abc') as segment_info: self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/') os.environ['HOME'] = '/tmp' @@ -484,6 +490,8 @@ class TestVim(TestCase): self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), 'abc') with vim_module._with('buffer', '/tmp/’’') as segment_info: self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), '’’') + with vim_module._with('buffer', b'/tmp/\xFF\xFF') as segment_info: + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), '') def test_file_size(self): pl = Pl() diff --git a/tests/vim.py b/tests/vim.py index 3cde286f..56efbbb1 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -60,7 +60,7 @@ class _Buffers(object): @_vim def __nonzero__(self): - return not not self.d + return bool(self.d) @_vim def keys(self): @@ -261,8 +261,8 @@ def _emul_getpos(expr): def _emul_fnamemodify(path, modstring): import os _modifiers = { - '~': lambda path: path.replace(os.environ['HOME'], '~') if path.startswith(os.environ['HOME']) else path, - '.': lambda path: (lambda tpath: path if tpath[:3] == '..' + os.sep else tpath)(os.path.relpath(path)), + '~': lambda path: path.replace(os.environ['HOME'].encode('utf-8'), b'~') if path.startswith(os.environ['HOME'].encode('utf-8')) else path, + '.': lambda path: (lambda tpath: path if tpath[:3] == b'..' + os.sep.encode() else tpath)(os.path.relpath(path)), 't': lambda path: os.path.basename(path), 'h': lambda path: os.path.dirname(path), } @@ -313,6 +313,22 @@ def _emul_line(expr): raise NotImplementedError +@_vim +@_str_func +def _emul_strtrans(s): + # FIXME Do more replaces + return s.replace(b'\xFF', b'') + + +@_vim +@_str_func +def _emul_bufname(bufnr): + try: + return buffers[bufnr]._name or b'' + except KeyError: + return b'' + + _window_ids = [None] _window_id = 0 @@ -348,11 +364,11 @@ _undo_written = {} class _Buffer(object): def __init__(self, name=None): global _last_bufnr - import os _last_bufnr += 1 bufnr = _last_bufnr self.number = bufnr - self.name = os.path.abspath(name) if name else None + # FIXME Use unicode() for python-3 + self.name = name self.vars = {} self.options = { 'modified': 0, @@ -369,6 +385,25 @@ class _Buffer(object): _undo_written[bufnr] = len(_undostate[bufnr]) buffers[bufnr] = self + @property + def name(self): + import sys + if sys.version_info < (3,): + return self._name + else: + return str(self._name, 'utf-8') if self._name else None + + @name.setter + def name(self, name): + if name is None: + self._name = None + else: + import os + if type(name) is not bytes: + name = name.encode('utf-8') + import sys + self._name = os.path.abspath(name) + def __getitem__(self, line): return _buf_lines[self.number][line] @@ -676,3 +711,7 @@ def _with(key, *args, **kwargs): return _WithDict(vars, **kwargs) elif key == 'split': return _WithSplit() + + +class error(Exception): + pass From e3bf63e55c34177569dd46404662232585cb36c1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 21:30:01 +0400 Subject: [PATCH 0863/1472] Do not do needless copying in test_shells/test.sh --- tests/test_shells/postproc.py | 10 ++++------ tests/test_shells/screenrc | 2 +- tests/test_shells/test.sh | 17 ++++++++--------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 1654bc7c..9696b5b9 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -8,11 +8,11 @@ import sys import codecs -fname = sys.argv[1] -new_fname = fname + '.new' -pid_fname = 'tests/shell/3rd/pid' +shell = sys.argv[1] +fname = os.path.join('tests', 'shell', shell + '.full.log') +new_fname = os.path.join('tests', 'shell', shell + '.log') +pid_fname = os.path.join('tests', 'shell', '3rd', 'pid') -shell = sys.argv[2] with open(pid_fname, 'r') as P: pid = P.read().strip() @@ -42,5 +42,3 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: except ValueError: line = '' W.write(line) - -os.rename(new_fname, fname) diff --git a/tests/test_shells/screenrc b/tests/test_shells/screenrc index ad5a1466..d998652a 100644 --- a/tests/test_shells/screenrc +++ b/tests/test_shells/screenrc @@ -1,3 +1,3 @@ width 1024 height 1 -logfile "tests/shell/screen.log" +logfile "tests/shell/${SH}.full.log" diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 3d8b8cc2..a53d507f 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -3,11 +3,12 @@ FAILED=0 ONLY_SHELL="$1" check_screen_log() { - if test -e tests/test_shells/${1}.ok ; then - diff -u tests/test_shells/${1}.ok tests/shell/screen.log + SH="$1" + if test -e tests/test_shells/${SH}.ok ; then + diff -u tests/test_shells/${SH}.ok tests/shell/${SH}.log return $? else - cat tests/shell/screen.log + cat tests/shell/${SH}.log return 1 fi } @@ -20,22 +21,21 @@ run_test() { which "${SH}" || return 0 + export SH + screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH # Wait for screen to initialize sleep 1s screen -S "$SESNAME" -p 0 -X width 300 1 - screen -S "$SESNAME" -p 0 -X logfile tests/shell/screen.log screen -S "$SESNAME" -p 0 -X paste a # Wait for screen to exit (sending command to non-existing screen session # fails; when launched instance exits corresponding session is deleted) while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do sleep 0.1s done - cp tests/shell/screen.log tests/shell/${SH}.full.log - ./tests/test_shells/postproc.py tests/shell/screen.log ${SH} - cp tests/shell/screen.log tests/shell/${SH}.log + ./tests/test_shells/postproc.py ${SH} if ! check_screen_log ${SH} ; then echo '____________________________________________________________' # Repeat the diff to make it better viewable in travis output @@ -52,10 +52,8 @@ run_test() { cat -v tests/shell/${SH}.full.log echo '____________________________________________________________' ${SH} --version - rm tests/shell/screen.log return 1 fi - rm tests/shell/screen.log return 0 } @@ -63,6 +61,7 @@ test -d tests/shell && rm -r tests/shell mkdir tests/shell git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH +mkdir tests/shell/3rd/"$(printf '\x08')" if ! run_test bash --norc --noprofile -i ; then FAILED=1 From 9edee8ad118d310df32f80b22fe9449a1cc127bf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 21:44:32 +0400 Subject: [PATCH 0864/1472] Remove backslash escaping on zsh and fish --- powerline/renderers/bash_prompt.py | 1 + powerline/renderers/shell.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index d3ac3f26..c37a2cf9 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -13,6 +13,7 @@ class BashPromptRenderer(ShellRenderer): character_translations = ShellRenderer.character_translations.copy() character_translations[ord('$')] = '\\$' character_translations[ord('`')] = '\\`' + character_translations[ord('\\')] = '\\\\' renderer = BashPromptRenderer diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index c7c57451..5358871c 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -22,7 +22,6 @@ class ShellRenderer(Renderer): screen_escape = False character_translations = Renderer.character_translations.copy() - character_translations[ord('\\')] = '\\\\' def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. From 480434331fcf053010ae8d9fc908e6650736b737 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 21:45:02 +0400 Subject: [PATCH 0865/1472] Add tests for special characters escaping --- tests/test_shells/bash.ok | 10 +++++++++- tests/test_shells/fish.ok | 8 ++++++++ tests/test_shells/input.bash | 8 ++++++++ tests/test_shells/input.fish | 8 ++++++++ tests/test_shells/input.zsh | 8 ++++++++ tests/test_shells/test.sh | 7 +++++++ tests/test_shells/zsh.ok | 10 +++++++++- 7 files changed, 57 insertions(+), 2 deletions(-) diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index a88c8091..167f6b2c 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -7,4 +7,12 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd $(printf '\x1b[32m') +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../$(printf '\x08') +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/fish.ok b/tests/test_shells/fish.ok index 9702a1c0..94c4e3fc 100644 --- a/tests/test_shells/fish.ok +++ b/tests/test_shells/fish.ok @@ -6,3 +6,11 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1     HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1     HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`   diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 954d6e1a..429b4e19 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -10,6 +10,14 @@ VIRTUAL_ENV= bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s +cd $(printf '\x1b[32m') +cd ../$(printf '\x08') +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' false true is the last line exit diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index dda72ec5..549343d6 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -11,6 +11,14 @@ set VIRTUAL_ENV bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill (cat pid) ; sleep 1s +cd (printf '\x1b[32m') +cd ../(printf '\x08') +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' false true is the last line exit diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index bcd1a421..744dea32 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -11,6 +11,14 @@ VIRTUAL_ENV= bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s +cd $(printf '\x1b[32m') +cd ../$(printf '\x08') +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' false true is the last line exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index a53d507f..dca958e3 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -61,7 +61,14 @@ test -d tests/shell && rm -r tests/shell mkdir tests/shell git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH +mkdir tests/shell/3rd/"$(printf '\x1b[32m')" mkdir tests/shell/3rd/"$(printf '\x08')" +mkdir tests/shell/3rd/'\[\]' +mkdir tests/shell/3rd/'%%' +mkdir tests/shell/3rd/'#[bold]' +mkdir tests/shell/3rd/'(echo)' +mkdir tests/shell/3rd/'$(echo)' +mkdir tests/shell/3rd/'`echo`' if ! run_test bash --norc --noprofile -i ; then FAILED=1 diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index 47eda0f2..ba98eb73 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -8,4 +8,12 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd $(printf '\x1b[32m') +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../$(printf '\x08') +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false From a571f49b3d0fa57561b27c517bf47987f7f5958c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 21:59:33 +0400 Subject: [PATCH 0866/1472] Get rid of printf --- tests/test_shells/bash.ok | 4 ++-- tests/test_shells/input.bash | 4 ++-- tests/test_shells/input.fish | 4 ++-- tests/test_shells/input.zsh | 4 ++-- tests/test_shells/test.sh | 6 ++++-- tests/test_shells/zsh.ok | 4 ++-- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index 167f6b2c..b5026d25 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -7,8 +7,8 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd $(printf '\x1b[32m') -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../$(printf '\x08') +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 429b4e19..e9f0d8ab 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -10,8 +10,8 @@ VIRTUAL_ENV= bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s -cd $(printf '\x1b[32m') -cd ../$(printf '\x08') +cd "$DIR1" +cd ../"$DIR2" cd ../'\[\]' cd ../'%%' cd ../'#[bold]' diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 549343d6..f6dcc4fc 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -11,8 +11,8 @@ set VIRTUAL_ENV bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill (cat pid) ; sleep 1s -cd (printf '\x1b[32m') -cd ../(printf '\x08') +cd "$DIR1" +cd ../"$DIR2" cd ../'\[\]' cd ../'%%' cd ../'#[bold]' diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 744dea32..b3b3ba59 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -11,8 +11,8 @@ VIRTUAL_ENV= bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & false kill `cat pid` ; sleep 1s -cd $(printf '\x1b[32m') -cd ../$(printf '\x08') +cd "$DIR1" +cd ../"$DIR2" cd ../'\[\]' cd ../'%%' cd ../'#[bold]' diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index dca958e3..c38d13c4 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -61,8 +61,10 @@ test -d tests/shell && rm -r tests/shell mkdir tests/shell git init tests/shell/3rd git --git-dir=tests/shell/3rd/.git checkout -b BRANCH -mkdir tests/shell/3rd/"$(printf '\x1b[32m')" -mkdir tests/shell/3rd/"$(printf '\x08')" +export DIR1="" +export DIR2="" +mkdir tests/shell/3rd/"$DIR1" +mkdir tests/shell/3rd/"$DIR2" mkdir tests/shell/3rd/'\[\]' mkdir tests/shell/3rd/'%%' mkdir tests/shell/3rd/'#[bold]' diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index ba98eb73..9ebcfb60 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -8,8 +8,8 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd $(printf '\x1b[32m') -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../$(printf '\x08') +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' From becc8ee59c3be3ab1165f963d13239af186d2fda Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Jun 2013 23:48:05 +0400 Subject: [PATCH 0867/1472] =?UTF-8?q?Support=20zsh=20=E2=80=9Cmodes?= =?UTF-8?q?=E2=80=9D=20(different=20keymaps=20like=20when=20using=20vicmd/?= =?UTF-8?q?viins=20pair)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #508 --- powerline/bindings/zsh/powerline.zsh | 41 +++++++++++++++++++ .../colorschemes/shell/default.json | 10 ++++- .../colorschemes/shell/solarized.json | 10 ++++- .../config_files/themes/shell/default.json | 4 ++ powerline/lint/__init__.py | 26 ++++++++++-- powerline/renderer.py | 5 ++- powerline/renderers/ipython.py | 2 +- powerline/renderers/tmux.py | 3 +- powerline/renderers/vim.py | 2 +- powerline/segments/shell.py | 28 +++++++++++++ scripts/powerline | 1 + 11 files changed, 122 insertions(+), 10 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 917f5a8b..34d0cfc6 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -59,6 +59,47 @@ _powerline_setup_prompt() { fi } +_powerline_add_widget() { + local widget="$1" + local function="$2" + local old_widget_command="$(zle -l -L $widget)" + if [[ "$old_widget_command" = "zle -N $widget $function" ]] ; then + return 0 + elif [[ -z "$old_widget_command" ]] ; then + zle -N $widget $function + else + local save_widget="powerline_save_$widget" + local -i i=0 + while ! test -z "$(zle -l -L $save_widget)" ; do + save_widget="${save_widget}_$i" + (( i++ )) + done + eval "${old_widget_command/$widget/$save_widget}" + zle -N $widget $function + export POWERLINE_SAVE_WIDGET="$save_widget" + fi +} + +_powerline_zle_keymap_select() { + export POWERLINE_MODE="${KEYMAP}" + zle reset-prompt + test -z "$POWERLINE_SAVE_WIDGET" || zle $POWERLINE_SAVE_WIDGET +} + +_powerline_set_mode() { + local keymap + if test -z "$(bindkey -lL main)" ; then + keymap=".safe" + else + keymap="main" + fi + export POWERLINE_MODE="${keymap}" +} + +_powerline_set_mode + +_powerline_add_widget zle-keymap-select _powerline_zle_keymap_select + trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns _powerline_tmux_set_pwd diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 61fecdad..d4f5a6f2 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -14,6 +14,14 @@ "hostname": { "fg": "brightyellow", "bg": "mediumorange" }, "exit_fail": { "fg": "white", "bg": "darkestred" }, "exit_success": { "fg": "white", "bg": "darkestgreen" }, - "environment": { "fg": "white", "bg": "darkestgreen" } + "environment": { "fg": "white", "bg": "darkestgreen" }, + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } + }, + "mode_translations": { + "vicmd": { + "groups": { + "mode": {"fg": "darkestcyan", "bg": "white", "attr": ["bold"]} + } + } } } diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index 121dd16b..e87abe8e 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -14,6 +14,14 @@ "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, "exit_fail": { "fg": "oldlace", "bg": "red" }, "exit_success": { "fg": "oldlace", "bg": "green" }, - "environment": { "fg": "oldlace", "bg": "green" } + "environment": { "fg": "oldlace", "bg": "green" }, + "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } + }, + "mode_translations": { + "vicmd": { + "groups": { + "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] } + } + } } } diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 89dfa33a..35e8e18b 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -16,6 +16,10 @@ }, "segments": { "left": [ + { + "module": "powerline.segments.shell", + "name": "mode" + }, { "name": "hostname" }, diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 9d7a009b..6d778234 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -535,15 +535,15 @@ group_name_spec = Spec().re('^\w+(?::\w+)?$').copy groups_spec = Spec().unknown_spec( group_name_spec(), group_spec(), -).copy +).context_message('Error while loading groups (key {key})').copy colorscheme_spec = (Spec( name=name_spec(), - groups=groups_spec().context_message('Error while loading groups (key {key})'), + groups=groups_spec(), ).context_message('Error while loading coloscheme')) vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc'])).copy vim_colorscheme_spec = (Spec( name=name_spec(), - groups=groups_spec().context_message('Error while loading groups (key {key})'), + groups=groups_spec(), mode_translations=Spec().unknown_spec( vim_mode_spec(), Spec( @@ -558,6 +558,24 @@ vim_colorscheme_spec = (Spec( ), ).context_message('Error while loading mode translations (key {key})'), ).context_message('Error while loading vim colorscheme')) +shell_mode_spec = Spec().re('^(?:[\w\-]+|\.safe)$').copy +shell_colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec(), + mode_translations=Spec().unknown_spec( + shell_mode_spec(), + Spec( + colors=Spec().unknown_spec( + color_spec(), + color_spec(), + ).optional(), + groups=Spec().unknown_spec( + group_name_spec().func(check_translated_group_name), + group_spec(), + ).optional(), + ), + ).context_message('Error while loading mode translations (key {key})'), +).context_message('Error while loading shell colorscheme')) generic_keys = set(('exclude_modes', 'include_modes', 'width', 'align', 'name', 'draw_soft_divider', 'draw_hard_divider', 'priority', 'after', 'before')) @@ -1106,6 +1124,8 @@ def check(path=None, debug=False): colorscheme_configs[ext][colorscheme] = config if ext == 'vim': spec = vim_colorscheme_spec + elif ext == 'shell': + spec = shell_colorscheme_spec else: spec = colorscheme_spec if spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: diff --git a/powerline/renderer.py b/powerline/renderer.py index 0555caef..256d11f0 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -150,7 +150,7 @@ class Renderer(object): segment['divider_highlight'] = None return segment - def get_segment_info(self, segment_info): + def get_segment_info(self, segment_info, mode): '''Get segment information. Must return a dictionary containing at least ``home``, ``environ`` and @@ -167,6 +167,7 @@ class Renderer(object): :return: dict with segment information. ''' r = self.segment_info.copy() + r['mode'] = mode if segment_info: r.update(segment_info) if 'PWD' in r['environ']: @@ -201,7 +202,7 @@ class Renderer(object): Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side, self.get_segment_info(segment_info)) + segments = theme.get_segments(side, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode segments = [self._get_highlighting(segment, mode) for segment in segments diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index f5c2b24b..8de06dfe 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -9,7 +9,7 @@ class IpythonRenderer(ShellRenderer): escape_hl_start = '\x01' escape_hl_end = '\x02' - def get_segment_info(self, segment_info): + def get_segment_info(self, segment_info, mode): r = self.segment_info.copy() r['ipython'] = segment_info return r diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index c237ae1a..5daccbbb 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -46,7 +46,7 @@ class TmuxRenderer(Renderer): tmux_attr += ['nounderscore'] return '#[' + ','.join(tmux_attr) + ']' - def get_segment_info(self, segment_info): + def get_segment_info(self, segment_info, mode): r = self.segment_info.copy() if segment_info: r.update(segment_info) @@ -54,6 +54,7 @@ class TmuxRenderer(Renderer): varname = 'TMUX_PWD_' + r['pane_id'].lstrip('%') if varname in r['environ']: r['getcwd'] = lambda: r['environ'][varname] + r['mode'] = mode return r diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 8ee1bf0b..6065dbbf 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -74,7 +74,7 @@ class VimRenderer(Renderer): def strwidth(string): return vim.strwidth(string) - def get_segment_info(self, segment_info): + def get_segment_info(self, segment_info, mode): return segment_info or self.segment_info def render(self, window, window_id, winnr): diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 07212059..962a1a26 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -41,3 +41,31 @@ def last_pipe_status(pl, segment_info): for status in last_pipe_status] else: return None + + +@requires_segment_info +def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, default='main'): + '''Return the current mode. + + :param dict override: + dict for overriding mode strings. + :param str default: + If current mode is equal to this string then this segment will not get + displayed. + ''' + mode = segment_info['mode'] + if not mode: + pl.warn('No or empty POWERLINE_MODE variable') + return None + if mode == default: + return None + try: + return override[mode] + except KeyError: + # Note: with zsh line editor you can emulate as much modes as you wish. + # Thus having unknown mode is not an error: maybe just some developer + # added support for his own zle widgets. As there is no built-in mode() + # function like in VimL and POWERLINE_MODE is likely be defined by our + # code or by somebody knowing what he is doing there is absolutely no + # need in keeping translations dictionary. + return mode.upper() diff --git a/scripts/powerline b/scripts/powerline index e6517c7f..3192b4af 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -22,6 +22,7 @@ if __name__ == '__main__': width=args.width, side=args.side, segment_info=segment_info, + mode=os.environ.get('POWERLINE_MODE'), ) try: sys.stdout.write(rendered) From 34d4877abff71f09484938212ed38deab1fe4863 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Nov 2013 00:12:40 +0400 Subject: [PATCH 0868/1472] Some fixes for powerline.zsh: - Replace main and other linked modes with modes they link to - Add proper keymap initialization Ref #508 --- powerline/bindings/zsh/powerline.zsh | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 34d0cfc6..ec4ebab8 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -36,6 +36,7 @@ _powerline_precmd() { # wrong number of jobs. You need to filter the lines first. Or not use # jobs built-in at all. _POWERLINE_JOBNUM=${(%):-%j} + _powerline_set_true_keymap_name "${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" } _powerline_setup_prompt() { @@ -47,6 +48,7 @@ _powerline_setup_prompt() { done precmd_functions+=( _powerline_precmd ) chpwd_functions+=( _powerline_tmux_set_pwd ) + _powerline_set_true_keymap_name "${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" if zmodload zsh/zpython &>/dev/null ; then zpython 'from powerline.bindings.zsh import setup as powerline_setup' zpython 'powerline_setup()' @@ -80,25 +82,22 @@ _powerline_add_widget() { fi } +_powerline_set_true_keymap_name() { + export POWERLINE_MODE="${1}" + local plm_bk="$(bindkey -lL ${POWERLINE_MODE})" + if [[ $plm_bk = 'bindkey -A'* ]] ; then + _powerline_set_true_keymap_name ${(Q)${${(z)plm_bk}[3]}} + fi +} + _powerline_zle_keymap_select() { - export POWERLINE_MODE="${KEYMAP}" + _powerline_set_true_keymap_name $KEYMAP zle reset-prompt test -z "$POWERLINE_SAVE_WIDGET" || zle $POWERLINE_SAVE_WIDGET } -_powerline_set_mode() { - local keymap - if test -z "$(bindkey -lL main)" ; then - keymap=".safe" - else - keymap="main" - fi - export POWERLINE_MODE="${keymap}" -} - -_powerline_set_mode - _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select +_powerline_precmd trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns From 36c007058d418fdbcd530df30193a1ec1750ee00 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Nov 2013 01:36:16 +0400 Subject: [PATCH 0869/1472] Omit any non-vi* mode by default --- powerline/bindings/zsh/powerline.zsh | 4 ++++ powerline/segments/shell.py | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index ec4ebab8..6b55f1c3 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -99,6 +99,10 @@ _powerline_zle_keymap_select() { _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select _powerline_precmd +if [[ "$POWERLINE_MODE" != vi* ]] ; then + export POWERLINE_DEFAULT_MODE="$POWERLINE_MODE" +fi + trap "_powerline_tmux_set_columns" SIGWINCH _powerline_tmux_set_columns _powerline_tmux_set_pwd diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 962a1a26..1245d109 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -44,19 +44,22 @@ def last_pipe_status(pl, segment_info): @requires_segment_info -def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, default='main'): +def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, default=None): '''Return the current mode. :param dict override: dict for overriding mode strings. :param str default: If current mode is equal to this string then this segment will not get - displayed. + displayed. If not specified the value is taken from + ``$POWERLINE_DEFAULT_MODE`` variable. This variable is set by zsh + bindings for any mode that does not start from ``vi``. ''' mode = segment_info['mode'] if not mode: pl.warn('No or empty POWERLINE_MODE variable') return None + default = default or segment_info['environ'].get('POWERLINE_DEFAULT_MODE') if mode == default: return None try: From 8984647106c928f055c227b1c3c5b255653404e6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 22:26:29 +0400 Subject: [PATCH 0870/1472] Add tests --- tests/test_shells/input.zsh | 5 +++++ tests/test_shells/zsh.ok | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index b3b3ba59..20383310 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -19,6 +19,11 @@ cd ../'#[bold]' cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' +cd .. +POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,5] ) ; bindkey -v + + +echo abc false true is the last line exit diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index 9ebcfb60..bde41125 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -16,4 +16,10 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,5] ) ; bindkey -v + INSERT  USER  ⋯  tests  shell  3rd   COMMND  USER  ⋯  tests  shell  3rd   + INSERT  USER  ⋯  tests  shell  3rd   + INSERT  USER  ⋯  tests  shell  3rd  echo abc +abc + INSERT  USER  ⋯  tests  shell  3rd  false From 6ba13c1d0f725521b6e8c42d6c5d6bb08ebb0271 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 22:35:02 +0400 Subject: [PATCH 0871/1472] Modify appropriate theme --- tests/test_shells/input.zsh | 2 +- tests/test_shells/zsh.ok | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 20383310..8cd28d54 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -20,7 +20,7 @@ cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' cd .. -POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,5] ) ; bindkey -v +POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,4] ${${POWERLINE_COMMAND[5]}/_leftonly} ) ; bindkey -v  echo abc diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index bde41125..dbc5e17b 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -17,9 +17,9 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,5] ) ; bindkey -v - INSERT  USER  ⋯  tests  shell  3rd   COMMND  USER  ⋯  tests  shell  3rd   - INSERT  USER  ⋯  tests  shell  3rd   - INSERT  USER  ⋯  tests  shell  3rd  echo abc +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,4] ${${POWERLINE_COMMAND[5]}/_leftonly} ) ; bindkey -v + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd   COMMND   HOSTNAME  USER  ⋯  tests  shell  3rd   + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd   + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  echo abc abc - INSERT  USER  ⋯  tests  shell  3rd  false + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  false From bb343765b0a04493572e61426c52311d30f035bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 22:45:11 +0400 Subject: [PATCH 0872/1472] Add powerline: exception prefix --- tests/test_config_reload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index b58eaff8..a98bfd1c 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -141,7 +141,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertAccessEvents('config', 'themes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: themes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent']) config['config']['ext']['test']['theme'] = 'default' add_watcher_events(p, 'config') @@ -154,7 +154,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertAccessEvents('config', 'colorschemes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: colorschemes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent']) config['config']['ext']['test']['colorscheme'] = '2' add_watcher_events(p, 'config') @@ -187,7 +187,7 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'config') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents('config') - self.assertIn('exception:test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) + self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) config['colorschemes/test/nonexistentraise'] = { 'groups': { From 3ecbeb8c5b9b555f320001c162b9975d8a86acad Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Jan 2014 21:44:11 +0400 Subject: [PATCH 0873/1472] Add powerline.tcsh Ref #762 --- powerline/bindings/tcsh/powerline.tcsh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 powerline/bindings/tcsh/powerline.tcsh diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh new file mode 100644 index 00000000..aa81d011 --- /dev/null +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -0,0 +1,21 @@ +# http://unix.stackexchange.com/questions/4650/determining-path-to-sourced-shell-script: +# > In tcsh, $_ at the beginning of the script will contain the location if the +# > file was sourced and $0 contains it if it was run. +# +# Guess this relies on `$_` being set as to last argument to previous command +# which must be `.` or `source` in this case +set POWERLINE_SOURCED=($_) +if ! $?POWERLINE_COMMAND then + if ( { which powerline-client > /dev/null } ) then + setenv POWERLINE_COMMAND powerline-client + else if ( { which powerline > /dev/null } ) then + setenv POWERLINE_COMMAND powerline + else + setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline + endif +endif +alias _powerline_tmux_set_pwd 'if $?TMUX tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if $?TMUX tmux refresh -S' +alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r zsh_prompt --last_exit_code=$?`"' +alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r zsh_prompt --last_exit_code=$?`"' +alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" +alias precmd "`alias precmd` ; _powerline_set_prompt ; _powerline_set_rprompt" From a2e11ef94f3662cc123721987bb823253b082066 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 23:39:25 +0400 Subject: [PATCH 0874/1472] Add space to rprompt According to tcsh documentation `%{%}` must not be the last segment. Note: this means that right prompt has *two* characters between its end and terminal emulator border. --- powerline/bindings/tcsh/powerline.tcsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index aa81d011..d60b46d1 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -16,6 +16,6 @@ if ! $?POWERLINE_COMMAND then endif alias _powerline_tmux_set_pwd 'if $?TMUX tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if $?TMUX tmux refresh -S' alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r zsh_prompt --last_exit_code=$?`"' -alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r zsh_prompt --last_exit_code=$?`"' +alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r zsh_prompt --last_exit_code=$?` "' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" alias precmd "`alias precmd` ; _powerline_set_prompt ; _powerline_set_rprompt" From 219b81d23daebee389faf78cea002311420975aa Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 27 Jan 2014 00:00:25 +0400 Subject: [PATCH 0875/1472] Fix tcsh escaping --- powerline/bindings/tcsh/powerline.tcsh | 4 ++-- powerline/renderers/tcsh_prompt.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 powerline/renderers/tcsh_prompt.py diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index d60b46d1..48566318 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -15,7 +15,7 @@ if ! $?POWERLINE_COMMAND then endif endif alias _powerline_tmux_set_pwd 'if $?TMUX tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if $?TMUX tmux refresh -S' -alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r zsh_prompt --last_exit_code=$?`"' -alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r zsh_prompt --last_exit_code=$?` "' +alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' +alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" alias precmd "`alias precmd` ; _powerline_set_prompt ; _powerline_set_rprompt" diff --git a/powerline/renderers/tcsh_prompt.py b/powerline/renderers/tcsh_prompt.py new file mode 100644 index 00000000..adbb91c9 --- /dev/null +++ b/powerline/renderers/tcsh_prompt.py @@ -0,0 +1,16 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import, unicode_literals + +from powerline.renderers.zsh_prompt import ZshPromptRenderer + + +class TcshPromptRenderer(ZshPromptRenderer): + '''Powerline tcsh prompt segment renderer.''' + character_translations = ZshPromptRenderer.character_translations.copy() + character_translations[ord('%')] = '%%' + character_translations[ord('\\')] = '\\\\' + character_translations[ord('^')] = '\\^' + + +renderer = TcshPromptRenderer From fe99e252e9d9d2116f22ad148c6aadbfc01d2880 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 23:52:06 +0400 Subject: [PATCH 0876/1472] Add tcsh tests --- tests/install.sh | 2 +- tests/test_shells/bash.ok | 4 ++-- tests/test_shells/input.bash | 2 +- tests/test_shells/input.fish | 2 +- tests/test_shells/input.tcsh | 21 +++++++++++++++++++++ tests/test_shells/input.zsh | 2 +- tests/test_shells/tcsh.ok | 19 +++++++++++++++++++ tests/test_shells/test.sh | 4 ++++ tests/test_shells/zsh.ok | 4 ++-- 9 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 tests/test_shells/input.tcsh create mode 100644 tests/test_shells/tcsh.ok diff --git a/tests/install.sh b/tests/install.sh index a448371f..ab666a81 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -9,7 +9,7 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -sudo apt-get install -qq screen zsh +sudo apt-get install -qq screen zsh tcsh # Travis has too outdated fish. It cannot be used for tests. # sudo apt-get install fish true diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index b5026d25..34ec242f 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -2,11 +2,11 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +[1]+ Terminated bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done'   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index e9f0d8ab..3a95d3a1 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -7,7 +7,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index f6dcc4fc..afb3df3b 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -8,7 +8,7 @@ cd .git cd .. set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" set VIRTUAL_ENV -bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false kill (cat pid) ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.tcsh b/tests/test_shells/input.tcsh new file mode 100644 index 00000000..4d48a465 --- /dev/null +++ b/tests/test_shells/input.tcsh @@ -0,0 +1,21 @@ +setenv POWERLINE_COMMAND $PWD:q/scripts/powerline" -p "$PWD:q/powerline/config_files" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" +unsetenv VIRTUAL_ENV +source powerline/bindings/tcsh/powerline.tcsh ; cd tests/shell/3rd +cd .git +cd .. +setenv VIRTUAL_ENV $HOME:q"/.virtenvs/some-virtual-environment" +unsetenv VIRTUAL_ENV +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +false # Warning: currently tcsh bindings do not support job count +kill `cat pid` ; sleep 1s +cd $DIR1:q +cd ../$DIR2:q +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' +false +true is the last line +exit diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 8cd28d54..1b3020a5 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/tcsh.ok b/tests/test_shells/tcsh.ok new file mode 100644 index 00000000..cea0c31a --- /dev/null +++ b/tests/test_shells/tcsh.ok @@ -0,0 +1,19 @@ + +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git    cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    setenv VIRTUAL_ENV $HOME:q"/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd    unsetenv VIRTUAL_ENV +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    false # Warning: currently tcsh bindings do not support job count +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1    kill `cat pid` ; sleep 1s +[1] + Terminated bash -c echo $$>pid ; while true ; do sleep 0.1s ; done +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    cd $DIR1:q +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m    cd ../$DIR2:q +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H    cd ../'\[\]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]    cd ../'%%' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%    cd ../'#[bold]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]    cd ../'(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)    cd ../'$(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)    cd ../'`echo`' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`    false diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index c38d13c4..9e9c370b 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -86,5 +86,9 @@ if ! run_test fish -i ; then FAILED=1 fi +if ! run_test tcsh -f -i ; then + FAILED=1 +fi + test "x$ONLY_SHELL" = "x" && rm -r tests/shell exit $FAILED diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index dbc5e17b..13daf829 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -3,11 +3,11 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1] + terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +[1] + terminated bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done'   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' From c648178be0eb07ae431ba5f9a9502e857920a8ab Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 27 Jan 2014 00:22:38 +0400 Subject: [PATCH 0877/1472] Strip anything but prompt from tcsh tests There are some problems with a number of spaces on travis: it differs. Cannot use the same code as for fish because output looks like {prompt}{spaces}^[0m ^[[{number}D and both `{spaces}` and `{number}` differ on my machine and in travis. --- tests/test_shells/postproc.py | 7 +++++++ tests/test_shells/tcsh.ok | 35 ++++++++++++++++------------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 9696b5b9..2da1fbc7 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -41,4 +41,11 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: line = line[start : end+4] + '\n' except ValueError: line = '' + elif shell == 'tcsh': + try: + start = line.index('\033[0;') + end = line.index(' ', start) + line = line[start : end] + '\033[0m\n' + except ValueError: + line = '' W.write(line) diff --git a/tests/test_shells/tcsh.ok b/tests/test_shells/tcsh.ok index cea0c31a..cf87b8f9 100644 --- a/tests/test_shells/tcsh.ok +++ b/tests/test_shells/tcsh.ok @@ -1,19 +1,16 @@ - -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    cd .git -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git    cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    setenv VIRTUAL_ENV $HOME:q"/.virtenvs/some-virtual-environment" -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd    unsetenv VIRTUAL_ENV -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & -[1] PID -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    false # Warning: currently tcsh bindings do not support job count -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1    kill `cat pid` ; sleep 1s -[1] + Terminated bash -c echo $$>pid ; while true ; do sleep 0.1s ; done -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd    cd $DIR1:q -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m    cd ../$DIR2:q -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H    cd ../'\[\]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]    cd ../'%%' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%    cd ../'#[bold]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]    cd ../'(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)    cd ../'$(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)    cd ../'`echo`' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`    false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`   From b1e81707c1186af1eb65a58491b29476644f2c48 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Jan 2014 00:31:50 +0400 Subject: [PATCH 0878/1472] Change warning level to debug --- powerline/segments/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 1245d109..af894ebf 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -57,7 +57,7 @@ def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, defa ''' mode = segment_info['mode'] if not mode: - pl.warn('No or empty POWERLINE_MODE variable') + pl.debug('No or empty POWERLINE_MODE variable') return None default = default or segment_info['environ'].get('POWERLINE_DEFAULT_MODE') if mode == default: From dc33c360981fac36ce0ff4684a0cc57f6fef3d77 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Jan 2014 00:42:56 +0400 Subject: [PATCH 0879/1472] Fix case when widget was defined with `zle -N widget` Fixes #774 --- powerline/bindings/zsh/powerline.zsh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 6b55f1c3..32e9b645 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -76,6 +76,9 @@ _powerline_add_widget() { save_widget="${save_widget}_$i" (( i++ )) done + # If widget was defined with `zle -N widget` (without `function` + # argument) then this function will be handy. + eval "function $save_widget() { emulate -L zsh; $widget \$@ }" eval "${old_widget_command/$widget/$save_widget}" zle -N $widget $function export POWERLINE_SAVE_WIDGET="$save_widget" From bc856987ffbb9b89e017c488a8a42a2244b93291 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Jan 2014 00:44:32 +0400 Subject: [PATCH 0880/1472] Add underscore to non-API functions and variables --- powerline/bindings/zsh/powerline.zsh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 32e9b645..21f56740 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -50,9 +50,9 @@ _powerline_setup_prompt() { chpwd_functions+=( _powerline_tmux_set_pwd ) _powerline_set_true_keymap_name "${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" if zmodload zsh/zpython &>/dev/null ; then - zpython 'from powerline.bindings.zsh import setup as powerline_setup' - zpython 'powerline_setup()' - zpython 'del powerline_setup' + zpython 'from powerline.bindings.zsh import setup as _powerline_setup' + zpython '_powerline_setup()' + zpython 'del _powerline_setup' else local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' @@ -70,7 +70,7 @@ _powerline_add_widget() { elif [[ -z "$old_widget_command" ]] ; then zle -N $widget $function else - local save_widget="powerline_save_$widget" + local save_widget="_powerline_save_$widget" local -i i=0 while ! test -z "$(zle -l -L $save_widget)" ; do save_widget="${save_widget}_$i" @@ -81,13 +81,13 @@ _powerline_add_widget() { eval "function $save_widget() { emulate -L zsh; $widget \$@ }" eval "${old_widget_command/$widget/$save_widget}" zle -N $widget $function - export POWERLINE_SAVE_WIDGET="$save_widget" + export _POWERLINE_SAVE_WIDGET="$save_widget" fi } _powerline_set_true_keymap_name() { - export POWERLINE_MODE="${1}" - local plm_bk="$(bindkey -lL ${POWERLINE_MODE})" + export _POWERLINE_MODE="${1}" + local plm_bk="$(bindkey -lL ${_POWERLINE_MODE})" if [[ $plm_bk = 'bindkey -A'* ]] ; then _powerline_set_true_keymap_name ${(Q)${${(z)plm_bk}[3]}} fi @@ -96,14 +96,14 @@ _powerline_set_true_keymap_name() { _powerline_zle_keymap_select() { _powerline_set_true_keymap_name $KEYMAP zle reset-prompt - test -z "$POWERLINE_SAVE_WIDGET" || zle $POWERLINE_SAVE_WIDGET + test -z "$_POWERLINE_SAVE_WIDGET" || zle $_POWERLINE_SAVE_WIDGET } _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select _powerline_precmd -if [[ "$POWERLINE_MODE" != vi* ]] ; then - export POWERLINE_DEFAULT_MODE="$POWERLINE_MODE" +if [[ "$_POWERLINE_MODE" != vi* ]] ; then + export _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" fi trap "_powerline_tmux_set_columns" SIGWINCH From bcd7bb349e2b26c7548711e01dd3b33edae734ae Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Jan 2014 00:45:00 +0400 Subject: [PATCH 0881/1472] Do the same for bash bindings --- powerline/bindings/bash/powerline.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 5b2af071..e5295ab3 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -16,11 +16,11 @@ _powerline_tmux_setenv() { fi } -POWERLINE_SAVED_PWD= +_POWERLINE_SAVED_PWD= _powerline_tmux_set_pwd() { - if test "x$POWERLINE_SAVED_PWD" != "x$PWD" ; then - POWERLINE_SAVED_PWD="$PWD" + if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" _powerline_tmux_setenv PWD "$PWD" fi } From 8096f2e8e4f6c6c9d1e7839d41f67d0a280def96 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Jan 2014 08:46:22 +0400 Subject: [PATCH 0882/1472] Also rename variables in python files --- powerline/segments/shell.py | 6 +++--- scripts/powerline | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index af894ebf..5e77f656 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -57,9 +57,9 @@ def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, defa ''' mode = segment_info['mode'] if not mode: - pl.debug('No or empty POWERLINE_MODE variable') + pl.debug('No or empty _POWERLINE_MODE variable') return None - default = default or segment_info['environ'].get('POWERLINE_DEFAULT_MODE') + default = default or segment_info['environ'].get('_POWERLINE_DEFAULT_MODE') if mode == default: return None try: @@ -68,7 +68,7 @@ def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, defa # Note: with zsh line editor you can emulate as much modes as you wish. # Thus having unknown mode is not an error: maybe just some developer # added support for his own zle widgets. As there is no built-in mode() - # function like in VimL and POWERLINE_MODE is likely be defined by our + # function like in VimL and _POWERLINE_MODE is likely be defined by our # code or by somebody knowing what he is doing there is absolutely no # need in keeping translations dictionary. return mode.upper() diff --git a/scripts/powerline b/scripts/powerline index 3192b4af..59dea618 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -22,7 +22,7 @@ if __name__ == '__main__': width=args.width, side=args.side, segment_info=segment_info, - mode=os.environ.get('POWERLINE_MODE'), + mode=os.environ.get('_POWERLINE_MODE'), ) try: sys.stdout.write(rendered) From d85ae963c39f122c741d67291c68461584e67d88 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 30 Jan 2014 09:00:33 +0400 Subject: [PATCH 0883/1472] Sleep more --- tests/test_config_reload.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 224baf4d..282d611f 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -99,7 +99,7 @@ def sleep(interval): def add_watcher_events(p, *args, **kwargs): p._watcher._reset(args) while not p._will_create_renderer(): - sleep(kwargs.get('interval', 0.000001)) + sleep(kwargs.get('interval', 0.1)) if not kwargs.get('wait', True): return @@ -185,6 +185,13 @@ class TestConfigReload(TestCase): config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' add_watcher_events(p, 'config') + # It may appear that p.logger._pop_msgs() is called after given + # exception is added to the mesagges, but before config_loader + # exception was added (this one: + # “exception:test:config_loader:Error while running condition + # function for key colorschemes/test/nonexistentraise: + # fcf:colorschemes/test/nonexistentraise”). + # sleep(0.1) self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents('config') self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) From 78f236b7359df9d2f2fc9122ca48385ff35f0c5f Mon Sep 17 00:00:00 2001 From: Pavlo Shchelokovskyy Date: Fri, 31 Jan 2014 12:46:40 +0200 Subject: [PATCH 0884/1472] Add cpu_load_percent colors to tmux Definitions for cpu_load_percent segment colors and gradient where missing from default tmux color definitions, rendering this segment unusable for tmux "out-of-the-box". --- powerline/config_files/colorschemes/tmux/default.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index c7c76a47..d418473c 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -20,6 +20,8 @@ "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, "system_load": { "fg": "gray8", "bg": "gray0" }, "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, + "cpu_load_percent": { "fg": "gray8", "bg": "gray0" }, + "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, "environment": { "fg": "gray8", "bg": "gray0" }, "battery": { "fg": "gray8", "bg": "gray0" }, "battery_gradient": { "fg": "white_red", "bg": "gray0" } From 6e0fe442de205966a19b550b09c961876374c3e2 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 29 Jan 2014 22:41:24 -0600 Subject: [PATCH 0885/1472] Port spotify mac script from tmux-powerline --- .../colorschemes/tmux/default.json | 3 +- powerline/lib/apple_script_runner.py | 13 +++++ powerline/segments/common.py | 58 ++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 powerline/lib/apple_script_runner.py diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index d418473c..b718a2ed 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -24,6 +24,7 @@ "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, "environment": { "fg": "gray8", "bg": "gray0" }, "battery": { "fg": "gray8", "bg": "gray0" }, - "battery_gradient": { "fg": "white_red", "bg": "gray0" } + "battery_gradient": { "fg": "white_red", "bg": "gray0" }, + "now_playing": { "fg": "gray10", "bg": "black" } } } diff --git a/powerline/lib/apple_script_runner.py b/powerline/lib/apple_script_runner.py new file mode 100644 index 00000000..d544e16e --- /dev/null +++ b/powerline/lib/apple_script_runner.py @@ -0,0 +1,13 @@ +# vim:fileencoding=utf-8:noet + +def asrun(ascript): + '''Run the given AppleScript and return the standard output and error.''' + from subprocess import Popen, PIPE + osa = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE) + return osa.communicate(ascript)[0] + +def asquote(astr): + '''Return the AppleScript equivalent of the given string.''' + + astr = astr.replace('"', '" & quote & "') + return '"{}"'.format(astr) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index b5cd1016..d4f47937 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -10,6 +10,7 @@ import socket from multiprocessing import cpu_count as _cpu_count from powerline.lib import add_divider_highlight_group +from powerline.lib.apple_script_runner import asrun, asquote from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess, tree_status from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring @@ -983,7 +984,7 @@ class NowPlayingSegment(object): 'total': now_playing[3], } - def player_spotify(self, pl): + def player_spotify_dbus(self, pl, dbus=None): try: import dbus except ImportError: @@ -1011,6 +1012,61 @@ class NowPlayingSegment(object): 'total': self._convert_seconds(info.get('mpris:length') / 1e6), } + def player_spotify_apple_script(self, pl): + ascript = ''' + tell application "System Events" + set process_list to (name of every process) + end tell + + if process_list contains "Spotify" then + tell application "Spotify" + if player state is playing or player state is paused then + set track_name to name of current track + set artist_name to artist of current track + set album_name to album of current track + set track_length to duration of current track + set trim_length to 40 + set now_playing to player state & album_name & artist_name & track_name & track_length + if length of now_playing is less than trim_length then + set now_playing_trim to now_playing + else + set now_playing_trim to characters 1 thru trim_length of now_playing as string + end if + else + return player state + end if + + end tell + else + return "stopped" + end if + ''' + + spotify = asrun(ascript) + + spotify_status = spotify.split(", ") + state = self._convert_state(spotify_status[0]) + if state == 'stop': + return + return { + 'state': state, + 'state_symbol': self.STATE_SYMBOLS.get(state), + 'album': spotify_status[1], + 'artist': spotify_status[2], + 'title': spotify_status[3], + 'total': self._convert_seconds(int(spotify_status[4])) + } + + try: + __import__('dbus') # NOQA + except ImportError: + if sys.platform.startswith('darwin'): + player_spotify = player_spotify_apple_script + else: + player_spotify = player_spotify_dbus # NOQA + else: + player_spotify = player_spotify_dbus # NOQA + def player_rhythmbox(self, pl): now_playing = self._run_cmd(['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) if not now_playing: From a975ece4e9fddcd8c4acd665f8cb250f449db2ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 21:09:33 +0400 Subject: [PATCH 0886/1472] Do not use sys.stderr for logging --- powerline/segments/common.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index d4f47937..f608b472 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -890,13 +890,13 @@ class NowPlayingSegment(object): return format.format(**stats) @staticmethod - def _run_cmd(cmd): + def _run_cmd(pl, cmd): from subprocess import Popen, PIPE try: p = Popen(cmd, stdout=PIPE) stdout, err = p.communicate() except OSError as e: - sys.stderr.write('Could not execute command ({0}): {1}\n'.format(e, cmd)) + pl.exception('Could not execute command ({0}): {1}'.format(e, cmd)) return None return stdout.strip() @@ -934,7 +934,7 @@ class NowPlayingSegment(object): method takes anything in ignore_levels and brings the key inside that to the first level of the dictionary. ''' - now_playing_str = self._run_cmd(['cmus-remote', '-Q']) + now_playing_str = self._run_cmd(pl, ['cmus-remote', '-Q']) if not now_playing_str: return ignore_levels = ('tag', 'set',) @@ -973,7 +973,7 @@ class NowPlayingSegment(object): 'total': self._convert_seconds(now_playing.get('time', 0)), } except ImportError: - now_playing = self._run_cmd(['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) + now_playing = self._run_cmd(pl, ['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) if not now_playing: return now_playing = now_playing.split('\n') @@ -988,7 +988,7 @@ class NowPlayingSegment(object): try: import dbus except ImportError: - sys.stderr.write('Could not add Spotify segment: Requires python-dbus.\n') + pl.exception('Could not add Spotify segment: requires python-dbus.') return bus = dbus.SessionBus() DBUS_IFACE_PROPERTIES = 'org.freedesktop.DBus.Properties' @@ -1068,7 +1068,7 @@ class NowPlayingSegment(object): player_spotify = player_spotify_dbus # NOQA def player_rhythmbox(self, pl): - now_playing = self._run_cmd(['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) + now_playing = self._run_cmd(pl, ['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) if not now_playing: return now_playing = now_playing.split('\n') From 44083581511a76e04fb417d1f71775871fa6f8cf Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 21:20:53 +0400 Subject: [PATCH 0887/1472] Move NowPlaying._run_cmd to powerline.lib.shell.run_cmd, make asrun use it --- powerline/lib/apple_script_runner.py | 13 ------------- powerline/lib/shell.py | 26 ++++++++++++++++++++++++++ powerline/segments/common.py | 21 +++++---------------- 3 files changed, 31 insertions(+), 29 deletions(-) delete mode 100644 powerline/lib/apple_script_runner.py create mode 100644 powerline/lib/shell.py diff --git a/powerline/lib/apple_script_runner.py b/powerline/lib/apple_script_runner.py deleted file mode 100644 index d544e16e..00000000 --- a/powerline/lib/apple_script_runner.py +++ /dev/null @@ -1,13 +0,0 @@ -# vim:fileencoding=utf-8:noet - -def asrun(ascript): - '''Run the given AppleScript and return the standard output and error.''' - from subprocess import Popen, PIPE - osa = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE) - return osa.communicate(ascript)[0] - -def asquote(astr): - '''Return the AppleScript equivalent of the given string.''' - - astr = astr.replace('"', '" & quote & "') - return '"{}"'.format(astr) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py new file mode 100644 index 00000000..608f3c31 --- /dev/null +++ b/powerline/lib/shell.py @@ -0,0 +1,26 @@ +# vim:fileencoding=utf-8:noet + +from subprocess import Popen, PIPE + + +def run_cmd(pl, cmd, stdin=None): + try: + p = Popen(cmd, stdout=PIPE, stdin=PIPE) + except OSError as e: + pl.exception('Could not execute command ({0}): {1}'.format(e, cmd)) + return None + else: + stdout, err = p.communicate(stdin) + return stdout.strip() + + +def asrun(pl, ascript): + '''Run the given AppleScript and return the standard output and error.''' + return run_cmd(pl, ['osascript', '-'], ascript) + + +def asquote(astr): + '''Return the AppleScript equivalent of the given string.''' + + astr = astr.replace('"', '" & quote & "') + return '"{}"'.format(astr) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f608b472..0fdd1f37 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -10,7 +10,7 @@ import socket from multiprocessing import cpu_count as _cpu_count from powerline.lib import add_divider_highlight_group -from powerline.lib.apple_script_runner import asrun, asquote +from powerline.lib.shell import asrun, asquote, run_cmd from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess, tree_status from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring @@ -889,17 +889,6 @@ class NowPlayingSegment(object): stats.update(func_stats) return format.format(**stats) - @staticmethod - def _run_cmd(pl, cmd): - from subprocess import Popen, PIPE - try: - p = Popen(cmd, stdout=PIPE) - stdout, err = p.communicate() - except OSError as e: - pl.exception('Could not execute command ({0}): {1}'.format(e, cmd)) - return None - return stdout.strip() - @staticmethod def _convert_state(state): state = state.lower() @@ -934,7 +923,7 @@ class NowPlayingSegment(object): method takes anything in ignore_levels and brings the key inside that to the first level of the dictionary. ''' - now_playing_str = self._run_cmd(pl, ['cmus-remote', '-Q']) + now_playing_str = run_cmd(pl, ['cmus-remote', '-Q']) if not now_playing_str: return ignore_levels = ('tag', 'set',) @@ -973,7 +962,7 @@ class NowPlayingSegment(object): 'total': self._convert_seconds(now_playing.get('time', 0)), } except ImportError: - now_playing = self._run_cmd(pl, ['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) + now_playing = run_cmd(pl, ['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) if not now_playing: return now_playing = now_playing.split('\n') @@ -1042,7 +1031,7 @@ class NowPlayingSegment(object): end if ''' - spotify = asrun(ascript) + spotify = asrun(pl, ascript) spotify_status = spotify.split(", ") state = self._convert_state(spotify_status[0]) @@ -1068,7 +1057,7 @@ class NowPlayingSegment(object): player_spotify = player_spotify_dbus # NOQA def player_rhythmbox(self, pl): - now_playing = self._run_cmd(pl, ['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) + now_playing = run_cmd(pl, ['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) if not now_playing: return now_playing = now_playing.split('\n') From f26eb24e105c6cf31854091c3898a7e662be21e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 21:21:22 +0400 Subject: [PATCH 0888/1472] Remove asquote function for it being used nowhere --- powerline/lib/shell.py | 7 ------- powerline/segments/common.py | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index 608f3c31..ef6cec41 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -17,10 +17,3 @@ def run_cmd(pl, cmd, stdin=None): def asrun(pl, ascript): '''Run the given AppleScript and return the standard output and error.''' return run_cmd(pl, ['osascript', '-'], ascript) - - -def asquote(astr): - '''Return the AppleScript equivalent of the given string.''' - - astr = astr.replace('"', '" & quote & "') - return '"{}"'.format(astr) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 0fdd1f37..c47d04d9 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -10,7 +10,7 @@ import socket from multiprocessing import cpu_count as _cpu_count from powerline.lib import add_divider_highlight_group -from powerline.lib.shell import asrun, asquote, run_cmd +from powerline.lib.shell import asrun, run_cmd from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess, tree_status from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring From 5db02bba3a6865a2b5c92071fdb54ca1b846a146 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 21:23:42 +0400 Subject: [PATCH 0889/1472] Check for asrun() returning None --- powerline/segments/common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c47d04d9..cbc8e0be 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1032,11 +1032,13 @@ class NowPlayingSegment(object): ''' spotify = asrun(pl, ascript) + if not asrun: + return None spotify_status = spotify.split(", ") state = self._convert_state(spotify_status[0]) if state == 'stop': - return + return None return { 'state': state, 'state_symbol': self.STATE_SYMBOLS.get(state), From 5071f127a82b1e43226f788ba5ea04f463fb7faf Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 21:56:16 +0400 Subject: [PATCH 0890/1472] Recreate args dictionary with str() arguments, use closures Fixes #776 --- powerline/segment.py | 23 ++++++++++++++++++++--- powerline/theme.py | 8 ++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index c96f1c0e..4afe37a3 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -63,7 +63,7 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): raise TypeError('Unknown segment type: {0}'.format(segment_type)) try: - contents, contents_func, module = get_segment_info(data, segment) + contents, _contents_func, module = get_segment_info(data, segment) except Exception as e: pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') return None @@ -73,6 +73,23 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): else: highlight_group = segment.get('highlight_group') or segment.get('name') + if segment_type == 'function': + args = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) + try: + _startup_func = _contents_func.startup + except AttributeError: + startup_func = None + else: + startup_func = lambda pl, shutdown_event: _startup_func(pl=pl, shutdown_event=shutdown_event, **args) + + if hasattr(_contents_func, 'powerline_requires_segment_info'): + contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) + else: + contents_func = lambda pl, segment_info: _contents_func(pl=pl, **args) + else: + startup_func = None + contents_func = None + return { 'name': segment.get('name'), 'type': segment_type, @@ -82,7 +99,7 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): 'after': get_key(segment, module, 'after', ''), 'contents_func': contents_func, 'contents': contents, - 'args': get_key(segment, module, 'args', {}) if segment_type == 'function' else {}, + 'args': args if segment_type == 'function' else {}, 'priority': segment.get('priority', None), 'draw_hard_divider': segment.get('draw_hard_divider', True), 'draw_soft_divider': segment.get('draw_soft_divider', True), @@ -93,7 +110,7 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): 'width': segment.get('width'), 'align': segment.get('align', 'l'), 'shutdown': getattr(contents_func, 'shutdown', None), - 'startup': getattr(contents_func, 'startup', None), + 'startup': startup_func, '_rendered_raw': '', '_rendered_hl': '', '_len': 0, diff --git a/powerline/theme.py b/powerline/theme.py index 56a2a1bb..0e76727c 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -51,7 +51,7 @@ class Theme(object): if not run_once: if segment['startup']: try: - segment['startup'](pl=pl, shutdown_event=shutdown_event, **segment['args']) + segment['startup'](pl, shutdown_event) except Exception as e: pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) continue @@ -84,11 +84,7 @@ class Theme(object): if segment['type'] == 'function': self.pl.prefix = segment['name'] try: - if (hasattr(segment['contents_func'], 'powerline_requires_segment_info') - and segment['contents_func'].powerline_requires_segment_info): - contents = segment['contents_func'](pl=self.pl, segment_info=segment_info, **segment['args']) - else: - contents = segment['contents_func'](pl=self.pl, **segment['args']) + contents = segment['contents_func'](self.pl, segment_info) except Exception as e: self.pl.exception('Exception while computing segment: {0}', str(e)) continue From 7f1abf1459bf21b8c8bdccaa82953d81fdf7db30 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 14 Nov 2013 01:40:40 +0100 Subject: [PATCH 0891/1472] Vim: add window_title function, used in quickfix theme This is used to display the window title of the quickfix window. Both Syntastic and Vim use this. See w:quickfix_title. --- .../config_files/colorschemes/vim/default.json | 1 + .../config_files/colorschemes/vim/solarized.json | 1 + powerline/config_files/themes/vim/quickfix.json | 5 ++++- powerline/segments/vim.py | 14 ++++++++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 984ac606..2cdef8db 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -14,6 +14,7 @@ "branch:divider": { "fg": "gray7", "bg": "gray4" }, "file_directory": { "fg": "gray9", "bg": "gray4" }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, + "window_title": { "fg": "white", "bg": "gray4" }, "file_size": { "fg": "gray8", "bg": "gray2" }, "file_name_no_file": { "fg": "gray9", "bg": "gray4", "attr": ["bold"] }, "file_name_empty": { "fg": "gray9", "bg": "gray4" }, diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 3adeb259..7705b17c 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -14,6 +14,7 @@ "branch:divider": { "fg": "gray61", "bg": "darkgreencopper" }, "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper" }, "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "window_title": { "fg": "oldlace", "bg": "darkgreencopper" }, "file_size": { "fg": "oldlace", "bg": "darkgreencopper" }, "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper" }, diff --git a/powerline/config_files/themes/vim/quickfix.json b/powerline/config_files/themes/vim/quickfix.json index da77d631..e1e8f253 100644 --- a/powerline/config_files/themes/vim/quickfix.json +++ b/powerline/config_files/themes/vim/quickfix.json @@ -9,7 +9,10 @@ { "type": "string", "name": "buffer_name", - "highlight_group": ["file_name"], + "highlight_group": ["file_name"] + }, + { + "name": "window_title", "draw_soft_divider": false }, { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 47441fe9..c79b498c 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -249,6 +249,20 @@ def file_type(pl, segment_info): return vim_getbufoption(segment_info, 'filetype') or None +@requires_segment_info +def window_title(pl, segment_info): + '''Return the window title. + + This currently looks at the ``quickfix_title`` window variable, + which is used by Syntastic and Vim itself. + + It is used in the quickfix theme.''' + try: + return segment_info['window'].vars['quickfix_title'] + except KeyError: + return None + + @requires_segment_info def line_percent(pl, segment_info, gradient=False): '''Return the cursor position in the file as a percentage. From d7589bc8950782c879a5af00f018156201888229 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 22:26:34 +0400 Subject: [PATCH 0892/1472] Make window_title work in older vims --- powerline/bindings/vim/__init__.py | 21 +++++++++++++++------ powerline/segments/vim.py | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 32736fdf..d618a089 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -43,7 +43,7 @@ else: # It may crash on some old vim versions and I do not remember in which patch # I fixed this crash. -if hasattr(vim, 'vars') and vim.vvars['version'] > 703: +if hasattr(vim, 'vvars') and vim.vvars['version'] > 703: _vim_to_python_types = { vim.Dictionary: lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), vim.List: lambda value: [_vim_to_python(item) for item in value], @@ -57,6 +57,13 @@ if hasattr(vim, 'vars') and vim.vvars['version'] > 703: def vim_getvar(varname): return _vim_to_python(vim.vars[str(varname)]) + + def bufvar_exists(buffer, varname): + buffer = buffer or vim.current.buffer + return varname in buffer.vars + + def vim_getwinvar(segment_info, varname): + return _vim_to_python(segment_info['window'].vars[str(varname)]) else: _vim_exists = vim_get_func('exists', rettype=int) @@ -67,11 +74,6 @@ else: else: raise KeyError(varname) -if hasattr(vim, 'vars') and vim.vvars['version'] > 703: - def bufvar_exists(buffer, varname): - buffer = buffer or vim.current.buffer - return varname in buffer.vars -else: def bufvar_exists(buffer, varname): # NOQA if not buffer or buffer.number == vim.current.buffer.number: return vim.eval('exists("b:{0}")'.format(varname)) @@ -79,6 +81,13 @@ else: return vim.eval('has_key(getbufvar({0}, ""), {1})' .format(buffer.number, varname)) + def vim_getwinvar(segment_info, varname): # NOQA + result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname)) + if result == '': + if not int(vim.eval('has_key(getwinvar({0}, ""), "{1}")'.format(segment_info['winnr'], varname))): + raise KeyError(varname) + return result + if hasattr(vim, 'options'): def vim_getbufoption(info, option): return info['buffer'].options[option] diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c79b498c..ac9552d6 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -9,7 +9,7 @@ except ImportError: vim = {} # NOQA from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, - buffer_name) + buffer_name, vim_getwinvar) from powerline.theme import requires_segment_info from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -258,7 +258,7 @@ def window_title(pl, segment_info): It is used in the quickfix theme.''' try: - return segment_info['window'].vars['quickfix_title'] + return vim_getwinvar(segment_info, 'quickfix_title') except KeyError: return None From 52d182f0e23700392ccbf2e9683acdaa4716ba35 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Feb 2014 22:34:14 +0400 Subject: [PATCH 0893/1472] Add window_title group to solarizedlight colorschem --- powerline/config_files/colorschemes/vim/solarizedlight.json | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index 70d8c4c5..b8e4c254 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -14,6 +14,7 @@ "branch:divider": { "fg": "gray61", "bg": "lightyellow" }, "file_directory": { "fg": "royalblue5", "bg": "lightyellow" }, "file_name": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, + "window_title": { "fg": "gray13", "bg": "lightyellow" }, "file_size": { "fg": "gray13", "bg": "lightyellow" }, "file_name_no_file": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "file_name_empty": { "fg": "gray13", "bg": "lightyellow" }, From 4a2fbb096be5e4bf5c13d6d71c8bcd5594de4ce6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Feb 2014 00:10:16 +0400 Subject: [PATCH 0894/1472] Add &ts setting and comment regarding .local.vimrc usage --- .local.vimrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.local.vimrc b/.local.vimrc index 68bf2234..e9a3bf05 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -1,3 +1,12 @@ +" Project vimrc file. To be sourced each time you open any file in this +" repository. You may use [vimscript #3393][1] [(homepage)][2] to do this +" automatically. +" +" [1]: http://www.vim.org/scripts/script.php?script_id=3393 +" [2]: https://github.com/thinca/vim-localrc setlocal noexpandtab +" Despite promise somewhere alignment is done only using tabs. Thus setting +" &tabstop is a requirement. +setlocal tabstop=4 let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225,W291,E126' let b:syntastic_checkers = ['flake8'] From 253ad02c3c8a30e898ae0e680286217a4937da7d Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Tue, 4 Feb 2014 10:50:44 +0100 Subject: [PATCH 0895/1472] Add i3 bindings and segments --- powerline/bindings/i3/powerline-i3.py | 40 +++++++++++++++++++++++++++ powerline/renderers/i3bgbar.py | 38 +++++++++++++++++++++++++ powerline/segments/i3wm.py | 18 ++++++++++++ 3 files changed, 96 insertions(+) create mode 100755 powerline/bindings/i3/powerline-i3.py create mode 100644 powerline/renderers/i3bgbar.py create mode 100644 powerline/segments/i3wm.py diff --git a/powerline/bindings/i3/powerline-i3.py b/powerline/bindings/i3/powerline-i3.py new file mode 100755 index 00000000..6e7873a6 --- /dev/null +++ b/powerline/bindings/i3/powerline-i3.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet + +from powerline import Powerline +from powerline.lib.monotonic import monotonic + +import sys +import time +import i3 +from threading import Lock + +name = 'wm' +if len( sys.argv ) > 1: + name = sys.argv[1] +powerline = Powerline(name, renderer_module='i3bgbar') +powerline.update_renderer() + +interval = 0.5 + +print '{"version": 1, "custom_workspace": true}' +print '[' +print ' [[],[]]' + +lock = Lock() + +def render( event=None, data=None, sub=None ): + global lock + lock.acquire() + s = '[\n' + powerline.render(side='right')[:-2] + '\n]\n' + s += ',[\n' + powerline.render(side='left' )[:-2] + '\n]' + print ',[\n' + s + '\n]' + sys.stdout.flush() + lock.release() + +sub = i3.Subscription( render, 'workspace' ) + +while True: + start_time = monotonic() + render() + time.sleep(max(interval - (monotonic() - start_time), 0.1)) diff --git a/powerline/renderers/i3bgbar.py b/powerline/renderers/i3bgbar.py new file mode 100644 index 00000000..a722028c --- /dev/null +++ b/powerline/renderers/i3bgbar.py @@ -0,0 +1,38 @@ +# vim:fileencoding=utf-8:noet + +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +import json + +class i3bgbarRenderer(Renderer): + '''i3bgbar Segment Renderer''' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don't need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attr=None): + '''Highlight a segment.''' + + segment = { "full_text": contents, "separator": False, "separator_block_width": 0 } # no seperators + + if fg is not None: + if fg is not False and fg[1] is not False: + segment['color'] = "#{0:06x}".format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + segment['background_color'] = "#{0:06x}".format(bg[1]) + """ + if attr is not None and attr is not False: + if attr & ATTR_BOLD: + awesome_attr += ['font_weight="bold"'] + if attr & ATTR_ITALIC: + awesome_attr += ['font_style="italic"'] + if attr & ATTR_UNDERLINE: + awesome_attr += ['underline="single"'] + """ + return json.dumps( segment ) + ",\n" + + +renderer = i3bgbarRenderer diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py new file mode 100644 index 00000000..d8e9038d --- /dev/null +++ b/powerline/segments/i3wm.py @@ -0,0 +1,18 @@ +# vim:fileencoding=utf-8:noet + +from powerline.theme import requires_segment_info +import i3 + +def calcgrp( w ): + group = ["workspace"] + if w['urgent']: group.append( 'w_urgent' ) + if w['visible']: group.append( 'w_visible' ) + if w['focused']: return "w_focused" #group.append( 'w_focused' ) + return group + +def workspaces( pl ): + '''Return workspace list + + Highlight groups used: ``workspace, visible, focused, urgent`` + ''' + return [ {'contents': w['name'], 'highlight_group': calcgrp( w )} for w in i3.get_workspaces() ] From 21319fc8d22469911c1cbcc41ec7320b1d6141e9 Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Fri, 7 Feb 2014 13:14:56 +0100 Subject: [PATCH 0896/1472] Use 'with' instead of lock.acquire/release() --- powerline/bindings/i3/powerline-i3.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/powerline/bindings/i3/powerline-i3.py b/powerline/bindings/i3/powerline-i3.py index 6e7873a6..799bec62 100755 --- a/powerline/bindings/i3/powerline-i3.py +++ b/powerline/bindings/i3/powerline-i3.py @@ -25,12 +25,11 @@ lock = Lock() def render( event=None, data=None, sub=None ): global lock - lock.acquire() - s = '[\n' + powerline.render(side='right')[:-2] + '\n]\n' - s += ',[\n' + powerline.render(side='left' )[:-2] + '\n]' - print ',[\n' + s + '\n]' - sys.stdout.flush() - lock.release() + with lock: + s = '[\n' + powerline.render(side='right')[:-2] + '\n]\n' + s += ',[\n' + powerline.render(side='left' )[:-2] + '\n]' + print ',[\n' + s + '\n]' + sys.stdout.flush() sub = i3.Subscription( render, 'workspace' ) From 7a90e6d6c3cf66b472ed6c81be7416f3197ab51d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Feb 2014 23:31:45 +0400 Subject: [PATCH 0897/1472] Cleanup import in segments.common: - move `import re` to top - if import is in a `try` block and there is some processing before it and `except` move this to `else`. --- powerline/segments/common.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index cbc8e0be..cf2076d9 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals, absolute_import import os import sys +import re from datetime import datetime import socket @@ -89,7 +90,6 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. ''' - import re try: cwd = segment_info['getcwd']() except OSError as e: @@ -672,7 +672,6 @@ def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_form class NetworkLoadSegment(KwThreadedSegment): - import re interfaces = {} replace_num_pat = re.compile(r'[a-zA-Z]+') @@ -815,14 +814,14 @@ class EmailIMAPSegment(KwThreadedSegment): return None try: import imaplib - import re + except imaplib.IMAP4.error as e: + unread_count = str(e) + else: mail = imaplib.IMAP4_SSL(key.server, key.port) mail.login(key.username, key.password) rc, message = mail.status(key.folder, '(UNSEEN)') unread_str = message[0].decode('utf-8') unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) - except imaplib.IMAP4.error as e: - unread_count = str(e) return unread_count @staticmethod @@ -944,6 +943,18 @@ class NowPlayingSegment(object): def player_mpd(self, pl, host='localhost', port=6600): try: import mpd + except ImportError: + now_playing = run_cmd(pl, ['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) + if not now_playing: + return + now_playing = now_playing.split('\n') + return { + 'album': now_playing[0], + 'artist': now_playing[1], + 'title': now_playing[2], + 'total': now_playing[3], + } + else: client = mpd.MPDClient() client.connect(host, port) now_playing = client.currentsong() @@ -961,17 +972,6 @@ class NowPlayingSegment(object): 'elapsed': self._convert_seconds(now_playing.get('elapsed', 0)), 'total': self._convert_seconds(now_playing.get('time', 0)), } - except ImportError: - now_playing = run_cmd(pl, ['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)]) - if not now_playing: - return - now_playing = now_playing.split('\n') - return { - 'album': now_playing[0], - 'artist': now_playing[1], - 'title': now_playing[2], - 'total': now_playing[3], - } def player_spotify_dbus(self, pl, dbus=None): try: From 9e8471be9eeee9d0a2c3dd85d59251124097b724 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Feb 2014 23:39:02 +0400 Subject: [PATCH 0898/1472] Fix non-ASCII directories processing Fixes #788 --- powerline/lib/unicode.py | 13 +++++++++++++ powerline/segments/common.py | 35 ++++++++++++++++++----------------- powerline/theme.py | 16 ++-------------- 3 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 powerline/lib/unicode.py diff --git a/powerline/lib/unicode.py b/powerline/lib/unicode.py new file mode 100644 index 00000000..645d4d7b --- /dev/null +++ b/powerline/lib/unicode.py @@ -0,0 +1,13 @@ +# vim:fileencoding=utf-8:noet + +try: + from __builtin__ import unicode +except ImportError: + unicode = str # NOQA + + +def u(s): + if type(s) is unicode: + return s + else: + return unicode(s, 'utf-8') diff --git a/powerline/segments/common.py b/powerline/segments/common.py index cf2076d9..8ddc8a9c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -17,6 +17,7 @@ from powerline.lib.vcs import guess, tree_status from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring from powerline.lib.monotonic import monotonic from powerline.lib.humanize_bytes import humanize_bytes +from powerline.lib.unicode import u from powerline.theme import requires_segment_info from collections import namedtuple @@ -91,7 +92,7 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. ''' try: - cwd = segment_info['getcwd']() + cwd = u(segment_info['getcwd']()) except OSError as e: if e.errno == 2: # user most probably deleted the directory @@ -100,7 +101,7 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s cwd = "[not found]" else: raise - home = segment_info['home'] + home = u(segment_info['home']) if home: cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) @@ -335,7 +336,7 @@ class WeatherSegment(ThreadedSegment): import json if not self.url: - # Do not lock attribute assignments in this branch: they are used + # Do not lock attribute assignments in this branch: they are used # only in .update() if not self.location: location_data = json.loads(urllib_read('http://freegeoip.net/json/')) @@ -428,12 +429,12 @@ weather conditions. :param str temp_format: format string, receives ``temp`` as an argument. Should also hold unit. :param float temp_coldest: - coldest temperature. Any temperature below it will have gradient level equal + coldest temperature. Any temperature below it will have gradient level equal to zero. :param float temp_hottest: - hottest temperature. Any temperature above it will have gradient level equal - to 100. Temperatures between ``temp_coldest`` and ``temp_hottest`` receive - gradient level that indicates relative position in this interval + hottest temperature. Any temperature above it will have gradient level equal + to 100. Temperatures between ``temp_coldest`` and ``temp_hottest`` receive + gradient level that indicates relative position in this interval (``100 * (cur-coldest) / (hottest-coldest)``). Divider highlight group used: ``background:divider``. @@ -453,17 +454,17 @@ def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2, track :param str format: format string, receives ``avg`` as an argument :param float threshold_good: - threshold for gradient level 0: any normalized load average below this + threshold for gradient level 0: any normalized load average below this value will have this gradient level. :param float threshold_bad: - threshold for gradient level 100: any normalized load average above this - value will have this gradient level. Load averages between - ``threshold_good`` and ``threshold_bad`` receive gradient level that + threshold for gradient level 100: any normalized load average above this + value will have this gradient level. Load averages between + ``threshold_good`` and ``threshold_bad`` receive gradient level that indicates relative position in this interval: (``100 * (cur-good) / (bad-good)``). Note: both parameters are checked against normalized load averages. :param bool track_cpu_count: - if True powerline will continuously poll the system to detect changes + if True powerline will continuously poll the system to detect changes in the number of CPUs. Divider highlight group used: ``background:divider``. @@ -629,7 +630,7 @@ elif 'psutil' in globals(): from time import time def _get_uptime(): # NOQA - # psutil.BOOT_TIME is not subject to clock adjustments, but time() is. + # psutil.BOOT_TIME is not subject to clock adjustments, but time() is. # Thus it is a fallback to /proc/uptime reading and not the reverse. return int(time() - psutil.BOOT_TIME) else: @@ -780,10 +781,10 @@ falls back to reading :param str sent_format: format string, receives ``value`` as argument :param float recv_max: - maximum number of received bytes per second. Is only used to compute + maximum number of received bytes per second. Is only used to compute gradient level :param float sent_max: - maximum number of sent bytes per second. Is only used to compute gradient + maximum number of sent bytes per second. Is only used to compute gradient level Divider highlight group used: ``background:divider``. @@ -855,8 +856,8 @@ email_imap_alert = with_docstring(EmailIMAPSegment(), :param str folder: folder to check for e-mails :param int max_msgs: - Maximum number of messages. If there are more messages then max_msgs then it - will use gradient level equal to 100, otherwise gradient level is equal to + Maximum number of messages. If there are more messages then max_msgs then it + will use gradient level equal to 100, otherwise gradient level is equal to ``100 * msgs_num / max_msgs``. If not present gradient is not computed. Highlight groups used: ``email_alert_gradient`` (gradient), ``email_alert``. diff --git a/powerline/theme.py b/powerline/theme.py index 0e76727c..23b0fc6f 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,19 +1,7 @@ # vim:fileencoding=utf-8:noet -from .segment import gen_segment_getter - - -try: - from __builtin__ import unicode -except ImportError: - unicode = str # NOQA - - -def u(s): - if type(s) is unicode: - return s - else: - return unicode(s, 'utf-8') +from powerline.segment import gen_segment_getter +from powerline.lib.unicode import u, unicode def requires_segment_info(func): From 4e350074861ff82390ec6de93653acc4487fa3cf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 9 Feb 2014 01:16:29 +0400 Subject: [PATCH 0899/1472] Home may be None, respect it --- powerline/segments/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 8ddc8a9c..b2311f16 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -101,8 +101,9 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s cwd = "[not found]" else: raise - home = u(segment_info['home']) + home = segment_info['home'] if home: + home = u(home) cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd_split = cwd.split(os.sep) cwd_split_len = len(cwd_split) From 5e4daed72bf87a1754e86fc6a2945d3866ab0a9a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 9 Feb 2014 01:16:48 +0400 Subject: [PATCH 0900/1472] Install bazaar with --allow-external --- tests/install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/install.sh b/tests/install.sh index ab666a81..bede6ac7 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -3,7 +3,8 @@ pip install . pip install psutil if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 - pip install mercurial bzr + pip install mercurial + pip install --allow-external bzr bzr if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 pip install unittest2 argparse From 773d6a6600d111a40471492c4e0955a6eb56713c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 9 Feb 2014 01:25:18 +0400 Subject: [PATCH 0901/1472] Also use --allow-unverified --- tests/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/install.sh b/tests/install.sh index bede6ac7..510e6a96 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -4,7 +4,7 @@ pip install psutil if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 pip install mercurial - pip install --allow-external bzr bzr + pip install --allow-external bzr --allow-unverified bzr bzr if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 pip install unittest2 argparse From 9accbdd9efcb0db17f18f5d1ca3da3f7b08c1b3f Mon Sep 17 00:00:00 2001 From: Joshua Perry Date: Fri, 7 Feb 2014 11:10:43 -0700 Subject: [PATCH 0902/1472] Added OSX battery support --- powerline/segments/common.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index b2311f16..68033c4c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1079,6 +1079,13 @@ if os.path.exists('/sys/class/power_supply/BAT0/capacity'): def _get_capacity(): with open('/sys/class/power_supply/BAT0/capacity', 'r') as f: return int(float(f.readline().split()[0])) +elif os.path.exists('/usr/bin/pmset'): + def _get_capacity(): + import re + import subprocess + battery_summary = subprocess.check_output(['pmset', '-g', 'batt']).decode('utf8') + batt_percent = re.search(r'\d+%', battery_summary).group(0) + return int(batt_percent.replace('%', '')) else: def _get_capacity(): raise NotImplementedError From f62d749f805e99fcd18e439f8c3047ffcc04e05e Mon Sep 17 00:00:00 2001 From: Joshua Perry Date: Sat, 8 Feb 2014 12:51:13 -0700 Subject: [PATCH 0903/1472] Refactored OSX battery code --- powerline/segments/common.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 68033c4c..c3db2ab5 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1076,22 +1076,21 @@ now_playing = NowPlayingSegment() if os.path.exists('/sys/class/power_supply/BAT0/capacity'): - def _get_capacity(): + def _get_capacity(pl): with open('/sys/class/power_supply/BAT0/capacity', 'r') as f: return int(float(f.readline().split()[0])) elif os.path.exists('/usr/bin/pmset'): - def _get_capacity(): + def _get_capacity(pl): import re - import subprocess - battery_summary = subprocess.check_output(['pmset', '-g', 'batt']).decode('utf8') - batt_percent = re.search(r'\d+%', battery_summary).group(0) - return int(batt_percent.replace('%', '')) + battery_summary = run_cmd(pl, ['pmset', '-g', 'batt']) + battery_percent = re.search(r'(\d+)%', battery_summary).group(1) + return int(battery_percent) else: - def _get_capacity(): + def _get_capacity(pl): raise NotImplementedError -def battery(pl, format='{batt:3.0%}', steps=5, gamify=False): +def battery(pl, format='{batt:3.0%}', steps=100, gamify=False): '''Return battery charge status. :param int steps: @@ -1102,7 +1101,7 @@ def battery(pl, format='{batt:3.0%}', steps=5, gamify=False): Highlight groups used: ``battery_gradient`` (gradient), ``battery``. ''' try: - capacity = _get_capacity() + capacity = _get_capacity(pl) except NotImplementedError: pl.warn('Unable to get battery capacity.') return None From 18f4fdaa7018a9324f609b8eb281ffaf27414b65 Mon Sep 17 00:00:00 2001 From: Joshua Perry Date: Sat, 8 Feb 2014 14:41:30 -0700 Subject: [PATCH 0904/1472] Revert change to segment parameter default value --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c3db2ab5..20084c1c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1090,7 +1090,7 @@ else: raise NotImplementedError -def battery(pl, format='{batt:3.0%}', steps=100, gamify=False): +def battery(pl, format='{batt:3.0%}', steps=5, gamify=False): '''Return battery charge status. :param int steps: From c4fb09a4189766053c5c5ddcdc78b9e7d905b7ae Mon Sep 17 00:00:00 2001 From: Joshua Perry Date: Sat, 8 Feb 2014 14:48:20 -0700 Subject: [PATCH 0905/1472] Update tests to support parameter on _get_capacity() --- tests/test_segments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index 789d5089..deca5c67 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -386,7 +386,7 @@ class TestCommon(TestCase): def test_battery(self): pl = Pl() - def _get_capacity(): + def _get_capacity(pl): return 86 with replace_attr(common, '_get_capacity', _get_capacity): From 66d98e7def979e1d03eee5e7a48f447ea7a2fef9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 9 Feb 2014 13:44:06 +0400 Subject: [PATCH 0906/1472] Improved battery segment: - Ignore `steps` if gamify is False. - Add `full_heart` and `empty_heart` keywords. - Document `format` keyword. - Replace `draw_soft_divider` with `draw_inner_divider`. --- powerline/segments/common.py | 36 +++++++++++++++++++++--------------- tests/test_segments.py | 30 ++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 20084c1c..01c18a20 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import +from __future__ import unicode_literals, absolute_import, division import os import sys @@ -1090,13 +1090,21 @@ else: raise NotImplementedError -def battery(pl, format='{batt:3.0%}', steps=5, gamify=False): +def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥', empty_heart='♥'): '''Return battery charge status. + :param str format: + Percent format in case gamify is False. :param int steps: - number of discrete steps to show between 0% and 100% capacity + Number of discrete steps to show between 0% and 100% capacity if gamify + is True. :param bool gamify: - measure in hearts (♥) instead of percentages + Measure in hearts (♥) instead of percentages. + :param str full_heart: + Heart displayed for “full” part of battery. + :param str empty_heart: + Heart displayed for “used” part of battery. It is also displayed using + another gradient level, so it is OK for it to be the same as full_heart. Highlight groups used: ``battery_gradient`` (gradient), ``battery``. ''' @@ -1106,27 +1114,25 @@ def battery(pl, format='{batt:3.0%}', steps=5, gamify=False): pl.warn('Unable to get battery capacity.') return None ret = [] - denom = int(steps) - numer = int(denom * capacity / 100) - full_heart = '♥' if gamify: + denom = int(steps) + numer = int(denom * capacity / 100) ret.append({ 'contents': full_heart * numer, - 'draw_soft_divider': False, + 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 99 + 'gradient_level': 99, }) ret.append({ - 'contents': full_heart * (denom - numer), - 'draw_soft_divider': False, + 'contents': empty_heart * (denom - numer), + 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 1 + 'gradient_level': 1, }) else: - batt = numer / float(denom) ret.append({ - 'contents': format.format(batt=batt), + 'contents': format.format(capacity=(capacity / 100.0)), 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': batt * 100 + 'gradient_level': capacity, }) return ret diff --git a/tests/test_segments.py b/tests/test_segments.py index deca5c67..2af11f5d 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -391,30 +391,44 @@ class TestCommon(TestCase): with replace_attr(common, '_get_capacity', _get_capacity): self.assertEqual(common.battery(pl=pl), [{ - 'contents': '80%', + 'contents': '86%', 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 80.0 + 'gradient_level': 86 }]) - self.assertEqual(common.battery(pl=pl, format='{batt:.2f}'), [{ - 'contents': '0.80', + self.assertEqual(common.battery(pl=pl, format='{capacity:.2f}'), [{ + 'contents': '0.86', 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 80.0 + 'gradient_level': 86 }]) self.assertEqual(common.battery(pl=pl, steps=7), [{ 'contents': '86%', 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 85.71428571428571 + 'gradient_level': 86 }]) self.assertEqual(common.battery(pl=pl, gamify=True), [ { 'contents': '♥♥♥♥', - 'draw_soft_divider': False, + 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], 'gradient_level': 99 }, { 'contents': '♥', - 'draw_soft_divider': False, + 'draw_inner_divider': False, + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 1 + } + ]) + self.assertEqual(common.battery(pl=pl, gamify=True, full_heart='+', empty_heart='-', steps='10'), [ + { + 'contents': '++++++++', + 'draw_inner_divider': False, + 'highlight_group': ['battery_gradient', 'battery'], + 'gradient_level': 99 + }, + { + 'contents': '--', + 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], 'gradient_level': 1 } From 7d89ff2b493cd403954a35da8201712a2f3beb9c Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Sun, 9 Feb 2014 11:10:49 +0100 Subject: [PATCH 0907/1472] Fix docstring for i3wm.workspaces segment --- powerline/segments/i3wm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index d8e9038d..e57f304e 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -7,12 +7,12 @@ def calcgrp( w ): group = ["workspace"] if w['urgent']: group.append( 'w_urgent' ) if w['visible']: group.append( 'w_visible' ) - if w['focused']: return "w_focused" #group.append( 'w_focused' ) + if w['focused']: return "w_focused" return group def workspaces( pl ): '''Return workspace list - Highlight groups used: ``workspace, visible, focused, urgent`` + Highlight groups used: ``workspace``, ``w_visible``, ``w_focused``, ``w_urgent`` ''' return [ {'contents': w['name'], 'highlight_group': calcgrp( w )} for w in i3.get_workspaces() ] From c5df55e25cf9d74a48b4168f72ca05b976335b6a Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Sun, 9 Feb 2014 11:11:57 +0100 Subject: [PATCH 0908/1472] Remove dead code from renderers/i3bgbar.py --- powerline/renderers/i3bgbar.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/powerline/renderers/i3bgbar.py b/powerline/renderers/i3bgbar.py index a722028c..bc549b10 100644 --- a/powerline/renderers/i3bgbar.py +++ b/powerline/renderers/i3bgbar.py @@ -23,16 +23,7 @@ class i3bgbarRenderer(Renderer): if bg is not None: if bg is not False and bg[1] is not False: segment['background_color'] = "#{0:06x}".format(bg[1]) - """ - if attr is not None and attr is not False: - if attr & ATTR_BOLD: - awesome_attr += ['font_weight="bold"'] - if attr & ATTR_ITALIC: - awesome_attr += ['font_style="italic"'] - if attr & ATTR_UNDERLINE: - awesome_attr += ['underline="single"'] - """ - return json.dumps( segment ) + ",\n" + return json.dumps( segment ) + ",\n" # i3bar "pseudo json" requires one line at a time renderer = i3bgbarRenderer From 8cb11d8915102e389d637b34f5515f91ebceda48 Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Sun, 9 Feb 2014 12:03:26 +0100 Subject: [PATCH 0909/1472] Adapt to python3 / __future__ print statement --- powerline/bindings/i3/powerline-i3.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/powerline/bindings/i3/powerline-i3.py b/powerline/bindings/i3/powerline-i3.py index 799bec62..463b3f4d 100755 --- a/powerline/bindings/i3/powerline-i3.py +++ b/powerline/bindings/i3/powerline-i3.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet +from __future__ import print_function from powerline import Powerline from powerline.lib.monotonic import monotonic @@ -17,9 +18,9 @@ powerline.update_renderer() interval = 0.5 -print '{"version": 1, "custom_workspace": true}' -print '[' -print ' [[],[]]' +print( '{"version": 1, "custom_workspace": true}' ) +print( '[' ) +print( ' [[],[]]' ) lock = Lock() @@ -28,7 +29,7 @@ def render( event=None, data=None, sub=None ): with lock: s = '[\n' + powerline.render(side='right')[:-2] + '\n]\n' s += ',[\n' + powerline.render(side='left' )[:-2] + '\n]' - print ',[\n' + s + '\n]' + print( ',[\n' + s + '\n]' ) sys.stdout.flush() sub = i3.Subscription( render, 'workspace' ) From 9548c4411938397b4f2d8a7b49b46cdc6aca0a3b Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Sun, 9 Feb 2014 12:10:18 +0100 Subject: [PATCH 0910/1472] Fix highlighting groups for workspaces segment --- powerline/segments/i3wm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index e57f304e..04115444 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -4,10 +4,11 @@ from powerline.theme import requires_segment_info import i3 def calcgrp( w ): - group = ["workspace"] + group = [] + if w['focused']: group.append( 'w_focused' ) if w['urgent']: group.append( 'w_urgent' ) if w['visible']: group.append( 'w_visible' ) - if w['focused']: return "w_focused" + group.append( 'workspace' ) return group def workspaces( pl ): From def68a2dc292a132231554d069440d4f2a491988 Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Sun, 9 Feb 2014 12:45:01 +0100 Subject: [PATCH 0911/1472] Update documentation to include i3 instructions --- docs/source/overview.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 90a4a791..d846efba 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -73,6 +73,7 @@ Python packages * ``pygit2`` * ``mercurial`` * ``psutil`` +* ``i3-py``, `available on github `_. Only used with i3wm bindings and segments. Other applications ^^^^^^^^^^^^^^^^^^ @@ -233,3 +234,19 @@ Add the following to your :file:`~/.config/qtile/config.py`: ), ), ] + +with i3bar replacement +----------------- + +.. note:: Until the patch is done in i3, you will need a custom ``i3bar`` build called ``i3bgbar``. + The source is available `here `_. + +Add the following to your :file:`~/.i3/config`:: + bar { + i3bar_command i3bgbar + + status_command python /path/to/powerline/bindings/i3/powerline-i3.py + font pango:PowerlineFont 12 + } + +where ``i3bgbar`` may be replaced with the path to the custom i3bar binary and ``PowerlineFont`` is any system font with powerline support. From bf89ac664436c0bae2d022e520a0c2b5a1506f5c Mon Sep 17 00:00:00 2001 From: Josh Turmel Date: Thu, 13 Feb 2014 22:49:13 -0600 Subject: [PATCH 0912/1472] Update font patching documentation Update reference to where fontpatcher.py is located --- docs/source/fontpatching.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst index b1d0f254..fcba993a 100644 --- a/docs/source/fontpatching.rst +++ b/docs/source/fontpatching.rst @@ -42,7 +42,8 @@ Code point Glyph Description Usage ===== -The font patcher is located at :file:`powerline/font/fontpatcher.py`. +The font patcher, :file:`fontpatcher.py` is located in the `powerline-fontpatcher +`_ repository on GitHub. It requires Python 2.7 and FontForge compiled with Python bindings to work. Patched fonts are renamed by default (" for Powerline" is added to the font From 4e6cd08078b8cde17372d49bcc3147e4e67b4b3d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 15 Feb 2014 20:10:07 +0400 Subject: [PATCH 0913/1472] In fish also check for presence of powerline in $PATH --- powerline/bindings/fish/powerline-setup.fish | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index e4dc1ace..ee90c9fd 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -2,8 +2,10 @@ function powerline-setup if test -z "$POWERLINE_COMMAND" if which powerline-client >/dev/null set -g -x POWERLINE_COMMAND powerline-client - else + else if which powerline >/dev/null set -g -x POWERLINE_COMMAND powerline + else + set -g -x POWERLINE_COMMAND (dirname (status -f))/../../../scripts/powerline end end function --on-variable POWERLINE_COMMAND _powerline_update From 6c1571139f416af85c824c1f45a483bfb3580b44 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 15 Feb 2014 20:21:09 +0400 Subject: [PATCH 0914/1472] Check for TMUX only once, check whether $TMUX is writeable Fixes #801 --- powerline/bindings/bash/powerline.sh | 50 ++++++++++++-------- powerline/bindings/fish/powerline-setup.fish | 20 ++++---- powerline/bindings/tcsh/powerline.tcsh | 2 +- powerline/bindings/zsh/powerline.zsh | 45 +++++++++++------- 4 files changed, 69 insertions(+), 48 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index e5295ab3..ca9e1792 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -9,26 +9,37 @@ if test -z "${POWERLINE_COMMAND}" ; then fi fi -_powerline_tmux_setenv() { - if [[ -n "$TMUX" ]]; then - tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" - tmux refresh -S +_powerline_init_tmux_support() { + # Note: `test -w ""` returns false, so first condition may be removed + if test -n "$TMUX" && test -w "$TMUX" ; then + # TMUX variable may be unset to create new tmux session inside this one + _POWERLINE_TMUX="$TMUX" + + _powerline_tmux_setenv() { + TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" + TMUX="$_POWERLINE_TMUX" tmux refresh -S + } + + _powerline_tmux_set_pwd() { + if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi + } + + _powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "$COLUMNS" + } + + trap "_powerline_tmux_set_columns" SIGWINCH + _powerline_tmux_set_columns + else + _powerline_tmux_set_pwd() { + return 0 + } fi } -_POWERLINE_SAVED_PWD= - -_powerline_tmux_set_pwd() { - if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then - _POWERLINE_SAVED_PWD="$PWD" - _powerline_tmux_setenv PWD "$PWD" - fi -} - -_powerline_tmux_set_columns() { - _powerline_tmux_setenv COLUMNS "$COLUMNS" -} - _powerline_prompt() { local last_exit_code=$? PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs -p|wc -l)")" @@ -36,8 +47,7 @@ _powerline_prompt() { return $last_exit_code } -trap "_powerline_tmux_set_columns" SIGWINCH -_powerline_tmux_set_columns - [[ "$PROMPT_COMMAND" != "${PROMPT_COMMAND/_powerline_prompt/}" ]] || export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n'"_powerline_prompt" + +_powerline_init_tmux_support diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index ee90c9fd..75ca19c4 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -20,18 +20,18 @@ function powerline-setup " end _powerline_update - function _powerline_tmux_setenv - if test -n "$TMUX" + if test -w "$TMUX" + function _powerline_tmux_setenv tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" tmux refresh -S end + function --on-variable PWD _powerline_tmux_set_pwd + _powerline_tmux_setenv PWD "$PWD" + end + function --on-signal WINCH _powerline_tmux_set_columns + _powerline_tmux_setenv COLUMNS "$COLUMNS" + end + _powerline_tmux_set_columns + _powerline_tmux_set_pwd end - function --on-variable PWD _powerline_tmux_set_pwd - _powerline_tmux_setenv PWD "$PWD" - end - function --on-signal WINCH _powerline_tmux_set_columns - _powerline_tmux_setenv COLUMNS "$COLUMNS" - end - _powerline_tmux_set_columns - _powerline_tmux_set_pwd end diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 48566318..44f969b6 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -14,7 +14,7 @@ if ! $?POWERLINE_COMMAND then setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline endif endif -alias _powerline_tmux_set_pwd 'if $?TMUX tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if $?TMUX tmux refresh -S' +alias _powerline_tmux_set_pwd 'if ( $?TMUX && { test -w $TMUX:q } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX && { test -w $TMUX:q } ) tmux refresh -S' alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 21f56740..347d236a 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -8,22 +8,37 @@ if test -z "${POWERLINE_COMMAND}" ; then fi fi -_powerline_tmux_setenv() { +integer _POWERLINE_JOBNUM + +_powerline_init_tmux_support() { emulate -L zsh - if [[ -n "$TMUX" ]]; then - tmux setenv -g TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" - tmux refresh -S + # Note: `test -w ""` returns false, so first condition may be removed + if test -n "$TMUX" && test -w "$TMUX" ; then + # TMUX variable may be unset to create new tmux session inside this one + typeset -g _POWERLINE_TMUX="$TMUX" + + function -g _powerline_tmux_setenv() { + emulate -L zsh + local -x TMUX="$_POWERLINE_TMUX" + tmux setenv -g TMUX_"$1"_$(tmux display -p "#D" | tr -d %) "$2" + tmux refresh -S + } + + function -g _powerline_tmux_set_pwd() { + _powerline_tmux_setenv PWD "$PWD" + } + + function -g _powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "$COLUMNS" + } + + chpwd_functions+=( _powerline_tmux_set_pwd ) + trap "_powerline_tmux_set_columns" SIGWINCH + _powerline_tmux_set_columns + _powerline_tmux_set_pwd fi } -_powerline_tmux_set_pwd() { - _powerline_tmux_setenv PWD "$PWD" -} - -_powerline_tmux_set_columns() { - _powerline_tmux_setenv COLUMNS "$COLUMNS" -} - _powerline_precmd() { # If you are wondering why I am not using the same code as I use for bash # ($(jobs|wc -l)): consider the following test: @@ -47,7 +62,6 @@ _powerline_setup_prompt() { fi done precmd_functions+=( _powerline_precmd ) - chpwd_functions+=( _powerline_tmux_set_pwd ) _powerline_set_true_keymap_name "${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" if zmodload zsh/zpython &>/dev/null ; then zpython 'from powerline.bindings.zsh import setup as _powerline_setup' @@ -106,10 +120,7 @@ if [[ "$_POWERLINE_MODE" != vi* ]] ; then export _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" fi -trap "_powerline_tmux_set_columns" SIGWINCH -_powerline_tmux_set_columns -_powerline_tmux_set_pwd - setopt promptpercent setopt promptsubst _powerline_setup_prompt +_powerline_init_tmux_support From 1add2b63de4695a980d335d2c7770573cdca74e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 15 Feb 2014 20:47:20 +0400 Subject: [PATCH 0915/1472] Disable mode support on <=zsh-4.3.10 Fixes #800 --- powerline/bindings/zsh/powerline.zsh | 76 ++++++++++++++++++---------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 347d236a..8d23c39e 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -39,7 +39,53 @@ _powerline_init_tmux_support() { fi } -_powerline_precmd() { +_powerline_init_modes_support() { + emulate -L zsh + + test -z "$ZSH_VERSION" && return 0 + + typeset -ga VS + VS=( ${(s:.:)ZSH_VERSION} ) + + # Mode support requires >=zsh-4.3.11 + if (( VS[1] < 4 || (VS[1] == 4 && (VS[2] < 3 || (VS[2] == 3 && VS[3] < 11))) )) ; then + return 0 + fi + + function -g _powerline_get_main_keymap_name() { + REPLY="${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" + } + + function -g _powerline_set_true_keymap_name() { + export _POWERLINE_MODE="${1}" + local plm_bk="$(bindkey -lL ${_POWERLINE_MODE})" + if [[ $plm_bk = 'bindkey -A'* ]] ; then + _powerline_set_true_keymap_name ${(Q)${${(z)plm_bk}[3]}} + fi + } + + function -g _powerline_zle_keymap_select() { + _powerline_set_true_keymap_name $KEYMAP + zle reset-prompt + test -z "$_POWERLINE_SAVE_WIDGET" || zle $_POWERLINE_SAVE_WIDGET + } + + function -g _powerline_set_main_keymap_name() { + local REPLY + _powerline_get_main_keymap_name + _powerline_set_true_keymap_name "$REPLY" + } + + if [[ "$_POWERLINE_MODE" != vi* ]] ; then + export _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" + fi + + _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select + _powerline_set_main_keymap_name + precmd_functions+=( _powerline_set_main_keymap_name ) +} + +_powerline_set_jobnum() { # If you are wondering why I am not using the same code as I use for bash # ($(jobs|wc -l)): consider the following test: # echo abc | less @@ -51,18 +97,16 @@ _powerline_precmd() { # wrong number of jobs. You need to filter the lines first. Or not use # jobs built-in at all. _POWERLINE_JOBNUM=${(%):-%j} - _powerline_set_true_keymap_name "${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" } _powerline_setup_prompt() { emulate -L zsh for f in "${precmd_functions[@]}"; do - if [[ "$f" = "_powerline_precmd" ]]; then + if [[ "$f" = "_powerline_set_jobnum" ]]; then return fi done - precmd_functions+=( _powerline_precmd ) - _powerline_set_true_keymap_name "${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" + precmd_functions+=( _powerline_set_jobnum ) if zmodload zsh/zpython &>/dev/null ; then zpython 'from powerline.bindings.zsh import setup as _powerline_setup' zpython '_powerline_setup()' @@ -99,28 +143,8 @@ _powerline_add_widget() { fi } -_powerline_set_true_keymap_name() { - export _POWERLINE_MODE="${1}" - local plm_bk="$(bindkey -lL ${_POWERLINE_MODE})" - if [[ $plm_bk = 'bindkey -A'* ]] ; then - _powerline_set_true_keymap_name ${(Q)${${(z)plm_bk}[3]}} - fi -} - -_powerline_zle_keymap_select() { - _powerline_set_true_keymap_name $KEYMAP - zle reset-prompt - test -z "$_POWERLINE_SAVE_WIDGET" || zle $_POWERLINE_SAVE_WIDGET -} - -_powerline_add_widget zle-keymap-select _powerline_zle_keymap_select -_powerline_precmd - -if [[ "$_POWERLINE_MODE" != vi* ]] ; then - export _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" -fi - setopt promptpercent setopt promptsubst _powerline_setup_prompt _powerline_init_tmux_support +_powerline_init_modes_support From 8041ea095614ca21d718c09d84a7dea77d85347f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 15 Feb 2014 21:01:14 +0400 Subject: [PATCH 0916/1472] Some style fixes --- docs/source/overview.rst | 13 +++++--- powerline/bindings/i3/powerline-i3.py | 47 ++++++++++++++------------- powerline/renderers/i3bgbar.py | 11 +++++-- powerline/segments/i3wm.py | 23 ++++++++----- 4 files changed, 56 insertions(+), 38 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index d846efba..6ec996e4 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -235,13 +235,15 @@ Add the following to your :file:`~/.config/qtile/config.py`: ), ] -with i3bar replacement ------------------ +I3 bar +------ -.. note:: Until the patch is done in i3, you will need a custom ``i3bar`` build called ``i3bgbar``. - The source is available `here `_. +.. note:: Until the patch is done in i3, you will need a custom ``i3bar`` build + called ``i3bgbar``. The source is available `here + `_. Add the following to your :file:`~/.i3/config`:: + bar { i3bar_command i3bgbar @@ -249,4 +251,5 @@ Add the following to your :file:`~/.i3/config`:: font pango:PowerlineFont 12 } -where ``i3bgbar`` may be replaced with the path to the custom i3bar binary and ``PowerlineFont`` is any system font with powerline support. +where ``i3bgbar`` may be replaced with the path to the custom i3bar binary and +``PowerlineFont`` is any system font with powerline support. diff --git a/powerline/bindings/i3/powerline-i3.py b/powerline/bindings/i3/powerline-i3.py index 463b3f4d..69ad1ffd 100755 --- a/powerline/bindings/i3/powerline-i3.py +++ b/powerline/bindings/i3/powerline-i3.py @@ -10,31 +10,34 @@ import time import i3 from threading import Lock -name = 'wm' -if len( sys.argv ) > 1: - name = sys.argv[1] -powerline = Powerline(name, renderer_module='i3bgbar') -powerline.update_renderer() -interval = 0.5 +if __name__ == '__main__': + name = 'wm' + if len(sys.argv) > 1: + name = sys.argv[1] -print( '{"version": 1, "custom_workspace": true}' ) -print( '[' ) -print( ' [[],[]]' ) + powerline = Powerline(name, renderer_module='i3bgbar') + powerline.update_renderer() -lock = Lock() + interval = 0.5 -def render( event=None, data=None, sub=None ): - global lock - with lock: - s = '[\n' + powerline.render(side='right')[:-2] + '\n]\n' - s += ',[\n' + powerline.render(side='left' )[:-2] + '\n]' - print( ',[\n' + s + '\n]' ) - sys.stdout.flush() + print ('{"version": 1, "custom_workspace": true}') + print ('[') + print ('\t[[],[]]') -sub = i3.Subscription( render, 'workspace' ) + lock = Lock() -while True: - start_time = monotonic() - render() - time.sleep(max(interval - (monotonic() - start_time), 0.1)) + def render(event=None, data=None, sub=None): + global lock + with lock: + s = '[\n' + powerline.render(side='right')[:-2] + '\n]\n' + s += ',[\n' + powerline.render(side='left')[:-2] + '\n]' + print (',[\n' + s + '\n]') + sys.stdout.flush() + + sub = i3.Subscription(render, 'workspace') + + while True: + start_time = monotonic() + render() + time.sleep(max(interval - (monotonic() - start_time), 0.1)) diff --git a/powerline/renderers/i3bgbar.py b/powerline/renderers/i3bgbar.py index bc549b10..55e1c3d4 100644 --- a/powerline/renderers/i3bgbar.py +++ b/powerline/renderers/i3bgbar.py @@ -1,9 +1,9 @@ # vim:fileencoding=utf-8:noet from powerline.renderer import Renderer -from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE import json + class i3bgbarRenderer(Renderer): '''i3bgbar Segment Renderer''' @@ -15,7 +15,11 @@ class i3bgbarRenderer(Renderer): def hl(self, contents, fg=None, bg=None, attr=None): '''Highlight a segment.''' - segment = { "full_text": contents, "separator": False, "separator_block_width": 0 } # no seperators + segment = { + "full_text": contents, + "separator": False, + "separator_block_width": 0, # no seperators + } if fg is not None: if fg is not False and fg[1] is not False: @@ -23,7 +27,8 @@ class i3bgbarRenderer(Renderer): if bg is not None: if bg is not False and bg[1] is not False: segment['background_color'] = "#{0:06x}".format(bg[1]) - return json.dumps( segment ) + ",\n" # i3bar "pseudo json" requires one line at a time + # i3bar "pseudo json" requires one line at a time + return json.dumps(segment) + ",\n" renderer = i3bgbarRenderer diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index 04115444..69752dff 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -1,19 +1,26 @@ # vim:fileencoding=utf-8:noet -from powerline.theme import requires_segment_info import i3 -def calcgrp( w ): + +def calcgrp(w): group = [] - if w['focused']: group.append( 'w_focused' ) - if w['urgent']: group.append( 'w_urgent' ) - if w['visible']: group.append( 'w_visible' ) - group.append( 'workspace' ) + if w['focused']: + group.append('w_focused') + if w['urgent']: + group.append('w_urgent') + if w['visible']: + group.append('w_visible') + group.append('workspace') return group -def workspaces( pl ): + +def workspaces(pl): '''Return workspace list Highlight groups used: ``workspace``, ``w_visible``, ``w_focused``, ``w_urgent`` ''' - return [ {'contents': w['name'], 'highlight_group': calcgrp( w )} for w in i3.get_workspaces() ] + return [{ + 'contents': w['name'], + 'highlight_group': calcgrp(w) + } for w in i3.get_workspaces()] From dfdc12b45a4cf85d4e87cda032bb62e2318c9739 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 15 Feb 2014 21:04:40 +0400 Subject: [PATCH 0917/1472] Move i3bgbar.py to i3bar.py --- powerline/bindings/i3/powerline-i3.py | 2 +- powerline/renderers/{i3bgbar.py => i3bar.py} | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) rename powerline/renderers/{i3bgbar.py => i3bar.py} (82%) diff --git a/powerline/bindings/i3/powerline-i3.py b/powerline/bindings/i3/powerline-i3.py index 69ad1ffd..d6d84f59 100755 --- a/powerline/bindings/i3/powerline-i3.py +++ b/powerline/bindings/i3/powerline-i3.py @@ -16,7 +16,7 @@ if __name__ == '__main__': if len(sys.argv) > 1: name = sys.argv[1] - powerline = Powerline(name, renderer_module='i3bgbar') + powerline = Powerline(name, renderer_module='i3bar') powerline.update_renderer() interval = 0.5 diff --git a/powerline/renderers/i3bgbar.py b/powerline/renderers/i3bar.py similarity index 82% rename from powerline/renderers/i3bgbar.py rename to powerline/renderers/i3bar.py index 55e1c3d4..2e0ef9b3 100644 --- a/powerline/renderers/i3bgbar.py +++ b/powerline/renderers/i3bar.py @@ -4,8 +4,11 @@ from powerline.renderer import Renderer import json -class i3bgbarRenderer(Renderer): - '''i3bgbar Segment Renderer''' +class I3barRenderer(Renderer): + '''I3bar Segment Renderer. + + Currently works only for i3bgbar (i3 bar with custom patches). + ''' @staticmethod def hlstyle(*args, **kwargs): @@ -13,8 +16,6 @@ class i3bgbarRenderer(Renderer): return '' def hl(self, contents, fg=None, bg=None, attr=None): - '''Highlight a segment.''' - segment = { "full_text": contents, "separator": False, @@ -31,4 +32,4 @@ class i3bgbarRenderer(Renderer): return json.dumps(segment) + ",\n" -renderer = i3bgbarRenderer +renderer = I3barRenderer From 536427f4beca06e123268d27bc93cb422384cf37 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 7 Jul 2013 19:06:11 +0400 Subject: [PATCH 0918/1472] Make powerline.vim work in compatible mode --- powerline/bindings/vim/plugin/powerline.vim | 42 +++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index a6497d3f..350c34ab 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -3,39 +3,41 @@ if exists('g:powerline_loaded') endif let g:powerline_loaded = 1 -function! s:CriticalError(message) - echohl ErrorMsg - echomsg a:message - echohl None -endfunction - if ! has('python') && ! has('python3') if !exists('g:powerline_no_python_error') - call s:CriticalError('You need vim compiled with Python 2.6+ or 3.2+ support - \ for Powerline to work. Please consult the documentation for more details.') + echohl ErrorMsg + echom 'You need vim compiled with Python 2.6, 2.7 or 3.2 and later support' + echom 'for Powerline to work. Please consult the documentation for more' + echom 'details.' + echohl None endif finish endif -let s:pycmd = substitute(get(g:, 'powerline_pycmd', has('python') ? 'py' : 'py3'), - \'\v^(py)%[thon](3?)$', '\1\2', '') +let s:pycmd = substitute(get(g:, 'powerline_pycmd', has('python') ? 'py' : 'py3'), '\v^(py)%[thon](3?)$', '\1\2', '') let s:pyeval = get(g:, 'powerline_pyeval', s:pycmd.'eval') let s:import_cmd = 'from powerline.vim import setup as powerline_setup' try - execute s:pycmd "try:\n" - \ ." ".s:import_cmd."\n" - \ ."except ImportError:\n" - \ ." import sys, vim\n" - \ ." sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))\n" - \ ." ".s:import_cmd + let s:pystr = "try:\n" + let s:pystr .= " ".s:import_cmd."\n" + let s:pystr .= "except ImportError:\n" + let s:pystr .= " import sys, vim\n" + let s:pystr .= " sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))\n" + let s:pystr .= " ".s:import_cmd."\n" + execute s:pycmd s:pystr + unlet s:pystr let s:launched = 1 finally if !exists('s:launched') - call s:CriticalError('An error occurred while importing the Powerline package. - \ This could be caused by an invalid sys.path setting, or by an incompatible - \ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult - \ the troubleshooting section in the documentation for possible solutions.') + echohl ErrorMsg + echom 'An error occurred while importing powerline package.' + echom 'This could be caused by invalid sys.path setting,' + echom 'or by an incompatible Python version (powerline requires' + echom 'Python 2.6, 2.7 or 3.2 and later to work). Please consult' + echom 'the troubleshooting section in the documentation for' + echom 'possible solutions.' + echohl None finish else unlet s:launched From 13df3d376e15a6d8c18838ea0d2b70a86b0e057a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 14 Oct 2013 18:32:56 +0400 Subject: [PATCH 0919/1472] More improved troubleshooting --- .../installation/troubleshooting-common.rst | 26 ++++++++++++ docs/source/overview.rst | 6 ++- powerline/bindings/vim/plugin/powerline.vim | 40 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 64d433c1..09425b96 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -131,3 +131,29 @@ Solution to this problem is simple: be sure that :file:`z.sh` is sourced strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background jobs are spawned by `z `_ after powerline has done its job. + + +I am experiencing problems after update +--------------------------------------- + +First, check out that you have only one powerline installed. Do + + .. code-block:: vim + + python import powerline + python print(powerline.__file__) + +(replace ``python`` with ``python3`` if appropriate) and check out that printed +file location is the one you have recently updated. If it is not you have the +following alternatives: + +* Always remember to update all installations of powerline you have. +* (Here and below I assume you have double vim and pip installation.) Use code + from first paragraph of :ref:`vim-vimrc` section and remove vim installation. +* Remove existing pip installation and use + + .. code-block:: sh + + pip install -e ~/.vim/bundle/powerline + + to reinstall. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 6ec996e4..cc1dfbf2 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -93,10 +93,12 @@ Installation Usage ===== +.. _vim-vimrc: + Vim statusline -------------- -If installed using pip just use +If installed using pip just add .. code-block:: vim @@ -104,7 +106,7 @@ If installed using pip just use python powerline_setup() python del powerline_setup -(replace ``python`` with ``python3`` if appropriate). +(replace ``python`` with ``python3`` if appropriate) to your :file:`vimrc`. If you just cloned the repository add the following line to your :file:`vimrc`, where ``{repository_root}`` is the absolute path to your Powerline installation diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 350c34ab..68ad6af9 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -29,6 +29,7 @@ try unlet s:pystr let s:launched = 1 finally + unlet s:import_cmd if !exists('s:launched') echohl ErrorMsg echom 'An error occurred while importing powerline package.' @@ -38,6 +39,42 @@ finally echom 'the troubleshooting section in the documentation for' echom 'possible solutions.' echohl None + let s:pystr = "def powerline_troubleshoot():\n" + let s:pystr .= " import sys\n" + let s:pystr .= " if sys.version_info < (2, 6):\n" + let s:pystr .= " print('Too old python version: ' + sys.version + ' (first supported is 2.6)')\n" + let s:pystr .= " elif sys.version_info[0] == 3 and sys.version_info[1] < 2:\n" + let s:pystr .= " print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')\n" + let s:pystr .= " try:\n" + let s:pystr .= " import powerline\n" + let s:pystr .= " except ImportError:\n" + let s:pystr .= " print('Unable to import powerline, is it installed?')\n" + if expand('')[:4] isnot# '/usr/' + let s:pystr .= " else:\n" + let s:pystr .= " import os\n" + let s:pystr .= " powerline_dir = os.path.dirname(os.path.realpath(powerline.__file__))\n" + let s:pystr .= " this_dir = os.path.dirname(os.path.realpath(vim.eval('expand(\":p\")')))\n" + let s:pystr .= " this_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))\n" + let s:pystr .= " if os.path.basename(this_dir) != 'powerline':\n" + let s:pystr .= " print('Check your installation:')\n" + let s:pystr .= " print('this script is not in powerline[/bindings/vim/plugin] directory,')\n" + let s:pystr .= " print('neither it is installed system-wide')\n" + let s:pystr .= " this_dir = os.path.dirname(this_dir)\n" + let s:pystr .= " real_powerline_dir = os.path.realpath(powerline_dir)\n" + let s:pystr .= " real_this_dir = os.path.realpath(this_dir)\n" + let s:pystr .= " if real_this_dir != sys.path[-1]:\n" + let s:pystr .= " print('Check your installation:')\n" + let s:pystr .= " print('this script is symlinked somewhere where powerline is not present.')\n" + let s:pystr .= " elif real_powerline_dir != real_this_dir:\n" + let s:pystr .= " print('It appears that you have two powerline versions installed:')\n" + let s:pystr .= " print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')\n" + let s:pystr .= " print('You should remove one of this. Check out troubleshooting section,')\n" + let s:pystr .= " print('it contains some information about the alternatives.')\n" + endif + execute s:pycmd s:pystr + unlet s:pystr + unlet s:pycmd + unlet s:pyeval finish else unlet s:launched @@ -47,3 +84,6 @@ endtry execute s:pycmd 'import vim' execute s:pycmd 'powerline_setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"))' execute s:pycmd 'del powerline_setup' + +unlet s:pycmd +unlet s:pyeval From 7e65332ddf2a59f2be4ec919c27dd1fae5a97f8c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 00:37:17 +0400 Subject: [PATCH 0920/1472] Remove outdated information from troubleshooting --- .../source/installation/troubleshooting-common.rst | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 09425b96..7f5a1e94 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -99,20 +99,6 @@ My vim statusline is hidden/only appears in split windows! * Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. -I get E858/E860 error in vim (Eval did not return a valid python object) --------------------------------------------------------------------------- - -* You need to make ``pyeval()`` display python stack trace. There is currently - a patch for this, but it was not merged into main vim tree, thus you will have - to use different approach: reproduce the error with - - .. code-block:: sh - - vim --cmd "let g:powerline_debugging_pyeval=1" ... - - and then use the stack trace to search for existing issues or to create a new - one. - My vim statusline is not displayed completely and has too much spaces --------------------------------------------------------------------- From d6f7f11491909287f6766ba64a84bb9596822b48 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 00:38:36 +0400 Subject: [PATCH 0921/1472] Add more spaces so that python indentation will be correct --- powerline/bindings/vim/plugin/powerline.vim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 68ad6af9..b5e11683 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -39,16 +39,16 @@ finally echom 'the troubleshooting section in the documentation for' echom 'possible solutions.' echohl None - let s:pystr = "def powerline_troubleshoot():\n" - let s:pystr .= " import sys\n" - let s:pystr .= " if sys.version_info < (2, 6):\n" - let s:pystr .= " print('Too old python version: ' + sys.version + ' (first supported is 2.6)')\n" - let s:pystr .= " elif sys.version_info[0] == 3 and sys.version_info[1] < 2:\n" - let s:pystr .= " print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')\n" - let s:pystr .= " try:\n" - let s:pystr .= " import powerline\n" - let s:pystr .= " except ImportError:\n" - let s:pystr .= " print('Unable to import powerline, is it installed?')\n" + let s:pystr = "def powerline_troubleshoot():\n" + let s:pystr .= " import sys\n" + let s:pystr .= " if sys.version_info < (2, 6):\n" + let s:pystr .= " print('Too old python version: ' + sys.version + ' (first supported is 2.6)')\n" + let s:pystr .= " elif sys.version_info[0] == 3 and sys.version_info[1] < 2:\n" + let s:pystr .= " print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')\n" + let s:pystr .= " try:\n" + let s:pystr .= " import powerline\n" + let s:pystr .= " except ImportError:\n" + let s:pystr .= " print('Unable to import powerline, is it installed?')\n" if expand('')[:4] isnot# '/usr/' let s:pystr .= " else:\n" let s:pystr .= " import os\n" From f384055f32cae2069fcbd3b837a6f02348c5149f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 00:40:22 +0400 Subject: [PATCH 0922/1472] A few style fixes --- powerline/bindings/vim/plugin/powerline.vim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index b5e11683..3aa97853 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -3,12 +3,12 @@ if exists('g:powerline_loaded') endif let g:powerline_loaded = 1 -if ! has('python') && ! has('python3') +if !has('python') && !has('python3') if !exists('g:powerline_no_python_error') echohl ErrorMsg - echom 'You need vim compiled with Python 2.6, 2.7 or 3.2 and later support' - echom 'for Powerline to work. Please consult the documentation for more' - echom 'details.' + echomsg 'You need vim compiled with Python 2.6, 2.7 or 3.2 and later support' + echomsg 'for Powerline to work. Please consult the documentation for more' + echomsg 'details.' echohl None endif finish @@ -32,12 +32,12 @@ finally unlet s:import_cmd if !exists('s:launched') echohl ErrorMsg - echom 'An error occurred while importing powerline package.' - echom 'This could be caused by invalid sys.path setting,' - echom 'or by an incompatible Python version (powerline requires' - echom 'Python 2.6, 2.7 or 3.2 and later to work). Please consult' - echom 'the troubleshooting section in the documentation for' - echom 'possible solutions.' + echomsg 'An error occurred while importing powerline package.' + echomsg 'This could be caused by invalid sys.path setting,' + echomsg 'or by an incompatible Python version (powerline requires' + echomsg 'Python 2.6, 2.7 or 3.2 and later to work). Please consult' + echomsg 'the troubleshooting section in the documentation for' + echomsg 'possible solutions.' echohl None let s:pystr = "def powerline_troubleshoot():\n" let s:pystr .= " import sys\n" From 7ab74ab73b55b1461150ccaea1726d68a5f2d805 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 00:41:44 +0400 Subject: [PATCH 0923/1472] Remove duplicate troubleshooting question --- .../installation/troubleshooting-common.rst | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 7f5a1e94..2c777ff5 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -117,29 +117,3 @@ Solution to this problem is simple: be sure that :file:`z.sh` is sourced strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background jobs are spawned by `z `_ after powerline has done its job. - - -I am experiencing problems after update ---------------------------------------- - -First, check out that you have only one powerline installed. Do - - .. code-block:: vim - - python import powerline - python print(powerline.__file__) - -(replace ``python`` with ``python3`` if appropriate) and check out that printed -file location is the one you have recently updated. If it is not you have the -following alternatives: - -* Always remember to update all installations of powerline you have. -* (Here and below I assume you have double vim and pip installation.) Use code - from first paragraph of :ref:`vim-vimrc` section and remove vim installation. -* Remove existing pip installation and use - - .. code-block:: sh - - pip install -e ~/.vim/bundle/powerline - - to reinstall. From 25212a7dd68cfde39d6b8ff19e2d76af32bdfcd9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 01:25:10 +0400 Subject: [PATCH 0924/1472] Use os.path.realpath() before os.path.abspath() Closes #454 Fixes #413 --- scripts/powerline | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/powerline b/scripts/powerline index 59dea618..74d0fc98 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -7,8 +7,7 @@ import os try: from powerline.shell import ShellPowerline, get_argparser, finish_args except ImportError: - import os - sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) from powerline.shell import ShellPowerline, get_argparser, finish_args # NOQA if __name__ == '__main__': From 8483683738c63aab7a5e26629c75ad6c1c5390fe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 02:53:51 +0400 Subject: [PATCH 0925/1472] Make virtcol_current_gradient background color match line_current color Fixes #449 --- powerline/config_files/colorschemes/vim/solarized.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 7705b17c..4822573e 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -30,7 +30,7 @@ "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, - "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "gray10" }, + "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "lightyellow" }, "col_current": { "fg": "azure4", "bg": "lightyellow" }, "environment": { "fg": "gray61", "bg": "royalblue5" }, "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, From 70a9da61f49b3f9a59cf0717daf23acb638851c1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 03:36:02 +0400 Subject: [PATCH 0926/1472] Format in PowerlineLogger._log, not in pl.exception arguments --- powerline/lib/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index ef6cec41..121cea58 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -7,7 +7,7 @@ def run_cmd(pl, cmd, stdin=None): try: p = Popen(cmd, stdout=PIPE, stdin=PIPE) except OSError as e: - pl.exception('Could not execute command ({0}): {1}'.format(e, cmd)) + pl.exception('Could not execute command ({0}): {1}', e, cmd) return None else: stdout, err = p.communicate(stdin) From 3aec68449ff7ebaa0013deb26e6289ca1b716b3f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 16:44:01 +0400 Subject: [PATCH 0927/1472] Use tuple in place of single-element list --- powerline/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/shell.py b/powerline/shell.py index aa04dc3d..f6889cd7 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -54,7 +54,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') p.add_argument('-p', '--config_path', metavar='PATH') - p.add_argument('-R', '--renderer_arg', metavar='KEY="VAL"', type=lambda a: dict([parsedotval(a)])) + p.add_argument('-R', '--renderer_arg', metavar='KEY="VAL"', type=lambda a: dict((parsedotval(a),))) return p From 0d49c06846164fa7246e02e81ce776e006dfdd05 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 19:17:06 +0400 Subject: [PATCH 0928/1472] Allow more then one -R be effective --- powerline/shell.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/shell.py b/powerline/shell.py index f6889cd7..9de81c17 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -54,7 +54,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') p.add_argument('-p', '--config_path', metavar='PATH') - p.add_argument('-R', '--renderer_arg', metavar='KEY="VAL"', type=lambda a: dict((parsedotval(a),))) + p.add_argument('-R', '--renderer_arg', metavar='KEY="VAL"', action='append') return p @@ -65,3 +65,5 @@ def finish_args(args): args.theme_option = mergeargs((parsedotval(v) for v in args.theme_option)) else: args.theme_option = {} + if args.renderer_arg: + args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg)) From 01ef26354fdfa8dba2ae56fc877edaeaa8ed165c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 19:59:06 +0400 Subject: [PATCH 0929/1472] Fix renderer_arg metavar --- powerline/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/shell.py b/powerline/shell.py index 9de81c17..9d7d114c 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -54,7 +54,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') p.add_argument('-p', '--config_path', metavar='PATH') - p.add_argument('-R', '--renderer_arg', metavar='KEY="VAL"', action='append') + p.add_argument('-R', '--renderer_arg', metavar='KEY=VAL', action='append') return p From aff028e4e9ff8fca12da0aa530d13a41e62f8d06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 20:06:32 +0400 Subject: [PATCH 0930/1472] Add support for zsh local themes Still missing: parser state Ref #771 --- powerline/bindings/zsh/__init__.py | 4 +- powerline/bindings/zsh/powerline.zsh | 4 ++ .../colorschemes/shell/default.json | 1 + powerline/config_files/config.json | 6 ++- .../themes/shell/continuation.json | 15 +++++++ .../config_files/themes/shell/select.json | 13 ++++++ powerline/renderers/ipython.py | 6 ++- powerline/renderers/zsh_prompt.py | 43 +++++++++++++++++++ powerline/shell.py | 7 +++ 9 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 powerline/config_files/themes/shell/continuation.json create mode 100644 powerline/config_files/themes/shell/select.json diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 5acdc39f..d6f7c523 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -104,10 +104,12 @@ class Prompt(object): self.args = powerline.args def __str__(self): + segment_info = {'args': self.args, 'environ': environ}, r = self.powerline.render( width=zsh.columns(), side=self.side, - segment_info={'args': self.args, 'environ': environ} + segment_info=segment_info, + matcher_info=segment_info, ) if type(r) is not str: if type(r) is bytes: diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 8d23c39e..68c589bb 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -113,9 +113,13 @@ _powerline_setup_prompt() { zpython 'del _powerline_setup' else local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' + add_args+=' --renderer_arg="client_id=$$"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args')' RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' + PS2='$($POWERLINE_COMMAND shell left -r zsh_prompt -R local_theme=continuation '$add_args')' + RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt -R local_theme=continuation '$add_args')' + PS3='$($POWERLINE_COMMAND shell left -r zsh_prompt -R local_theme=select '$add_args')' fi } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index d4f5a6f2..a9b0742f 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -8,6 +8,7 @@ "branch": { "fg": "gray9", "bg": "gray2" }, "branch_dirty": { "fg": "brightyellow", "bg": "gray2" }, "branch_clean": { "fg": "gray9", "bg": "gray2" }, + "prompt": { "fg": "gray9", "bg": "gray4" }, "cwd": { "fg": "gray9", "bg": "gray4" }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, "cwd:divider": { "fg": "gray7", "bg": "gray4" }, diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 4da4e9b1..b918838d 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -25,7 +25,11 @@ }, "shell": { "colorscheme": "default", - "theme": "default" + "theme": "default", + "local_themes": { + "continuation": "continuation", + "select": "select" + } }, "tmux": { "colorscheme": "default", diff --git a/powerline/config_files/themes/shell/continuation.json b/powerline/config_files/themes/shell/continuation.json new file mode 100644 index 00000000..2b4370de --- /dev/null +++ b/powerline/config_files/themes/shell/continuation.json @@ -0,0 +1,15 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "", + "width": "auto", + "align": "r", + "highlight_group": ["prompt"] + } + ], + "right": [ + ] + } +} diff --git a/powerline/config_files/themes/shell/select.json b/powerline/config_files/themes/shell/select.json new file mode 100644 index 00000000..cdf0998c --- /dev/null +++ b/powerline/config_files/themes/shell/select.json @@ -0,0 +1,13 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "Select", + "width": "auto", + "align": "r", + "highlight_group": ["prompt"] + } + ] + } +} diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 8de06dfe..69f38eaa 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -22,7 +22,11 @@ class IpythonRenderer(ShellRenderer): try: return match['theme'] except KeyError: - match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) + match['theme'] = Theme( + theme_config=match['config'], + top_theme_config=self.theme_config, + **self.theme_kwargs + ) return match['theme'] def shutdown(self): diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 47e76cc3..fcbe3e1c 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals from powerline.renderers.shell import ShellRenderer +from powerline.theme import Theme class ZshPromptRenderer(ShellRenderer): @@ -13,5 +14,47 @@ class ZshPromptRenderer(ShellRenderer): character_translations = ShellRenderer.character_translations.copy() character_translations[ord('%')] = '%%' + old_widths = {} + + def render(self, segment_info, *args, **kwargs): + client_id = segment_info.get('client_id') + key = (client_id, kwargs.get('side')) + kwargs = kwargs.copy() + width = kwargs.pop('width', None) + local_theme = segment_info.get('local_theme') + if client_id and local_theme: + output_raw = False + try: + width = self.old_widths[key] + except KeyError: + pass + else: + output_raw = True + ret = super(ShellRenderer, self).render( + output_raw=output_raw, + width=width, + matcher_info=local_theme, + segment_info=segment_info, + *args, **kwargs + ) + if output_raw: + self.old_widths[key] = len(ret[1]) + ret = ret[0] + return ret + + def get_theme(self, matcher_info): + if not matcher_info: + return self.theme + match = self.local_themes[matcher_info] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + top_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + renderer = ZshPromptRenderer diff --git a/powerline/shell.py b/powerline/shell.py index 9d7d114c..677f2c49 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -38,6 +38,13 @@ class ShellPowerline(Powerline): else: return super(ShellPowerline, self).get_config_paths() + def get_local_themes(self, local_themes): + if not local_themes: + return {} + + return dict(((key, {'config': self.load_theme_config(val)}) + for key, val in local_themes.items())) + def get_argparser(parser=None, *args, **kwargs): if not parser: From 772a09d01bf5e43ed3affae055201b7a91dcc7ae Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 20:28:59 +0400 Subject: [PATCH 0931/1472] Make PS2 display something more meaningful: add continuation segment --- powerline/bindings/zsh/powerline.zsh | 5 ++- .../colorschemes/shell/default.json | 3 +- .../themes/shell/continuation.json | 7 +-- powerline/segments/shell.py | 45 +++++++++++++++++++ 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 68c589bb..6e8206fe 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -115,10 +115,11 @@ _powerline_setup_prompt() { local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' + local add_args_2=$add_args' -R parser_state=${(%%):-%_} -R local_theme=continuation' PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args')' RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' - PS2='$($POWERLINE_COMMAND shell left -r zsh_prompt -R local_theme=continuation '$add_args')' - RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt -R local_theme=continuation '$add_args')' + PS2='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_2')' + RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args_2')' PS3='$($POWERLINE_COMMAND shell left -r zsh_prompt -R local_theme=select '$add_args')' fi } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index a9b0742f..2efd3796 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -8,7 +8,8 @@ "branch": { "fg": "gray9", "bg": "gray2" }, "branch_dirty": { "fg": "brightyellow", "bg": "gray2" }, "branch_clean": { "fg": "gray9", "bg": "gray2" }, - "prompt": { "fg": "gray9", "bg": "gray4" }, + "continuation": { "fg": "gray9", "bg": "gray4" }, + "continuation:current": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, "cwd": { "fg": "gray9", "bg": "gray4" }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, "cwd:divider": { "fg": "gray7", "bg": "gray4" }, diff --git a/powerline/config_files/themes/shell/continuation.json b/powerline/config_files/themes/shell/continuation.json index 2b4370de..e377f5b4 100644 --- a/powerline/config_files/themes/shell/continuation.json +++ b/powerline/config_files/themes/shell/continuation.json @@ -1,12 +1,9 @@ { + "default_module": "powerline.segments.shell", "segments": { "left": [ { - "type": "string", - "contents": "", - "width": "auto", - "align": "r", - "highlight_group": ["prompt"] + "name": "continuation" } ], "right": [ diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 5e77f656..a15b5522 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -72,3 +72,48 @@ def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, defa # code or by somebody knowing what he is doing there is absolutely no # need in keeping translations dictionary. return mode.upper() + + +@requires_segment_info +def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, renames={}): + '''Display parser state. + + :param bool omit_cmdsubst: + Do not display cmdsubst parser state if it is the last, but not the only + one. + :param bool right_align: + Align to the right. + :param dict renames: + Rename states: ``{old_name : new_name}``. If ``new_name`` is ``None`` + then given state is not displayed. + + Highlight groups used: ``continuation``, ``continuation:current``. + ''' + if not segment_info['parser_state']: + return None + ret = [] + + for state in segment_info['parser_state'].split(): + state = renames.get(state, state) + if state: + ret.append({ + 'contents': state, + 'highlight_group': 'continuation', + 'draw_inner_divider': True, + }) + + if omit_cmdsubst and len(ret) > 1 and ret[-1]['contents'] == 'cmdsubst': + ret.pop(-1) + + if not ret: + ret.append({ + 'contents': '' + }) + + if right_align: + ret[0].update(width='auto', align='r') + ret[-1]['highlight_group'] = 'continuation:current' + else: + ret[-1].update(width='auto', align='l', highlight_group='continuation:current') + + return ret From 1f4c77db5d1098d9ef8e8a136dae1a109ae2aff0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 20:40:40 +0400 Subject: [PATCH 0932/1472] Do not take number of subsegments into account, work without parser_state --- powerline/segments/shell.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index a15b5522..750c6f25 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -79,8 +79,7 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename '''Display parser state. :param bool omit_cmdsubst: - Do not display cmdsubst parser state if it is the last, but not the only - one. + Do not display cmdsubst parser state if it is the last one. :param bool right_align: Align to the right. :param dict renames: @@ -89,7 +88,7 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename Highlight groups used: ``continuation``, ``continuation:current``. ''' - if not segment_info['parser_state']: + if not segment_info.get('parser_state'): return None ret = [] @@ -102,7 +101,7 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename 'draw_inner_divider': True, }) - if omit_cmdsubst and len(ret) > 1 and ret[-1]['contents'] == 'cmdsubst': + if omit_cmdsubst and ret[-1]['contents'] == 'cmdsubst': ret.pop(-1) if not ret: From 8718bf76cee8bf54ad44b5bf07f753c61132b795 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 20:41:01 +0400 Subject: [PATCH 0933/1472] Add tests for continuation segment --- tests/test_segments.py | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index 2af11f5d..48fde0c2 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -48,6 +48,103 @@ class TestShell(TestCase): self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info, show_zero=False), '1') self.assertEqual(shell.jobnum(pl=pl, segment_info=segment_info, show_zero=True), '1') + def test_continuation(self): + pl = Pl() + self.assertEqual(shell.continuation(pl=pl, segment_info={}), None) + segment_info = {'parser_state': 'if cmdsubst'} + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info), [ + { + 'contents': 'if', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'l', + }, + ]) + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, right_align=True), [ + { + 'contents': 'if', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'r', + }, + ]) + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=False), [ + { + 'contents': 'if', + 'draw_inner_divider': True, + 'highlight_group': 'continuation', + }, + { + 'contents': 'cmdsubst', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'l', + }, + ]) + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=False, right_align=True), [ + { + 'contents': 'if', + 'draw_inner_divider': True, + 'highlight_group': 'continuation', + 'width': 'auto', + 'align': 'r', + }, + { + 'contents': 'cmdsubst', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + }, + ]) + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=True, right_align=True), [ + { + 'contents': 'if', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'r', + }, + ]) + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=True, right_align=True, renames={'if': 'IF'}), [ + { + 'contents': 'IF', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'r', + }, + ]) + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=True, right_align=True, renames={'if': None}), [ + { + 'contents': '', + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'r', + }, + ]) + segment_info = {'parser_state': 'then then then cmdsubst'} + self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info), [ + { + 'contents': 'then', + 'draw_inner_divider': True, + 'highlight_group': 'continuation', + }, + { + 'contents': 'then', + 'draw_inner_divider': True, + 'highlight_group': 'continuation', + }, + { + 'contents': 'then', + 'draw_inner_divider': True, + 'highlight_group': 'continuation:current', + 'width': 'auto', + 'align': 'l', + }, + ]) + class TestCommon(TestCase): def test_hostname(self): From 7eed06f149a35f7f4587c766aeee4fe2aa900dad Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 20:52:02 +0400 Subject: [PATCH 0934/1472] Make themes/shell/select.json look better --- powerline/config_files/themes/shell/select.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/themes/shell/select.json b/powerline/config_files/themes/shell/select.json index cdf0998c..172912f1 100644 --- a/powerline/config_files/themes/shell/select.json +++ b/powerline/config_files/themes/shell/select.json @@ -3,10 +3,10 @@ "left": [ { "type": "string", - "contents": "Select", + "contents": "Select variant", "width": "auto", "align": "r", - "highlight_group": ["prompt"] + "highlight_group": ["continuation:current"] } ] } From 5c33de7a243abc5a670c6f21ee3407e2b2eab724 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 20:52:22 +0400 Subject: [PATCH 0935/1472] Also test zsh local themes --- tests/test_configuration.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 672c31b7..64e9d6a6 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -85,10 +85,18 @@ class TestConfig(TestCase): def test_zsh(self): from powerline.shell import ShellPowerline args = Args(last_pipe_status=[1, 0], jobnum=0, ext=['shell'], renderer_module='zsh_prompt') + segment_info = {'args': args} with ShellPowerline(args, run_once=False) as powerline: - powerline.render(segment_info={'args': args}) + powerline.render(segment_info=segment_info) with ShellPowerline(args, run_once=False) as powerline: - powerline.render(segment_info={'args': args}) + powerline.render(segment_info=segment_info) + segment_info['local_theme'] = 'select' + with ShellPowerline(args, run_once=False) as powerline: + powerline.render(segment_info=segment_info) + segment_info['local_theme'] = 'continuation' + segment_info['parser_state'] = 'if cmdsubst' + with ShellPowerline(args, run_once=False) as powerline: + powerline.render(segment_info=segment_info) def test_bash(self): from powerline.shell import ShellPowerline From cf0d0944b0bb7071062f6630a82af05cce93bc08 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 21:11:37 +0400 Subject: [PATCH 0936/1472] Add zpython support --- powerline/bindings/zsh/__init__.py | 35 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index d6f7c523..1df90956 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -55,8 +55,8 @@ class Args(object): @property def jobnum(self): - zsh.eval('integer POWERLINE_JOBNUM=${(%):-%j}') - return zsh.getvalue('POWERLINE_JOBNUM') + zsh.eval('integer _POWERLINE_JOBNUM=${(%):-%j}') + return zsh.getvalue('_POWERLINE_JOBNUM') def string(s): @@ -94,22 +94,29 @@ environ = Environment() class Prompt(object): - __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args') + __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme') - def __init__(self, powerline, side, savedpsvar=None, savedps=None): + def __init__(self, powerline, side, theme, savedpsvar=None, savedps=None): self.powerline = powerline self.side = side self.savedpsvar = savedpsvar self.savedps = savedps self.args = powerline.args + self.theme = theme def __str__(self): - segment_info = {'args': self.args, 'environ': environ}, + zsh.eval('_POWERLINE_PARSER_STATE="${(%):-%_}"') + segment_info = { + 'args': self.args, + 'environ': environ, + 'client_id': 1, + 'local_theme': self.theme, + 'parser_state': zsh.getvalue('_POWERLINE_PARSER_STATE'), + } r = self.powerline.render( width=zsh.columns(), side=self.side, segment_info=segment_info, - matcher_info=segment_info, ) if type(r) is not str: if type(r) is bytes: @@ -126,10 +133,13 @@ class Prompt(object): self.powerline.shutdown() -def set_prompt(powerline, psvar, side): - savedps = zsh.getvalue(psvar) +def set_prompt(powerline, psvar, side, theme): + try: + savedps = zsh.getvalue(psvar) + except IndexError: + savedps = None zpyvar = 'ZPYTHON_POWERLINE_' + psvar - prompt = Prompt(powerline, side, psvar, savedps) + prompt = Prompt(powerline, side, theme, psvar, savedps) zsh.set_special_string(zpyvar, prompt) zsh.setvalue(psvar, '${' + zpyvar + '}') @@ -138,6 +148,9 @@ def setup(): powerline = ShellPowerline(Args()) used_powerlines.append(powerline) used_powerlines.append(powerline) - set_prompt(powerline, 'PS1', 'left') - set_prompt(powerline, 'RPS1', 'right') + set_prompt(powerline, 'PS1', 'left', None) + set_prompt(powerline, 'RPS1', 'right', None) + set_prompt(powerline, 'PS2', 'left', 'continuation') + set_prompt(powerline, 'RPS2', 'right', 'continuation') + set_prompt(powerline, 'PS3', 'left', 'select') atexit.register(shutdown) From a86c66f4e0cde1006e446458340d22767f241c0e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 21:15:57 +0400 Subject: [PATCH 0937/1472] Update zsh tests Note: to make prompt fancy enough powerline-daemon should be used. Otherwise continuation prompt length is different from what was expected: With daemon: user > env > path > if true ; then then > fi Without: user > env > path > if true ; then then > fi Fixes #771 as I do not want to touch tcsh any longer --- tests/test_shells/input.zsh | 6 ++++++ tests/test_shells/zsh.ok | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 1b3020a5..5beb8517 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -10,6 +10,12 @@ VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false +select abc in def ghi jkl +do + echo $abc + break +done +1 kill `cat pid` ; sleep 1s cd "$DIR1" cd ../"$DIR2" diff --git a/tests/test_shells/zsh.ok b/tests/test_shells/zsh.ok index 13daf829..c681e75e 100644 --- a/tests/test_shells/zsh.ok +++ b/tests/test_shells/zsh.ok @@ -6,7 +6,15 @@   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  select abc in def ghi jkl + select  do + select   echo $abc + select   break + select  done +1) def 2) ghi 3) jkl + Select variant  1 +def +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done'   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" From 22b1c7437acf32e84cf4ca6b8aaf4b3cf414a4b9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 21:19:44 +0400 Subject: [PATCH 0938/1472] Also update solarized colorscheme --- .../colorschemes/shell/solarized.json | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index e87abe8e..612941d0 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,21 +1,23 @@ { "name": "Solarized Dark", "groups": { - "jobnum": { "fg": "oldlace", "bg": "darkgreencopper" }, - "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, - "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, - "virtualenv": { "fg": "oldlace", "bg": "green" }, - "branch": { "fg": "gray61", "bg": "royalblue5" }, - "branch_dirty": { "fg": "yellow", "bg": "royalblue5" }, - "branch_clean": { "fg": "gray61", "bg": "royalblue5" }, - "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, - "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, - "exit_fail": { "fg": "oldlace", "bg": "red" }, - "exit_success": { "fg": "oldlace", "bg": "green" }, - "environment": { "fg": "oldlace", "bg": "green" }, - "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } + "jobnum": { "fg": "oldlace", "bg": "darkgreencopper" }, + "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, + "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, + "virtualenv": { "fg": "oldlace", "bg": "green" }, + "branch": { "fg": "gray61", "bg": "royalblue5" }, + "branch_dirty": { "fg": "yellow", "bg": "royalblue5" }, + "branch_clean": { "fg": "gray61", "bg": "royalblue5" }, + "continuation": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "continuation:current": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, + "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, + "exit_fail": { "fg": "oldlace", "bg": "red" }, + "exit_success": { "fg": "oldlace", "bg": "green" }, + "environment": { "fg": "oldlace", "bg": "green" }, + "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } }, "mode_translations": { "vicmd": { From 58d5d6b078abe524c51e6db7963cc5091b0e0835 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 21:22:05 +0400 Subject: [PATCH 0939/1472] Remove failing test --- tests/test_cmdline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 05db6ab2..cc27d396 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -43,7 +43,6 @@ class TestParser(TestCase): (['-r', 'zsh_prompt'], 'too few arguments|the following arguments are required: ext'), (['shell', '--last_exit_code', 'i'], 'invalid int value'), (['shell', '--last_pipe_status', '1 i'], 'invalid value'), - (['shell', '-R', 'abc'], 'invalid value'), ]: self.assertRaises(SystemExit, parser.parse_args, raising_args) self.assertFalse(out.getvalue()) From 19da7b28a0def808c3ecbdfeb4bb3bd404b4f05e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Feb 2014 21:25:49 +0400 Subject: [PATCH 0940/1472] Add powerline-lint support for new local themes --- powerline/lint/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6d778234..ec4d4608 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -478,6 +478,14 @@ main_spec = (Spec( rewrite=theme_spec(), ), ).optional(), + shell=Spec( + colorscheme=colorscheme_spec(), + theme=theme_spec(), + local_themes=Spec( + continuation=theme_spec(), + select=theme_spec(), + ), + ).optional(), ).unknown_spec(check_ext, Spec( colorscheme=colorscheme_spec(), From 725ff69be0ebf3146544f6b5c217a3c2a8529146 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Feb 2014 17:24:21 +0400 Subject: [PATCH 0941/1472] Refactor KwThreadedSegment Fixes #813 --- powerline/lib/threaded.py | 43 +++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 7ceea797..759e72c1 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -144,7 +144,7 @@ class KwThreadedSegment(ThreadedSegment): self.updated = True self.update_value = ({}, set()) self.write_lock = Lock() - self.new_queries = {} + self.new_queries = [] @staticmethod def key(**kwargs): @@ -159,34 +159,43 @@ class KwThreadedSegment(ThreadedSegment): try: update_state = queries[key][1] except KeyError: - # Allow only to forbid to compute missing values: in either user - # configuration or in subclasses. - update_state = self.compute_state(key) if ((update_first and self.update_first) or self.run_once) else None + with self.write_lock: + self.new_queries.append(key) + if update_first and self.update_first: + return self.render(update_value=self.get_update_value(True), update_first=False, **kwargs) + else: + update_state = None - with self.write_lock: - self.new_queries[key] = (monotonic(), update_state) return self.render_one(update_state, **kwargs) + def update_one(self, crashed, updates, key): + try: + updates[key] = (monotonic(), self.compute_state(key)) + except Exception as e: + self.exception('Exception while computing state for {0!r}: {1}', key, str(e)) + crashed.add(key) + except KeyboardInterrupt: + self.warn('Interrupt while computing state for {0!r}', key) + crashed.add(key) + def update(self, old_update_value): updates = {} crashed = set() update_value = (updates, crashed) queries = old_update_value[0] + + new_queries = self.new_queries with self.write_lock: - if self.new_queries: - queries.update(self.new_queries) - self.new_queries.clear() + self.new_queries = [] for key, (last_query_time, state) in queries.items(): if last_query_time < monotonic() < last_query_time + self.drop_interval: - try: - updates[key] = (last_query_time, self.compute_state(key)) - except Exception as e: - self.exception('Exception while computing state for {0!r}: {1}', key, str(e)) - crashed.add(key) - except KeyboardInterrupt: - self.warn('Interrupt while computing state for {0!r}', key) - crashed.add(key) + updates[key] = (last_query_time, state) + else: + self.update_one(crashed, updates, key) + + for key in new_queries: + self.update_one(crashed, updates, key) return update_value From 66a20874741cf7a9505c17fbc24ad6202023b67a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 22 Feb 2014 18:01:23 +0400 Subject: [PATCH 0942/1472] Use only get/set_update_value functions to access update_value Also rename .skip to .crashed --- powerline/lib/threaded.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 759e72c1..8a259719 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -37,7 +37,7 @@ class ThreadedSegment(MultiRunnedThread): def __init__(self): super(ThreadedSegment, self).__init__() self.run_once = True - self.skip = False + self.crashed = False self.crashed_value = None self.update_value = None self.updated = False @@ -59,31 +59,34 @@ class ThreadedSegment(MultiRunnedThread): update_value = self.get_update_value(True) self.updated = True else: - update_value = self.update_value + update_value = self.get_update_value() - if self.skip: + if self.crashed: return self.crashed_value return self.render(update_value, update_first=update_first, pl=pl, **kwargs) + def set_update_value(self): + try: + self.update_value = self.update(self.update_value) + except Exception as e: + self.exception('Exception while updating: {0}', str(e)) + self.crashed = True + except KeyboardInterrupt: + self.warn('Caught keyboard interrupt while updating') + self.crashed = True + else: + self.crashed = False + def get_update_value(self, update=False): if update: - self.update_value = self.update(self.update_value) + self.set_update_value() return self.update_value def run(self): while not self.shutdown_event.is_set(): start_time = monotonic() - try: - self.update_value = self.update(self.update_value) - except Exception as e: - self.exception('Exception while updating: {0}', str(e)) - self.skip = True - except KeyboardInterrupt: - self.warn('Caught keyboard interrupt while updating') - self.skip = True - else: - self.skip = False + self.set_update_value() self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) def shutdown(self): From 5111d9baf62588d05854724f8eab614ebd06d37d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 23 Feb 2014 12:21:38 +0400 Subject: [PATCH 0943/1472] If update_first is False make it first wait and then update Reason: otherwise it is likely that two updating processes are being run simultaneously. With the current code it is not impossible as well, but less likely. --- powerline/lib/threaded.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 8a259719..01cfc52e 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -53,13 +53,10 @@ class ThreadedSegment(MultiRunnedThread): # cursor”. # # If running once .update() is called in __call__. - update_value = self.get_update_value(update_first and self.update_first) self.start() - elif not self.updated: - update_value = self.get_update_value(True) - self.updated = True + update_value = self.get_update_value(self.do_update_first) else: - update_value = self.get_update_value() + update_value = self.get_update_value(not self.updated) if self.crashed: return self.crashed_value @@ -77,6 +74,7 @@ class ThreadedSegment(MultiRunnedThread): self.crashed = True else: self.crashed = False + self.updated = True def get_update_value(self, update=False): if update: @@ -84,10 +82,16 @@ class ThreadedSegment(MultiRunnedThread): return self.update_value def run(self): - while not self.shutdown_event.is_set(): + if self.do_update_first: start_time = monotonic() - self.set_update_value() - self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) + while not self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)): + start_time = monotonic() + self.set_update_value() + else: + while not self.shutdown_event.is_set(): + start_time = monotonic() + self.set_update_value() + self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) def shutdown(self): self.shutdown_event.set() @@ -107,7 +111,8 @@ class ThreadedSegment(MultiRunnedThread): def set_state(self, interval=None, update_first=True, shutdown_event=None, **kwargs): self.set_interval(interval) self.shutdown_event = shutdown_event or Event() - self.updated = self.updated or (not (update_first and self.update_first)) + self.do_update_first = update_first and self.update_first + self.updated = self.updated or (not self.do_update_first) def startup(self, pl, **kwargs): self.run_once = False From b1dba59170930ecb6cf8b59e302888d2a363ee58 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 23 Feb 2014 14:32:55 +0400 Subject: [PATCH 0944/1472] Do not recompute key --- powerline/lib/threaded.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 01cfc52e..0d4575c0 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -158,9 +158,10 @@ class KwThreadedSegment(ThreadedSegment): def key(**kwargs): return frozenset(kwargs.items()) - def render(self, update_value, update_first, **kwargs): + def render(self, update_value, update_first, key=None, **kwargs): queries, crashed = update_value - key = self.key(**kwargs) + if key is None: + key = self.key(**kwargs) if key in crashed: return self.crashed_value @@ -169,8 +170,8 @@ class KwThreadedSegment(ThreadedSegment): except KeyError: with self.write_lock: self.new_queries.append(key) - if update_first and self.update_first: - return self.render(update_value=self.get_update_value(True), update_first=False, **kwargs) + if self.do_update_first or self.run_once: + return self.render(update_value=self.get_update_value(True), update_first=False, key=key, **kwargs) else: update_state = None @@ -207,8 +208,9 @@ class KwThreadedSegment(ThreadedSegment): return update_value - def set_state(self, interval=None, shutdown_event=None, **kwargs): + def set_state(self, interval=None, update_first=True, shutdown_event=None, **kwargs): self.set_interval(interval) + self.do_update_first = update_first and self.update_first self.shutdown_event = shutdown_event or Event() @staticmethod From 68a6fd056cda20f35f15336bc28b7aec0652c9e0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 23 Feb 2014 14:35:11 +0400 Subject: [PATCH 0945/1472] Use monotonic() in place of time.time() --- tests/test_lib.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index f10ee293..0fb31430 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,11 +1,15 @@ # vim:fileencoding=utf-8:noet +from __future__ import division + from powerline.lib import mergedicts, add_divider_highlight_group from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess -from subprocess import call, PIPE +from powerline.lib.monotonic import monotonic import os import sys import re +from time import sleep +from subprocess import call, PIPE from functools import partial from tests import TestCase, SkipTest @@ -41,12 +45,11 @@ class TestLib(TestCase): class TestFilesystemWatchers(TestCase): def do_test_for_change(self, watcher, path): - import time - st = time.time() - while time.time() - st < 1: + st = monotonic() + while monotonic() - st < 1: if watcher(path): return - time.sleep(0.1) + sleep(0.1) self.fail('The change to {0} was not detected'.format(path)) def test_file_watcher(self): @@ -134,9 +137,8 @@ use_mercurial = use_bzr = sys.version_info < (3, 0) class TestVCS(TestCase): def do_branch_rename_test(self, repo, q): - import time - st = time.time() - while time.time() - st < 1: + st = monotonic() + while monotonic() - st < 1: # Give inotify time to deliver events ans = repo.branch() if hasattr(q, '__call__'): @@ -145,7 +147,7 @@ class TestVCS(TestCase): else: if ans == q: break - time.sleep(0.01) + sleep(0.01) if hasattr(q, '__call__'): self.assertTrue(q(ans)) else: From ba41cecb721e897f4474951220032211ccb19627 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 23 Feb 2014 14:36:08 +0400 Subject: [PATCH 0946/1472] Add powerline.lib.threaded tests --- tests/lib/__init__.py | 3 +- tests/test_lib.py | 331 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+), 1 deletion(-) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index c6ac15d3..8447ab2f 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -5,13 +5,14 @@ import sys class Pl(object): def __init__(self): + self.exceptions = [] self.errors = [] self.warns = [] self.debugs = [] self.prefix = None self.use_daemon_threads = True - for meth in ('error', 'warn', 'debug'): + for meth in ('error', 'warn', 'debug', 'exception'): exec (('def {0}(self, msg, *args, **kwargs):\n' ' self.{0}s.append((kwargs.get("prefix") or self.prefix, msg, args, kwargs))\n').format(meth)) diff --git a/tests/test_lib.py b/tests/test_lib.py index 0fb31430..d1735b65 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -4,7 +4,9 @@ from __future__ import division from powerline.lib import mergedicts, add_divider_highlight_group from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic +import threading import os import sys import re @@ -12,6 +14,335 @@ from time import sleep from subprocess import call, PIPE from functools import partial from tests import TestCase, SkipTest +from tests.lib import Pl + + +def thread_number(): + return len(threading.enumerate()) + + +class TestThreaded(TestCase): + def test_threaded_segment(self): + log = [] + pl = Pl() + updates = [(None,)] + lock = threading.Lock() + event = threading.Event() + block_event = threading.Event() + + class TestSegment(ThreadedSegment): + interval = 10 + + def set_state(self, **kwargs): + event.clear() + log.append(('set_state', kwargs)) + return super(TestSegment, self).set_state(**kwargs) + + def update(self, update_value): + block_event.wait() + event.set() + # Make sleep first to prevent some race conditions + log.append(('update', update_value)) + with lock: + ret = updates[0] + if isinstance(ret, Exception): + raise ret + else: + return ret[0] + + def render(self, update, **kwargs): + log.append(('render', update, kwargs)) + if isinstance(update, Exception): + raise update + else: + return update + + # Non-threaded tests + segment = TestSegment() + block_event.set() + updates[0] = (None,) + self.assertEqual(segment(pl=pl), None) + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('set_state', {}), + ('update', None), + ('render', None, {'pl': pl, 'update_first': True}), + ]) + log[:] = () + + segment = TestSegment() + block_event.set() + updates[0] = ('abc',) + self.assertEqual(segment(pl=pl), 'abc') + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('set_state', {}), + ('update', None), + ('render', 'abc', {'pl': pl, 'update_first': True}), + ]) + log[:] = () + + segment = TestSegment() + block_event.set() + updates[0] = ('abc',) + self.assertEqual(segment(pl=pl, update_first=False), 'abc') + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('set_state', {}), + ('update', None), + ('render', 'abc', {'pl': pl, 'update_first': False}), + ]) + log[:] = () + + segment = TestSegment() + block_event.set() + updates[0] = ValueError('abc') + self.assertEqual(segment(pl=pl), None) + self.assertEqual(thread_number(), 1) + self.assertEqual(len(pl.exceptions), 1) + self.assertEqual(log, [ + ('set_state', {}), + ('update', None), + ]) + log[:] = () + pl.exceptions[:] = () + + segment = TestSegment() + block_event.set() + updates[0] = (TypeError('def'),) + self.assertRaises(TypeError, segment, pl=pl) + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('set_state', {}), + ('update', None), + ('render', updates[0][0], {'pl': pl, 'update_first': True}), + ]) + log[:] = () + + # Threaded tests + segment = TestSegment() + block_event.clear() + kwargs = {'pl': pl, 'update_first': False, 'other': 1} + with lock: + updates[0] = ('abc',) + segment.startup(**kwargs) + ret = segment(**kwargs) + self.assertEqual(thread_number(), 2) + block_event.set() + event.wait() + segment.shutdown_event.set() + segment.thread.join() + self.assertEqual(ret, None) + self.assertEqual(log, [ + ('set_state', {'update_first': False, 'other': 1}), + ('render', None, {'pl': pl, 'update_first': False, 'other': 1}), + ('update', None), + ]) + log[:] = () + + segment = TestSegment() + block_event.set() + kwargs = {'pl': pl, 'update_first': True, 'other': 1} + with lock: + updates[0] = ('def',) + segment.startup(**kwargs) + ret = segment(**kwargs) + self.assertEqual(thread_number(), 2) + segment.shutdown_event.set() + segment.thread.join() + self.assertEqual(ret, 'def') + self.assertEqual(log, [ + ('set_state', {'update_first': True, 'other': 1}), + ('update', None), + ('render', 'def', {'pl': pl, 'update_first': True, 'other': 1}), + ]) + log[:] = () + + segment = TestSegment() + block_event.set() + kwargs = {'pl': pl, 'update_first': True, 'interval': 0.2} + with lock: + updates[0] = ('abc',) + segment.startup(**kwargs) + start = monotonic() + ret1 = segment(**kwargs) + with lock: + updates[0] = ('def',) + self.assertEqual(thread_number(), 2) + sleep(0.5) + ret2 = segment(**kwargs) + segment.shutdown_event.set() + segment.thread.join() + end = monotonic() + duration = end - start + self.assertEqual(ret1, 'abc') + self.assertEqual(ret2, 'def') + self.assertEqual(log[:5], [ + ('set_state', {'update_first': True, 'interval': 0.2}), + ('update', None), + ('render', 'abc', {'pl': pl, 'update_first': True, 'interval': 0.2}), + ('update', 'abc'), + ('update', 'def'), + ]) + num_runs = len([e for e in log if e[0] == 'update']) + self.assertAlmostEqual(duration / 0.2, num_runs, delta=1) + log[:] = () + + segment = TestSegment() + block_event.set() + kwargs = {'pl': pl, 'update_first': True, 'interval': 0.2} + with lock: + updates[0] = ('ghi',) + segment.startup(**kwargs) + start = monotonic() + ret1 = segment(**kwargs) + with lock: + updates[0] = TypeError('jkl') + self.assertEqual(thread_number(), 2) + sleep(0.5) + ret2 = segment(**kwargs) + segment.shutdown_event.set() + segment.thread.join() + end = monotonic() + duration = end - start + self.assertEqual(ret1, 'ghi') + self.assertEqual(ret2, None) + self.assertEqual(log[:5], [ + ('set_state', {'update_first': True, 'interval': 0.2}), + ('update', None), + ('render', 'ghi', {'pl': pl, 'update_first': True, 'interval': 0.2}), + ('update', 'ghi'), + ('update', 'ghi'), + ]) + num_runs = len([e for e in log if e[0] == 'update']) + self.assertAlmostEqual(duration / 0.2, num_runs, delta=1) + self.assertEqual(num_runs - 1, len(pl.exceptions)) + log[:] = () + + def test_kw_threaded_segment(self): + log = [] + pl = Pl() + lock = threading.Lock() + event = threading.Event() + + class TestSegment(KwThreadedSegment): + interval = 10 + + @staticmethod + def key(_key=(None,), **kwargs): + log.append(('key', _key, kwargs)) + return _key + + def compute_state(self, key): + event.set() + sleep(0.1) + log.append(('compute_state', key)) + with lock: + ret = key + if isinstance(ret, Exception): + raise ret + else: + return ret[0] + + def render_one(self, state, **kwargs): + log.append(('render_one', state, kwargs)) + if isinstance(state, Exception): + raise state + else: + return state + + # Non-threaded tests + segment = TestSegment() + event.clear() + self.assertEqual(segment(pl=pl), None) + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('key', (None,), {'pl': pl}), + ('compute_state', (None,)), + ('render_one', None, {'pl': pl}), + ]) + log[:] = () + + segment = TestSegment() + kwargs = {'pl': pl, '_key': ('abc',), 'update_first': False} + event.clear() + self.assertEqual(segment(**kwargs), 'abc') + kwargs.update(_key=('def',)) + self.assertEqual(segment(**kwargs), 'def') + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('key', ('abc',), {'pl': pl}), + ('compute_state', ('abc',)), + ('render_one', 'abc', {'pl': pl, '_key': ('abc',)}), + ('key', ('def',), {'pl': pl}), + ('compute_state', ('def',)), + ('render_one', 'def', {'pl': pl, '_key': ('def',)}), + ]) + log[:] = () + + segment = TestSegment() + kwargs = {'pl': pl, '_key': ValueError('xyz'), 'update_first': False} + event.clear() + self.assertEqual(segment(**kwargs), None) + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('key', kwargs['_key'], {'pl': pl}), + ('compute_state', kwargs['_key']), + ]) + log[:] = () + + segment = TestSegment() + kwargs = {'pl': pl, '_key': (ValueError('abc'),), 'update_first': False} + event.clear() + self.assertRaises(ValueError, segment, **kwargs) + self.assertEqual(thread_number(), 1) + self.assertEqual(log, [ + ('key', kwargs['_key'], {'pl': pl}), + ('compute_state', kwargs['_key']), + ('render_one', kwargs['_key'][0], {'pl': pl, '_key': kwargs['_key']}), + ]) + log[:] = () + + # Threaded tests + segment = TestSegment() + kwargs = {'pl': pl, 'update_first': False, '_key': ('_abc',)} + event.clear() + segment.startup(**kwargs) + ret = segment(**kwargs) + self.assertEqual(thread_number(), 2) + segment.shutdown_event.set() + segment.thread.join() + self.assertEqual(ret, None) + self.assertEqual(log[:2], [ + ('key', kwargs['_key'], {'pl': pl}), + ('render_one', None, {'pl': pl, '_key': kwargs['_key']}), + ]) + self.assertLessEqual(len(log), 3) + if len(log) > 2: + self.assertEqual(log[2], ('compute_state', kwargs['_key'])) + log[:] = () + + segment = TestSegment() + kwargs = {'pl': pl, 'update_first': True, '_key': ('_abc',)} + event.clear() + segment.startup(**kwargs) + ret1 = segment(**kwargs) + kwargs.update(_key=('_def',)) + ret2 = segment(**kwargs) + self.assertEqual(thread_number(), 2) + segment.shutdown_event.set() + segment.thread.join() + self.assertEqual(ret1, '_abc') + self.assertEqual(ret2, '_def') + self.assertEqual(log, [ + ('key', ('_abc',), {'pl': pl}), + ('compute_state', ('_abc',)), + ('render_one', '_abc', {'pl': pl, '_key': ('_abc',)}), + ('key', ('_def',), {'pl': pl}), + ('compute_state', ('_def',)), + ('render_one', '_def', {'pl': pl, '_key': ('_def',)}), + ]) + log[:] = () class TestLib(TestCase): From e1eecea36df30c1883c9ae6f4135d724da3c6bc2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 23 Feb 2014 15:34:15 +0400 Subject: [PATCH 0947/1472] Do not use drop_interval --- powerline/lib/threaded.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 0d4575c0..d8f47e71 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -144,7 +144,6 @@ class ThreadedSegment(MultiRunnedThread): class KwThreadedSegment(ThreadedSegment): - drop_interval = 10 * 60 update_first = True def __init__(self): @@ -198,7 +197,7 @@ class KwThreadedSegment(ThreadedSegment): self.new_queries = [] for key, (last_query_time, state) in queries.items(): - if last_query_time < monotonic() < last_query_time + self.drop_interval: + if last_query_time < monotonic() < last_query_time + self.interval: updates[key] = (last_query_time, state) else: self.update_one(crashed, updates, key) From c08346000b2dc8d9130353d5a1b0982f2529e77d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 23 Feb 2014 15:37:13 +0400 Subject: [PATCH 0948/1472] Add after_update argument to prevent infinite recursion Just in case. --- powerline/lib/threaded.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index d8f47e71..cae9c991 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -157,7 +157,7 @@ class KwThreadedSegment(ThreadedSegment): def key(**kwargs): return frozenset(kwargs.items()) - def render(self, update_value, update_first, key=None, **kwargs): + def render(self, update_value, update_first, key=None, after_update=False, **kwargs): queries, crashed = update_value if key is None: key = self.key(**kwargs) @@ -170,7 +170,17 @@ class KwThreadedSegment(ThreadedSegment): with self.write_lock: self.new_queries.append(key) if self.do_update_first or self.run_once: - return self.render(update_value=self.get_update_value(True), update_first=False, key=key, **kwargs) + if after_update: + self.error('internal error: value was not computed even though update_first was set') + update_state = None + else: + return self.render( + update_value=self.get_update_value(True), + update_first=False, + key=key, + after_update=True, + **kwargs + ) else: update_state = None From acff89a8e92fbaca9d5a97d6099eacb3bfd61318 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Feb 2014 08:15:26 +0400 Subject: [PATCH 0949/1472] Remove useless lock --- tests/test_lib.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index d1735b65..f4da57d2 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -222,7 +222,6 @@ class TestThreaded(TestCase): def test_kw_threaded_segment(self): log = [] pl = Pl() - lock = threading.Lock() event = threading.Event() class TestSegment(KwThreadedSegment): @@ -237,8 +236,7 @@ class TestThreaded(TestCase): event.set() sleep(0.1) log.append(('compute_state', key)) - with lock: - ret = key + ret = key if isinstance(ret, Exception): raise ret else: From 7df8918b39e0b8e8469aa0ca98b1fb85a86c4f6b Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Feb 2014 08:24:56 +0400 Subject: [PATCH 0950/1472] Make code compatible with python-2.6 In python 2.6 Event.wait() always returns None making condition always True. --- powerline/lib/threaded.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index cae9c991..b253ae8f 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -84,7 +84,10 @@ class ThreadedSegment(MultiRunnedThread): def run(self): if self.do_update_first: start_time = monotonic() - while not self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)): + while True: + self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) + if self.shutdown_event.is_set(): + break start_time = monotonic() self.set_update_value() else: From ecf26dfbc65e0c39001e8283e776c50899f90e1b Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 19:38:05 +0400 Subject: [PATCH 0951/1472] Update powerline for new psutil Fixes #835 --- powerline/segments/common.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 01c18a20..06f3d563 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -517,8 +517,14 @@ try: if data: yield interface, data.bytes_recv, data.bytes_sent - def _get_user(segment_info): - return psutil.Process(os.getpid()).username + # Pre psutil-2.0.0: psutil.Process.username has type property + if callable(psutil.Process.username): + def _get_user(segment_info): + return psutil.Process(os.getpid()).username() + # psutil-2.0.0: psutil.Process.username is unbound method + else: + def _get_user(segment_info): + return psutil.Process(os.getpid()).username class CPULoadPercentSegment(ThreadedSegment): interval = 1 From 678d47c61e455666fea0a3d0d2ed8fa60b926feb Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 19:41:59 +0400 Subject: [PATCH 0952/1472] Switch comments --- powerline/segments/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 06f3d563..56a51f6c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -517,11 +517,11 @@ try: if data: yield interface, data.bytes_recv, data.bytes_sent - # Pre psutil-2.0.0: psutil.Process.username has type property + # psutil-2.0.0: psutil.Process.username is unbound method if callable(psutil.Process.username): def _get_user(segment_info): return psutil.Process(os.getpid()).username() - # psutil-2.0.0: psutil.Process.username is unbound method + # pre psutil-2.0.0: psutil.Process.username has type property else: def _get_user(segment_info): return psutil.Process(os.getpid()).username From 55858e1a9d651018369453de6f72b658e2cffbe4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 20:05:52 +0400 Subject: [PATCH 0953/1472] Fix tests for new psutil --- tests/test_segments.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index 48fde0c2..bfea4f6a 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -169,7 +169,18 @@ class TestCommon(TestCase): def test_user(self): new_os = new_module('os', getpid=lambda: 1) - new_psutil = new_module('psutil', Process=lambda pid: Args(username='def')) + + class Process(object): + def __init__(self, pid): + pass + + def username(self): + return 'def' + + if hasattr(common, 'psutil') and not callable(common.psutil.Process.username): + username = property(username) + + new_psutil = new_module('psutil', Process=Process) pl = Pl() with replace_env('USER', 'def') as segment_info: common.username = False From 2b5b6af002f8fb73a8412e7b951a5b87d2c0b9c0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 20:35:20 +0400 Subject: [PATCH 0954/1472] Put _powerline_prompt at the start Reason: $? may be overridden by the previous commands in PROMPT_COMMAND that do not keep it like _powerline_prompt does (see `return $last_exit_code`). (@ascrane) Closes #836 --- powerline/bindings/bash/powerline.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index ca9e1792..c5d56e7c 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -47,7 +47,7 @@ _powerline_prompt() { return $last_exit_code } -[[ "$PROMPT_COMMAND" != "${PROMPT_COMMAND/_powerline_prompt/}" ]] || - export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n'"_powerline_prompt" +test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_prompt*}" || + export PROMPT_COMMAND=$'_powerline_prompt\n'"${PROMPT_COMMAND}" _powerline_init_tmux_support From c3b4654bfb499eb51394ada64fac66af7041724e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Feb 2014 08:49:25 +0400 Subject: [PATCH 0955/1472] Fix flake8 complaints --- powerline/bindings/vim/__init__.py | 1 + powerline/lib/file_watcher.py | 3 ++- powerline/lib/humanize_bytes.py | 6 ++--- powerline/lib/tree_watcher.py | 10 ++++---- powerline/lib/vcs/__init__.py | 37 ++++++++++++++++++++++-------- powerline/lib/vcs/bzr.py | 11 +++++---- powerline/lib/vcs/git.py | 8 ++++++- powerline/lib/vcs/mercurial.py | 2 ++ powerline/renderer.py | 9 ++++---- powerline/renderers/vim.py | 12 +++++----- powerline/segments/shell.py | 2 +- powerline/segments/vim.py | 5 ++-- powerline/theme.py | 2 +- tests/test_cmdline.py | 4 +++- tests/test_segments.py | 9 ++++---- tests/test_shells/postproc.py | 4 ++-- tests/vim.py | 5 ++-- tools/generate_gradients.py | 4 ++-- 18 files changed, 85 insertions(+), 49 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index d618a089..4a17565e 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -129,6 +129,7 @@ if sys.version_info < (3,): return buf.name else: vim_bufname = vim_get_func('bufname') + def buffer_name(buf): # NOQA try: name = buf.name diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 41e20362..21524e3b 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -13,11 +13,12 @@ from threading import RLock from powerline.lib.monotonic import monotonic from powerline.lib.inotify import INotify, INotifyError + def realpath(path): return os.path.abspath(os.path.realpath(path)) -class INotifyWatch(INotify): +class INotifyWatch(INotify): is_stat_based = False def __init__(self, expire_time=10): diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index 769c7d13..570d803e 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -17,6 +17,6 @@ def humanize_bytes(num, suffix='B', si_prefix=False): unit, decimals = unit_list[exponent] if unit and not si_prefix: unit = unit.upper() + 'i' - return '{{quotient:.{decimals}f}} {{unit}}{{suffix}}'\ - .format(decimals=decimals)\ - .format(quotient=quotient, unit=unit, suffix=suffix) + return ('{{quotient:.{decimals}f}} {{unit}}{{suffix}}' + .format(decimals=decimals) + .format(quotient=quotient, unit=unit, suffix=suffix)) diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index f283e10b..608e2946 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -16,19 +16,21 @@ from powerline.lib.inotify import INotify, INotifyError class NoSuchDir(ValueError): pass + class BaseDirChanged(ValueError): pass -class DirTooLarge(ValueError): +class DirTooLarge(ValueError): def __init__(self, bdir): ValueError.__init__(self, 'The directory {0} is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'.format(bdir)) + def realpath(path): return os.path.abspath(os.path.realpath(path)) -class INotifyTreeWatcher(INotify): +class INotifyTreeWatcher(INotify): is_dummy = False def __init__(self, basedir, ignore_event=None): @@ -141,7 +143,6 @@ class INotifyTreeWatcher(INotify): class DummyTreeWatcher(object): - is_dummy = True def __init__(self, basedir): @@ -150,8 +151,8 @@ class DummyTreeWatcher(object): def __call__(self): return False -class TreeWatcher(object): +class TreeWatcher(object): def __init__(self, expire_time=10): self.watches = {} self.last_query_times = {} @@ -203,6 +204,7 @@ class TreeWatcher(object): self.watches[path] = DummyTreeWatcher(path) return False + if __name__ == '__main__': w = INotifyTreeWatcher(sys.argv[-1]) w() diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 5fa066fe..ff971c5d 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,16 +1,11 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import -import os, errno +import os +import errno from threading import Lock from collections import defaultdict -vcs_props = ( - ('git', '.git', os.path.exists), - ('mercurial', '.hg', os.path.isdir), - ('bzr', '.bzr', os.path.isdir), -) - def generate_directories(path): if os.path.isdir(path): @@ -24,8 +19,10 @@ def generate_directories(path): break yield path + _file_watcher = None + def file_watcher(): global _file_watcher if _file_watcher is None: @@ -33,8 +30,10 @@ def file_watcher(): _file_watcher = create_file_watcher() return _file_watcher + _branch_watcher = None + def branch_watcher(): global _branch_watcher if _branch_watcher is None: @@ -42,10 +41,12 @@ def branch_watcher(): _branch_watcher = create_file_watcher() return _branch_watcher + branch_name_cache = {} branch_lock = Lock() file_status_lock = Lock() + def get_branch_name(directory, config_file, get_func): global branch_name_cache with branch_lock: @@ -79,8 +80,8 @@ def get_branch_name(directory, config_file, get_func): branch_name_cache[config_file] = get_func(directory, config_file) return branch_name_cache[config_file] -class FileStatusCache(dict): +class FileStatusCache(dict): def __init__(self): self.dirstate_map = defaultdict(set) self.ignore_map = defaultdict(set) @@ -112,8 +113,10 @@ class FileStatusCache(dict): for ignf in self.keypath_ignore_map[keypath]: yield ignf + file_status_cache = FileStatusCache() + def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_func, extra_ignore_files=()): global file_status_cache keypath = file_path if os.path.isabs(file_path) else os.path.join(directory, file_path) @@ -175,8 +178,8 @@ def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_f file_status_cache[keypath] = ans = get_func(directory, file_path) return ans -class TreeStatusCache(dict): +class TreeStatusCache(dict): def __init__(self): from powerline.lib.tree_watcher import TreeWatcher self.tw = TreeWatcher() @@ -196,14 +199,24 @@ class TreeStatusCache(dict): logger.warn('Failed to check %s for changes, with error: %s'% key, e) return self.cache_and_get(key, repo.status) + _tree_status_cache = None + def tree_status(repo, logger): global _tree_status_cache if _tree_status_cache is None: _tree_status_cache = TreeStatusCache() return _tree_status_cache(repo, logger) + +vcs_props = ( + ('git', '.git', os.path.exists), + ('mercurial', '.hg', os.path.isdir), + ('bzr', '.bzr', os.path.isdir), +) + + def guess(path): for directory in generate_directories(path): for vcs, vcs_dir, check in vcs_props: @@ -219,8 +232,12 @@ def guess(path): pass return None + def debug(): - ''' To use run python -c "from powerline.lib.vcs import debug; debug()" some_file_to_watch ''' + '''Test run guess(), repo.branch() and repo.status() + + To use run python -c "from powerline.lib.vcs import debug; debug()" + some_file_to_watch ''' import sys dest = sys.argv[-1] repo = guess(os.path.abspath(dest)) diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index 601546a5..9734572b 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -10,16 +10,17 @@ from bzrlib import (workingtree, status, library_state, trace, ui) from powerline.lib.vcs import get_branch_name, get_file_status + class CoerceIO(StringIO): def write(self, arg): if isinstance(arg, bytes): arg = arg.decode('utf-8', 'replace') return super(CoerceIO, self).write(arg) -state = None nick_pat = re.compile(br'nickname\s*=\s*(.+)') + def branch_name_from_config_file(directory, config_file): ans = None try: @@ -33,8 +34,11 @@ def branch_name_from_config_file(directory, config_file): pass return ans or os.path.basename(directory) -class Repository(object): +state = None + + +class Repository(object): def __init__(self, directory): if isinstance(directory, bytes): directory = directory.decode(sys.getfilesystemencoding() or sys.getdefaultencoding() or 'utf-8') @@ -75,7 +79,7 @@ class Repository(object): return if path: ans = raw[:2] - if ans == 'I ': # Ignored + if ans == 'I ': # Ignored ans = None return ans dirtied = untracked = ' ' @@ -90,4 +94,3 @@ class Repository(object): def branch(self): config_file = os.path.join(self.directory, '.bzr', 'branch', 'branch.conf') return get_branch_name(self.directory, config_file, branch_name_from_config_file) - diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index b20cafb9..08edcd7f 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -2,12 +2,13 @@ import os import re -import errno from powerline.lib.vcs import get_branch_name as _get_branch_name, get_file_status + _ref_pat = re.compile(br'ref:\s*refs/heads/(.+)') + def branch_name_from_config_file(directory, config_file): try: with open(config_file, 'rb') as f: @@ -19,6 +20,7 @@ def branch_name_from_config_file(directory, config_file): return m.group(1).decode('utf-8', 'replace') return raw[:7] + def git_directory(directory): path = os.path.join(directory, '.git') if os.path.isfile(path): @@ -28,10 +30,12 @@ def git_directory(directory): else: return path + def get_branch_name(base_dir): head = os.path.join(git_directory(base_dir), 'HEAD') return _get_branch_name(base_dir, head, branch_name_from_config_file) + def do_status(directory, path, func): if path: gitd = git_directory(directory) @@ -43,12 +47,14 @@ def do_status(directory, path, func): path, '.gitignore', func, extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('logs/HEAD', 'info/exclude'))) return func(directory, path) + def ignore_event(path, name): # Ignore changes to the index.lock file, since they happen frequently and # dont indicate an actual change in the working tree status return False return path.endswith('.git') and name == 'index.lock' + try: import pygit2 as git diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index ade06282..2aa890cd 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -7,6 +7,7 @@ from mercurial import hg, ui, match from powerline.lib.vcs import get_branch_name, get_file_status + def branch_name_from_config_file(directory, config_file): try: with open(config_file, 'rb') as f: @@ -15,6 +16,7 @@ def branch_name_from_config_file(directory, config_file): except Exception: return 'default' + class Repository(object): __slots__ = ('directory', 'ui') diff --git a/powerline/renderer.py b/powerline/renderer.py index 256d11f0..7445ff0f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -14,6 +14,7 @@ try: except ImportError: pass + def construct_returned_value(rendered_highlighted, segments, output_raw): if output_raw: return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) @@ -70,7 +71,7 @@ class Renderer(object): character_translations = {ord(' '): NBSP} '''Character translations for use in escape() function. - + See documentation of ``unicode.translate`` for details. ''' @@ -111,7 +112,7 @@ class Renderer(object): def strwidth(self, string): '''Function that returns string width. - + Is used to calculate the place given string occupies when handling ``width`` argument to ``.render()`` method. Must take east asian width into account. @@ -125,7 +126,7 @@ class Renderer(object): def get_theme(self, matcher_info): '''Get Theme object. - + Is to be overridden by subclasses to support local themes, this variant only returns ``.theme`` attribute. @@ -152,7 +153,7 @@ class Renderer(object): def get_segment_info(self, segment_info, mode): '''Get segment information. - + Must return a dictionary containing at least ``home``, ``environ`` and ``getcwd`` keys (see documentation for ``segment_info`` attribute). This implementation merges ``segment_info`` dictionary passed to diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 6065dbbf..14496a1e 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -140,12 +140,12 @@ class VimRenderer(Renderer): hl_group['attr'].append('italic') if attr & ATTR_UNDERLINE: hl_group['attr'].append('underline') - hl_group['name'] = 'Pl_' + \ - str(hl_group['ctermfg']) + '_' + \ - str(hl_group['guifg']) + '_' + \ - str(hl_group['ctermbg']) + '_' + \ - str(hl_group['guibg']) + '_' + \ - ''.join(hl_group['attr']) + hl_group['name'] = ('Pl_' + + str(hl_group['ctermfg']) + '_' + + str(hl_group['guifg']) + '_' + + str(hl_group['ctermbg']) + '_' + + str(hl_group['guibg']) + '_' + + ''.join(hl_group['attr'])) self.hl_groups[(fg, bg, attr)] = hl_group vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( group=hl_group['name'], diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 750c6f25..8afc20a1 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -46,7 +46,7 @@ def last_pipe_status(pl, segment_info): @requires_segment_info def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, default=None): '''Return the current mode. - + :param dict override: dict for overriding mode strings. :param str default: diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index ac9552d6..9161c729 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -161,7 +161,6 @@ def file_directory(pl, segment_info, shorten_user=True, shorten_cwd=True, shorte name = buffer_name(segment_info['buffer']) if not name: return None - import sys file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') + (':.' if shorten_cwd else '') + ':h') if not file_directory: @@ -285,7 +284,7 @@ def line_percent(pl, segment_info, gradient=False): @window_cached -def position(pl, position_strings={'top':'Top', 'bottom':'Bot', 'all':'All'}, gradient=False): +def position(pl, position_strings={'top': 'Top', 'bottom': 'Bot', 'all': 'All'}, gradient=False): '''Return the position of the current view in the file as a percentage. :param dict position_strings: @@ -368,6 +367,7 @@ def modified_buffers(pl, text='+ ', join_str=','): return text + join_str.join(buffer_mod) return None + @requires_segment_info def branch(pl, segment_info, status_colors=False): '''Return the current working branch. @@ -395,6 +395,7 @@ def branch(pl, segment_info, status_colors=False): 'divider_highlight_group': 'branch:divider', }] + @requires_segment_info def file_vcs_status(pl, segment_info): '''Return the VCS status for this buffer. diff --git a/powerline/theme.py b/powerline/theme.py index 23b0fc6f..d6b185f2 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8:noet from powerline.segment import gen_segment_getter -from powerline.lib.unicode import u, unicode +from powerline.lib.unicode import u def requires_segment_info(func): diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index cc27d396..c330da36 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -18,9 +18,11 @@ class TestParser(TestCase): parser = get_argparser() out = StrIO() err = StrIO() + def flush(): out.truncate(0) err.truncate(0) + with replace_attr(sys, 'stdout', out, 'stderr', err): for raising_args, raising_reg in [ ([], 'too few arguments|the following arguments are required: ext'), @@ -69,7 +71,7 @@ class TestParser(TestCase): '-c', 'common.spaces=4', '-t', 'default.segment_data.hostname.before=H:', '-p', '.', - '-R', 'smth={"abc":"def"}' + '-R', 'smth={"abc":"def"}', ], { 'ext': ['shell'], 'side': 'left', diff --git a/tests/test_segments.py b/tests/test_segments.py index bfea4f6a..97077658 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -476,8 +476,8 @@ class TestCommon(TestCase): def test_environment(self): pl = Pl() - variable = 'FOO'; - value = 'bar'; + variable = 'FOO' + value = 'bar' with replace_env(variable, value) as segment_info: self.assertEqual(common.environment(pl=pl, segment_info=segment_info, variable=variable), value) segment_info['environ'].pop(variable) @@ -542,6 +542,7 @@ class TestCommon(TestCase): } ]) + class TestVim(TestCase): def test_mode(self): pl = Pl() @@ -659,10 +660,10 @@ class TestVim(TestCase): vim_module._set_cursor(0, 0) self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'Top') vim_module._set_cursor(97, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top':'Comienzo', 'bottom':'Final', 'all':'Todo'}), 'Final') + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Final') segment_info['buffer'][0:-1] = [str(i) for i in range(2)] vim_module._set_cursor(0, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top':'Comienzo', 'bottom':'Final', 'all':'Todo'}), 'Todo') + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Todo') self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [{'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0}]) finally: diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 2da1fbc7..8d85917c 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -38,14 +38,14 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: try: start = line.index('\033[0;') end = line.index('\033[0m', start) - line = line[start : end+4] + '\n' + line = line[start:end + 4] + '\n' except ValueError: line = '' elif shell == 'tcsh': try: start = line.index('\033[0;') end = line.index(' ', start) - line = line[start : end] + '\033[0m\n' + line = line[start:end] + '\033[0m\n' except ValueError: line = '' W.write(line) diff --git a/tests/vim.py b/tests/vim.py index 56efbbb1..f447f490 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -307,9 +307,9 @@ def _emul_line(expr): cursorline = windows[_window - 1].cursor[0] + 1 numlines = len(_buf_lines[_buffer()]) if expr == 'w0': - return max(cursorline-5, 1) + return max(cursorline - 5, 1) if expr == 'w$': - return min(cursorline+5, numlines) + return min(cursorline + 5, numlines) raise NotImplementedError @@ -401,7 +401,6 @@ class _Buffer(object): import os if type(name) is not bytes: name = name.encode('utf-8') - import sys self._name = os.path.abspath(name) def __getitem__(self, line): diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 6bfe2cce..80adae15 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -138,8 +138,8 @@ palettes = { '256': (cterm_to_hex, lambda c: c), None: (cterm_to_hex[16:], lambda c: c + 16), } -r = [get_rgb(*color) for color in gradient] -r2 = [find_color(color, *palettes[args.palette])[0] for color in gradient] +r = [get_rgb(*col) for col in gradient] +r2 = [find_color(col, *palettes[args.palette])[0] for col in gradient] r3 = [i[0] for i in groupby(r2)] print(json.dumps(r)) print(json.dumps(r2)) From 3f1e621f1d2008bd46561f702ce5fdc657560380 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 20:47:06 +0400 Subject: [PATCH 0956/1472] Add ellipsis argument to cwd segment --- powerline/segments/common.py | 14 ++++++++++---- tests/test_segments.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 56a51f6c..8bff0abd 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -75,17 +75,22 @@ def branch(pl, segment_info, status_colors=False): @requires_segment_info -def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_separator=False): +def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_separator=False, ellipsis='⋯'): '''Return the current working directory. Returns a segment list to create a breadcrumb-like effect. :param int dir_shorten_len: - shorten parent directory names to this length (e.g. :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) + shorten parent directory names to this length (e.g. + :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) :param int dir_limit_depth: - limit directory depth to this number (e.g. :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) + limit directory depth to this number (e.g. + :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) :param bool use_path_separator: Use path separator in place of soft divider. + :param str ellipsis: + Specifies what to use in place of omitted directories. Use None to not + show this subsegment at all. Divider highlight group used: ``cwd:divider``. @@ -110,7 +115,8 @@ def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_s cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: del(cwd[0:-dir_limit_depth]) - cwd.insert(0, '⋯') + if ellipsis is not None: + cwd.insert(0, ellipsis) ret = [] if not cwd[0]: cwd[0] = '/' diff --git a/tests/test_segments.py b/tests/test_segments.py index 97077658..d730e602 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -257,10 +257,24 @@ class TestCommon(TestCase): {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis='...'), [ + {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis=None), [ + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True), [ {'contents': '⋯/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis='...'), [ + {'contents': '.../', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis=None), [ + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [ {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'fo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, From 3cf3fb8f8e37a6954bfa0d66c8baf672d152d825 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 20:48:19 +0400 Subject: [PATCH 0957/1472] Document istime date argument --- powerline/segments/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 8bff0abd..a5782799 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -144,6 +144,8 @@ def date(pl, format='%Y-%m-%d', istime=False): :param str format: strftime-style date format string + :param bool istime: + If true then segment uses ``time`` highlight group. Divider highlight group used: ``time:divider``. From e51d7a9dd3e099a95243d12a2689823ba057b300 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 20:54:07 +0400 Subject: [PATCH 0958/1472] Add fuzzy_time(unicode_text) argument --- powerline/segments/common.py | 24 ++++++++++++++++++++---- tests/test_segments.py | 8 ++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index a5782799..043a7bb1 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -158,8 +158,19 @@ def date(pl, format='%Y-%m-%d', istime=False): }] -def fuzzy_time(pl): - '''Display the current time as fuzzy time, e.g. "quarter past six".''' +UNICODE_TEXT_TRANSLATION = { + ord('\''): '’', + ord('-'): '‐', +} + + +def fuzzy_time(pl, unicode_text=True): + '''Display the current time as fuzzy time, e.g. "quarter past six". + + :param bool unicode_text: + If true then hyphenminuses (regular ASCII ``-``) and single quotes are + replaced with unicode dashes and apostrophes. + ''' hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'] minute_str = { 5: 'five past', @@ -202,10 +213,15 @@ def fuzzy_time(pl): minute = int(round(now.minute / 5.0) * 5) if minute == 60 or minute == 0: - return ' '.join([hour, 'o\'clock']) + result = ' '.join([hour, 'o\'clock']) else: minute = minute_str[minute] - return ' '.join([minute, hour]) + result = ' '.join([minute, hour]) + + if unicode_text: + result = result.translate(UNICODE_TEXT_TRANSLATION) + + return result def _external_ip(query_url='http://ipv4.icanhazip.com/'): diff --git a/tests/test_segments.py b/tests/test_segments.py index d730e602..ddd264f2 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -326,9 +326,13 @@ class TestCommon(TestCase): time.minute = 59 self.assertEqual(common.fuzzy_time(pl=pl), 'round about midnight') time.minute = 33 - self.assertEqual(common.fuzzy_time(pl=pl), 'twenty-five to twelve') + self.assertEqual(common.fuzzy_time(pl=pl), 'twenty‐five to twelve') time.minute = 60 - self.assertEqual(common.fuzzy_time(pl=pl), 'twelve o\'clock') + self.assertEqual(common.fuzzy_time(pl=pl), 'twelve o’clock') + time.minute = 33 + self.assertEqual(common.fuzzy_time(pl=pl, unicode_text=False), 'twenty-five to twelve') + time.minute = 60 + self.assertEqual(common.fuzzy_time(pl=pl, unicode_text=False), 'twelve o\'clock') def test_external_ip(self): pl = Pl() From 94155405f0860d58cd41e14f5fed1406e9023237 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 20:55:00 +0400 Subject: [PATCH 0959/1472] Suggest URIs under query_url description --- powerline/segments/common.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 043a7bb1..6cfa07a7 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -247,15 +247,15 @@ class ExternalIpSegment(ThreadedSegment): external_ip = with_docstring(ExternalIpSegment(), '''Return external IP address. -Suggested URIs: - -* http://ipv4.icanhazip.com/ -* http://ipv6.icanhazip.com/ -* http://icanhazip.com/ (returns IPv6 address if available, else IPv4) - :param str query_url: URI to query for IP address, should return only the IP address as a text string + Suggested URIs: + + * http://ipv4.icanhazip.com/ + * http://ipv6.icanhazip.com/ + * http://icanhazip.com/ (returns IPv6 address if available, else IPv4) + Divider highlight group used: ``background:divider``. ''') From b4644ffd29e3c9621afd1438988441226e759345 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 21:14:20 +0400 Subject: [PATCH 0960/1472] Add fix for old vims Old vim versions did not support both unicode() and str() in python2 bindings in a number of places. Fixes #829 --- powerline/bindings/vim/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 4a17565e..5ee5225e 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -90,7 +90,7 @@ else: if hasattr(vim, 'options'): def vim_getbufoption(info, option): - return info['buffer'].options[option] + return info['buffer'].options[str(option)] else: def vim_getbufoption(info, option): # NOQA return getbufvar(info['bufnr'], '&' + option) From 993402fe6c96debcc5e6f8a6c49b68d787209b68 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 23:27:06 +0400 Subject: [PATCH 0961/1472] Do not use :redrawstatus!: it makes intro disappear It seems that old fix for old code is no longer needed. At least I do not see any problems with vim -u NONE -S ~/.vam/powerline/powerline/bindings/vim/plugin/powerline.vim --cmd 'set ls=2' . Fixes #250 --- powerline/vim.py | 1 - 1 file changed, 1 deletion(-) diff --git a/powerline/vim.py b/powerline/vim.py index b9cf5820..517bae82 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -181,7 +181,6 @@ def setup(pyeval=None, pycmd=None): # inside :execute)). vim.command is :execute equivalent regarding this case. vim.command('augroup Powerline') vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd)) - vim.command(' autocmd! VimEnter * :redrawstatus!') vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) vim.command('augroup END') From d8562256c1103fd218e98767b9b26f2a714d16d5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 13 Mar 2014 23:41:25 +0400 Subject: [PATCH 0962/1472] Do not allow unicode-related exceptions inside logger Fixes #421 --- powerline/__init__.py | 23 ++--------------------- powerline/lib/unicode.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 4df195dd..465680d6 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -7,33 +7,14 @@ import logging from powerline.colorscheme import Colorscheme from powerline.lib.config import ConfigLoader +from powerline.lib.unicode import safe_unicode from threading import Lock, Event -try: - from __builtin__ import unicode -except ImportError: - unicode = str # NOQA - DEFAULT_SYSTEM_CONFIG_DIR = None -def safe_unicode(s): - '''Return unicode instance without raising an exception. - ''' - try: - try: - return unicode(s) - except UnicodeDecodeError: - try: - return unicode(s, 'utf-8') - except TypeError: - return unicode(str(s), 'utf-8') - except Exception as e: - return safe_unicode(e) - - class FailedUnicode(unicode): '''Builtin ``unicode`` (``str`` in python 3) subclass indicating fatal error. @@ -69,7 +50,7 @@ class PowerlineLogger(object): prefix = self.ext + ((':' + prefix) if prefix else '') if args or kwargs: msg = msg.format(*args, **kwargs) - msg = prefix + ':' + msg + msg = prefix + ':' + safe_unicode(msg) key = attr + ':' + prefix if msg != self.last_msgs.get(key): getattr(self.logger, attr)(msg) diff --git a/powerline/lib/unicode.py b/powerline/lib/unicode.py index 645d4d7b..aa717fee 100644 --- a/powerline/lib/unicode.py +++ b/powerline/lib/unicode.py @@ -1,5 +1,9 @@ # vim:fileencoding=utf-8:noet + +from locale import getpreferredencoding + + try: from __builtin__ import unicode except ImportError: @@ -7,7 +11,35 @@ except ImportError: def u(s): + '''Return unicode instance assuming UTF-8 encoded string. + ''' if type(s) is unicode: return s else: return unicode(s, 'utf-8') + + +def safe_unicode(s): + '''Return unicode instance without raising an exception. + + Order of assumptions: + * ASCII string or unicode object + * UTF-8 string + * Object with __str__() or __repr__() method that returns UTF-8 string or + unicode object (depending on python version) + * String in locale.getpreferredencoding() encoding + * If everything failed use safe_unicode on last exception with which + everything failed + ''' + try: + try: + return unicode(s) + except UnicodeDecodeError: + try: + return unicode(s, 'utf-8') + except TypeError: + return unicode(str(s), 'utf-8') + except UnicodeDecodeError: + return unicode(s, getpreferredencoding()) + except Exception as e: + return safe_unicode(e) From 839038d58e410aeea1721581aae765d4d035593b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 15 Mar 2014 01:27:10 +0400 Subject: [PATCH 0963/1472] Also move FailedUnicode to powerline/lib/unicode --- powerline/__init__.py | 15 +-------------- powerline/lib/unicode.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 465680d6..c82163a9 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -7,7 +7,7 @@ import logging from powerline.colorscheme import Colorscheme from powerline.lib.config import ConfigLoader -from powerline.lib.unicode import safe_unicode +from powerline.lib.unicode import safe_unicode, FailedUnicode from threading import Lock, Event @@ -15,19 +15,6 @@ from threading import Lock, Event DEFAULT_SYSTEM_CONFIG_DIR = None -class FailedUnicode(unicode): - '''Builtin ``unicode`` (``str`` in python 3) subclass indicating fatal - error. - - If your code for some reason wants to determine whether `.render()` method - failed it should check returned string for being a FailedUnicode instance. - Alternatively you could subclass Powerline and override `.render()` method - to do what you like in place of catching the exception and returning - FailedUnicode. - ''' - pass - - def find_config_file(search_paths, config_file): config_file += '.json' for path in search_paths: diff --git a/powerline/lib/unicode.py b/powerline/lib/unicode.py index aa717fee..a1a5d567 100644 --- a/powerline/lib/unicode.py +++ b/powerline/lib/unicode.py @@ -43,3 +43,16 @@ def safe_unicode(s): return unicode(s, getpreferredencoding()) except Exception as e: return safe_unicode(e) + + +class FailedUnicode(unicode): + '''Builtin ``unicode`` (``str`` in python 3) subclass indicating fatal + error. + + If your code for some reason wants to determine whether `.render()` method + failed it should check returned string for being a FailedUnicode instance. + Alternatively you could subclass Powerline and override `.render()` method + to do what you like in place of catching the exception and returning + FailedUnicode. + ''' + pass From e094e3c5a905024593d599183daef50397e63c71 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Mar 2014 19:26:43 +0400 Subject: [PATCH 0964/1472] Add more Droid Sans Mono families Fixes #830 --- font/10-powerline-symbols.conf | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/font/10-powerline-symbols.conf b/font/10-powerline-symbols.conf index 2a0d7255..7e34a12a 100644 --- a/font/10-powerline-symbols.conf +++ b/font/10-powerline-symbols.conf @@ -10,6 +10,18 @@ Droid Sans Mono PowerlineSymbols + + Droid Sans Mono Slashed + PowerlineSymbols + + + Droid Sans Mono Dotted + PowerlineSymbols + + + DejaVu Sans Mono + PowerlineSymbols + DejaVu Sans Mono PowerlineSymbols From 36e1854018b965359a76ac6616cb16864666c276 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 21 Mar 2014 19:28:41 +0400 Subject: [PATCH 0965/1472] Fix tmux guard --- powerline/bindings/bash/powerline.sh | 3 +-- powerline/bindings/fish/powerline-setup.fish | 26 +++++++++++--------- powerline/bindings/tcsh/powerline.tcsh | 2 +- powerline/bindings/zsh/powerline.zsh | 3 +-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index c5d56e7c..4aeefea6 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -10,8 +10,7 @@ if test -z "${POWERLINE_COMMAND}" ; then fi _powerline_init_tmux_support() { - # Note: `test -w ""` returns false, so first condition may be removed - if test -n "$TMUX" && test -w "$TMUX" ; then + if test -n "$TMUX" && tmux source-file /dev/null &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one _POWERLINE_TMUX="$TMUX" diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 75ca19c4..71acb3cb 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -20,18 +20,20 @@ function powerline-setup " end _powerline_update - if test -w "$TMUX" - function _powerline_tmux_setenv - tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" - tmux refresh -S + if test -n "$TMUX" + if tmux source-file /dev/null ^/dev/null + function _powerline_tmux_setenv + tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" + tmux refresh -S + end + function --on-variable PWD _powerline_tmux_set_pwd + _powerline_tmux_setenv PWD "$PWD" + end + function --on-signal WINCH _powerline_tmux_set_columns + _powerline_tmux_setenv COLUMNS "$COLUMNS" + end + _powerline_tmux_set_columns + _powerline_tmux_set_pwd end - function --on-variable PWD _powerline_tmux_set_pwd - _powerline_tmux_setenv PWD "$PWD" - end - function --on-signal WINCH _powerline_tmux_set_columns - _powerline_tmux_setenv COLUMNS "$COLUMNS" - end - _powerline_tmux_set_columns - _powerline_tmux_set_pwd end end diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 44f969b6..b83498ad 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -14,7 +14,7 @@ if ! $?POWERLINE_COMMAND then setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline endif endif -alias _powerline_tmux_set_pwd 'if ( $?TMUX && { test -w $TMUX:q } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX && { test -w $TMUX:q } ) tmux refresh -S' +alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux source-file /dev/null >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX && { tmux source-file /dev/null >&/dev/null } ) tmux refresh -S' alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 6e8206fe..46123940 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -12,8 +12,7 @@ integer _POWERLINE_JOBNUM _powerline_init_tmux_support() { emulate -L zsh - # Note: `test -w ""` returns false, so first condition may be removed - if test -n "$TMUX" && test -w "$TMUX" ; then + if test -n "$TMUX" && tmux source-file /dev/null &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one typeset -g _POWERLINE_TMUX="$TMUX" From 45cc73aa8dd21f1f82b0536e9693b793e7953114 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 28 Mar 2014 08:56:04 +0400 Subject: [PATCH 0966/1472] =?UTF-8?q?Use=20=E2=80=9Ctmux=20refresh=20-S?= =?UTF-8?q?=E2=80=9D=20to=20check=20for=20tmux=20presence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason: tmux may be launched and $TMUX set, but current shell still not under tmux (and most likely there are no active session). In this case `tmux refresh -S` may fail with “no clients” message, which is annoying. --- powerline/bindings/bash/powerline.sh | 2 +- powerline/bindings/fish/powerline-setup.fish | 2 +- powerline/bindings/tcsh/powerline.tcsh | 2 +- powerline/bindings/zsh/powerline.zsh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 4aeefea6..6f1de23b 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -10,7 +10,7 @@ if test -z "${POWERLINE_COMMAND}" ; then fi _powerline_init_tmux_support() { - if test -n "$TMUX" && tmux source-file /dev/null &>/dev/null ; then + if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one _POWERLINE_TMUX="$TMUX" diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 71acb3cb..d925a6fc 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -21,7 +21,7 @@ function powerline-setup end _powerline_update if test -n "$TMUX" - if tmux source-file /dev/null ^/dev/null + if tmux refresh -S ^/dev/null function _powerline_tmux_setenv tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" tmux refresh -S diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index b83498ad..68e031e8 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -14,7 +14,7 @@ if ! $?POWERLINE_COMMAND then setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline endif endif -alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux source-file /dev/null >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX && { tmux source-file /dev/null >&/dev/null } ) tmux refresh -S' +alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 46123940..838a99a7 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -12,7 +12,7 @@ integer _POWERLINE_JOBNUM _powerline_init_tmux_support() { emulate -L zsh - if test -n "$TMUX" && tmux source-file /dev/null &>/dev/null ; then + if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one typeset -g _POWERLINE_TMUX="$TMUX" From 96d89ae81bcc772d4fd402c238877f1dcfae5412 Mon Sep 17 00:00:00 2001 From: Kjell Braden Date: Fri, 4 Apr 2014 13:11:37 +0200 Subject: [PATCH 0967/1472] fix default mode in zsh bindings --- powerline/bindings/zsh/powerline.zsh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 838a99a7..976190c1 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -75,12 +75,13 @@ _powerline_init_modes_support() { _powerline_set_true_keymap_name "$REPLY" } + _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select + _powerline_set_main_keymap_name + if [[ "$_POWERLINE_MODE" != vi* ]] ; then export _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" fi - _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select - _powerline_set_main_keymap_name precmd_functions+=( _powerline_set_main_keymap_name ) } From ba45a002dc96c6af6891e6119cd2f4d95f270b74 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 23 Apr 2014 06:31:42 +0400 Subject: [PATCH 0968/1472] Decode bytes before using os.path.join Otherwise it will raise TypeError when trying to join str() and bytes() instance in python-3.3. Fixes #654 Closes #655 --- powerline/lib/vcs/git.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 08edcd7f..d3d112fa 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -1,6 +1,9 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, absolute_import, print_function) + import os +import sys import re from powerline.lib.vcs import get_branch_name as _get_branch_name, get_file_status @@ -26,7 +29,7 @@ def git_directory(directory): if os.path.isfile(path): with open(path, 'rb') as f: raw = f.read().partition(b':')[2].strip() - return os.path.abspath(os.path.join(directory, raw)) + return os.path.abspath(os.path.join(directory, raw.decode(sys.getfilesystemencoding() or 'utf-8'))) else: return path From c603410843756c62c1573c3d75f5b0fe186db01c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 23 Apr 2014 06:36:24 +0400 Subject: [PATCH 0969/1472] Do not use .strip(), add two check git has --- powerline/lib/vcs/git.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index d3d112fa..77d1f387 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -28,8 +28,13 @@ def git_directory(directory): path = os.path.join(directory, '.git') if os.path.isfile(path): with open(path, 'rb') as f: - raw = f.read().partition(b':')[2].strip() - return os.path.abspath(os.path.join(directory, raw.decode(sys.getfilesystemencoding() or 'utf-8'))) + raw = f.read() + if not raw.startswith(b'gitdir: '): + raise IOError('invalid gitfile format') + raw = raw[8:].decode(sys.getfilesystemencoding() or 'utf-8') + if not raw: + raise IOError('no path in gitfile') + return os.path.abspath(os.path.join(directory, raw)) else: return path From 4d85e9a9bc83a0c49a59460b0bdaddb61c2828f8 Mon Sep 17 00:00:00 2001 From: Austin Beam Date: Thu, 1 May 2014 00:02:37 -0500 Subject: [PATCH 0970/1472] tmux: Change session block color if prefix pressed This modification to the tmux powerline binding allows the user to easily identify when the prefix has been sent. When the prefix has been sent, the session block changes from white to blue (matching the color of the active window). This is accomplished using the conditional functionality of tmux format strings and the 'client_prefix' format variable. Unfortunately, the tmux parser fails to properly parse out comma-separated format strings inside a format conditional, so those have been split out into individual segments as well. Attempting to re-combine the formats inside the conditionals will break this feature. --- powerline/bindings/tmux/powerline.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 22cc9b6c..367123f8 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -5,7 +5,7 @@ set -g status-interval 2 set -g status-fg colour231 set -g status-bg colour234 set -g status-left-length 20 -set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(eval $POWERLINE_COMMAND tmux left)' +set -g status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)' set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " From 59b090e83670948552efeb7bd28efa7e3f840d90 Mon Sep 17 00:00:00 2001 From: Austin Beam Date: Thu, 1 May 2014 00:21:12 -0500 Subject: [PATCH 0971/1472] tmux: Colorize the last active window FG Use a feature of tmux v1.8+ to change the FG color of the last active window to blue. This uses the foo-{attr,bg,fg} format, which is deprecated (although still available) starting with tmux v1.9 in favor of the corresponding foo-style variable. However, tmux v1.9 is not yet ubiquitous enough to move to the foo-style variables. For example, the latest Ubuntu LTS (14.04) only has tmux v1.8 available in its repositories, so the foo-{attr,bg,fg} variables prove to be more 'compatible' for now. For future reference, here is the modification that should be made once tmux v1.8 is deemed old enough to drop support for (or whenever the deprecated foo-{attr,bg,fg} variables go away in the latest version of tmux): -set -g window-status-last-fg colour31 +set -g window-status-last-style fg=colour31 It might be appropriate to add version checking around some of these types of options for maximum compatibility, but my initial attempt to explore version checking in the tmux config file got extremely messy in a hurry. --- powerline/bindings/tmux/powerline.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 22cc9b6c..92448d44 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -10,6 +10,7 @@ set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux displ set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" +set -g window-status-last-fg colour31 set-window-option -g window-status-fg colour249 set-window-option -g window-status-activity-attr none set-window-option -g window-status-bell-attr none From e9852fec3724f63a52e043a056a71d44c64f22e0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 3 May 2014 12:44:00 +0400 Subject: [PATCH 0972/1472] Fix shutdown function being always None Problem reported by @IvanMalison --- powerline/segment.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index 4afe37a3..a085da2e 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -45,6 +45,18 @@ segment_getters = { } +def get_attr_func(contents_func, key, kwargs): + try: + func = getattr(contents_func, key) + except AttributeError: + return None + else: + if kwargs is None: + return lambda : func() + else: + return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **kwargs) + + def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): data = { 'default_module': default_module or 'powerline.segments.' + ext, @@ -74,20 +86,17 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): highlight_group = segment.get('highlight_group') or segment.get('name') if segment_type == 'function': - args = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) - try: - _startup_func = _contents_func.startup - except AttributeError: - startup_func = None - else: - startup_func = lambda pl, shutdown_event: _startup_func(pl=pl, shutdown_event=shutdown_event, **args) + kwargs = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) + startup_func = get_attr_func(_contents_func, 'startup', kwargs) + shutdown_func = get_attr_func(_contents_func, 'shutdown', None) if hasattr(_contents_func, 'powerline_requires_segment_info'): - contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) + contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **kwargs) else: - contents_func = lambda pl, segment_info: _contents_func(pl=pl, **args) + contents_func = lambda pl, segment_info: _contents_func(pl=pl, **kwargs) else: startup_func = None + shutdown_func = None contents_func = None return { @@ -109,8 +118,8 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): 'include_modes': segment.get('include_modes', []), 'width': segment.get('width'), 'align': segment.get('align', 'l'), - 'shutdown': getattr(contents_func, 'shutdown', None), 'startup': startup_func, + 'shutdown': shutdown_func, '_rendered_raw': '', '_rendered_hl': '', '_len': 0, From d759a0a806c27d734d6b817ceed279e2026325b2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 3 May 2014 13:03:57 +0400 Subject: [PATCH 0973/1472] Undo renaming args to kwargs --- powerline/segment.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index a085da2e..b842c62a 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -45,16 +45,16 @@ segment_getters = { } -def get_attr_func(contents_func, key, kwargs): +def get_attr_func(contents_func, key, args): try: func = getattr(contents_func, key) except AttributeError: return None else: - if kwargs is None: + if args is None: return lambda : func() else: - return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **kwargs) + return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): @@ -86,14 +86,14 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): highlight_group = segment.get('highlight_group') or segment.get('name') if segment_type == 'function': - kwargs = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) - startup_func = get_attr_func(_contents_func, 'startup', kwargs) + args = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) + startup_func = get_attr_func(_contents_func, 'startup', args) shutdown_func = get_attr_func(_contents_func, 'shutdown', None) if hasattr(_contents_func, 'powerline_requires_segment_info'): - contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **kwargs) + contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) else: - contents_func = lambda pl, segment_info: _contents_func(pl=pl, **kwargs) + contents_func = lambda pl, segment_info: _contents_func(pl=pl, **args) else: startup_func = None shutdown_func = None From 7937ab98664d31ab3e675ee48cbf7a2d56010fc0 Mon Sep 17 00:00:00 2001 From: Austin Beam Date: Sat, 3 May 2014 17:55:10 -0500 Subject: [PATCH 0974/1472] tmux: Shell variable definition for tmux version Add variables for tmux major and minor version numbers. These can subsequently be used to test for tmux version compatibility. --- powerline/bindings/tmux/powerline.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 48c87b51..ea33c866 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,4 +1,6 @@ if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline-client" "set-environment -g POWERLINE_COMMAND powerline-client" "set-environment -g POWERLINE_COMMAND powerline"' +run-shell "tmux set-environment -g TMUX_VERSION_MAJOR $(tmux -V | cut -d' ' -f2 | cut -d'.' -f1 | sed 's/[^0-9]*//g')" +run-shell "tmux set-environment -g TMUX_VERSION_MINOR $(tmux -V | cut -d' ' -f2 | cut -d'.' -f2 | sed 's/[^0-9]*//g')" set -g status on set -g status-utf8 on set -g status-interval 2 From 478e49773b61d2634ffcca01ddf8b4ee3b7828a7 Mon Sep 17 00:00:00 2001 From: Austin Beam Date: Sat, 3 May 2014 17:57:13 -0500 Subject: [PATCH 0975/1472] tmux: Check version for prefix active colorization Prefix active colorization feature introduced by pull request #863 causes undesired behavior on older versions of tmux. Add version checking and revert to old powerline configuration if the tmux version is less than v1.8. tmux v1.8 was the first version to support the client_prefix format variable. --- powerline/bindings/tmux/powerline.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index ea33c866..16204739 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -7,7 +7,10 @@ set -g status-interval 2 set -g status-fg colour231 set -g status-bg colour234 set -g status-left-length 20 -set -g status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)' +# Version check for 'client_prefix' format functionality +if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 8 \) ]' \ + "set -g status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)'" \ + "set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(eval $POWERLINE_COMMAND tmux left)'" set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " From 20d326fd4538610ad4edee82c144859695c8897d Mon Sep 17 00:00:00 2001 From: Austin Beam Date: Sat, 3 May 2014 17:58:43 -0500 Subject: [PATCH 0976/1472] tmux: Check version for last window indication Last active window indication feature introduced by pull request #864 causes undesired behavior on older versions of tmux. Add version checking to determine which method for feature implementation to use, dropping the feature entirely for tmux versions less that v1.8. For tmux v1.9, use the newer 'window-status-last-style' option rather than the deprecated (starting with v1.9) 'window-status-last-fg' option. Ideally, the other `foo-{attr,fg,bg}` options used in the `tmux` powerline config file should also be version checked and replaced with the corresponding `foo-style` option as well. --- powerline/bindings/tmux/powerline.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 16204739..6facec9c 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -15,7 +15,10 @@ set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux displ set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" -set -g window-status-last-fg colour31 +# Version check for window-status-last-style and/or window-status-last-fg functionality +if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 9 \) ]' \ + "set -g window-status-last-style fg=colour31" \ + 'if-shell "[ $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 8 ]" "set -g window-status-last-fg colour31"' set-window-option -g window-status-fg colour249 set-window-option -g window-status-activity-attr none set-window-option -g window-status-bell-attr none From 34b928091aad347bc5a6eb6c0a81fdb5b23d652a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 May 2014 12:10:32 +0400 Subject: [PATCH 0977/1472] Move some settings from .local.vimrc to .editorconfig Closes #865 as WONTFIX. It is better to have one .editorconfig then a number of editor-specific files. --- .editorconfig | 20 ++++++++++++++++++++ .local.vimrc | 4 ---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..94eab89c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# editorconfig ini file +# Check out http://editorconfig.org for a list of plugins for different +# IDEs/text editors that support this file. Vim plugin to support this: +# +# http://www.vim.org/scripts/script.php?script_id=3934 +# https://github.com/editorconfig/editorconfig-vim +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = tab +# Despite promise somewhere alignment is done only using tabs. Thus setting +# indent_size and tab_width is a requirement. +indent_size = 4 +tab_width = 4 +charset = utf-8 + +[*.rst] +indent_style = space diff --git a/.local.vimrc b/.local.vimrc index e9a3bf05..ac676144 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -4,9 +4,5 @@ " " [1]: http://www.vim.org/scripts/script.php?script_id=3393 " [2]: https://github.com/thinca/vim-localrc -setlocal noexpandtab -" Despite promise somewhere alignment is done only using tabs. Thus setting -" &tabstop is a requirement. -setlocal tabstop=4 let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225,W291,E126' let b:syntastic_checkers = ['flake8'] From be2fe98a21630bab1a45e57226012ffab5ed11e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 May 2014 12:58:09 +0400 Subject: [PATCH 0978/1472] Add support for omitting VALUE This will remove error reported in #853, but not its cause --- docs/source/configuration.rst | 6 ++++++ powerline/lib/__init__.py | 9 ++++++++- powerline/shell.py | 3 +-- tests/test_cmdline.py | 2 ++ tests/test_lib.py | 4 +++- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 4ea0e6b3..51a20595 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -472,11 +472,17 @@ Powerline script has a number of options controlling powerline behavior. Here example: ``{"K1": V1, "K2": V2}``) is recursively merged with the contents of the file. + If ``VALUE`` is omitted then corresponding key will be removed from the + configuration (if it was present). + ``-t THEME_NAME.KEY.NESTED_KEY=VALUE`` or ``--theme_option=THEME_NAME.KEY.NESTED_KEY=VALUE`` Overrides options from :file:`powerline/themes/{ext}/{THEME_NAME}.json`. ``KEY.NESTED_KEY=VALUE`` is processed like described above, ``{ext}`` is the first argument to powerline script. May be passed multiple times. + If ``VALUE`` is omitted then corresponding key will be removed from the + configuration (if it was present). + ``-p PATH`` or ``--config_path=PATH`` Sets directory where configuration should be read from. If present, no default locations are searched for configuration. No expansions are diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index f8d831ac..b68b4830 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -3,6 +3,9 @@ from functools import wraps import json +REMOVE_THIS_KEY = object() + + def wraps_saveargs(wrapped): def dec(wrapper): r = wraps(wrapped)(wrapper) @@ -17,6 +20,8 @@ def mergedicts(d1, d2): for k in d2: if k in d1 and type(d1[k]) is dict and type(d2[k]) is dict: mergedicts(d1[k], d2[k]) + elif d2[k] is REMOVE_THIS_KEY: + d1.pop(k, None) else: d1[k] = d2[k] @@ -45,7 +50,9 @@ def keyvaluesplit(s): idx = s.index('=') o = s[:idx] rest = s[idx + 1:] - if rest[0] in '"{[0193456789' or rest in ('null', 'true', 'false'): + if not rest: + val = REMOVE_THIS_KEY + elif rest[0] in '"{[0193456789' or rest in ('null', 'true', 'false'): val = json.loads(s[idx + 1:]) else: val = rest diff --git a/powerline/shell.py b/powerline/shell.py index 677f2c49..4b817ae1 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -7,8 +7,7 @@ from powerline.lib import mergedicts, parsedotval def mergeargs(argvalue): if not argvalue: return None - argvalue = iter(argvalue) - r = dict([next(argvalue)]) + r = {} for subval in argvalue: mergedicts(r, dict([subval])) return r diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index c330da36..91f27adb 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -94,6 +94,8 @@ class TestParser(TestCase): 'renderer_arg': {'smth': {'abc': 'def'}}, }), (['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}), + (['shell', '-R', 'arg=true', '-R', 'arg='], {'ext': ['shell'], 'renderer_arg': {}}), + (['shell', '-R', 'arg='], {'ext': ['shell'], 'renderer_arg': {}}), (['shell', '-t', 'default.segment_info={"hostname": {}}'], { 'ext': ['shell'], 'theme_option': { diff --git a/tests/test_lib.py b/tests/test_lib.py index f4da57d2..6c306615 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8:noet from __future__ import division -from powerline.lib import mergedicts, add_divider_highlight_group +from powerline.lib import mergedicts, add_divider_highlight_group, REMOVE_THIS_KEY from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment @@ -354,6 +354,8 @@ class TestLib(TestCase): self.assertEqual(d, {'abc': {'def': {'ghi': 'jkl'}}}) mergedicts(d, {'abc': {'mno': 'pqr'}}) self.assertEqual(d, {'abc': {'def': {'ghi': 'jkl'}, 'mno': 'pqr'}}) + mergedicts(d, {'abc': {'def': REMOVE_THIS_KEY}}) + self.assertEqual(d, {'abc': {'mno': 'pqr'}}) def test_add_divider_highlight_group(self): def decorated_function_name(**kwargs): From 032a363b81c0553999c5dc596a12c84e894a57de Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 May 2014 13:30:43 +0400 Subject: [PATCH 0979/1472] Allow disabling prompt and/or tmux support in shells Fixes #849 --- docs/source/configuration.rst | 30 +++++++-- powerline/bindings/bash/powerline.sh | 34 ++++++---- powerline/bindings/fish/powerline-setup.fish | 68 +++++++++++--------- powerline/bindings/tcsh/powerline.tcsh | 14 ++-- powerline/bindings/zsh/powerline.zsh | 10 ++- 5 files changed, 97 insertions(+), 59 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 51a20595..73c9943a 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -546,8 +546,28 @@ specific powerline implementation. This is mostly useful for putting powerline into different directory or replacing ``powerline`` script with ``powerline-client`` for performance reasons. -Note: ``$POWERLINE_COMMAND`` appears in shell scripts without quotes thus you -can specify additional parameters in bash. In zsh you will have to make -``$POWERLINE_COMMAND`` an array parameter to achieve the same result. In tmux it -is passed to ``eval`` and depends on the shell used. POSIX-compatible shells, -zsh, bash and fish will split this variable in this case. +.. note:: + + ``$POWERLINE_COMMAND`` appears in shell scripts without quotes thus you can + specify additional parameters in bash. In zsh you will have to make + ``$POWERLINE_COMMAND`` an array parameter to achieve the same result. In + tmux it is passed to ``eval`` and depends on the shell used. + POSIX-compatible shells, zsh, bash and fish will split this variable in this + case. + +If you want to disable prompt in shell, but still have tmux support or if you +want to disable tmux support you can use variables +``$POWERLINE_NO_{SHELL}_PROMPT``/``$POWERLINE_NO_SHELL_PROMPT`` and +``$POWERLINE_NO_{SHELL}_TMUX_SUPPORT``/``$POWERLINE_NO_SHELL_TMUX_SUPPORT`` +(substitute ``{SHELL}`` with the name of the shell (all-caps) you want to +disable support for (e.g. ``BASH``) or use all-inclusive ``SHELL`` that will +disable support for all shells). These variables have no effect after +configuration script was sourced (in fish case: after ``powerline-setup`` +function was run). To disable specific feature support set one of these +variables to some non-empty value. + +.. note:: + + Most supported shells’ configuration scripts check for additional + configuration variables being empty. But tcsh configuration script checks + for variables being *defined*, not empty. diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 6f1de23b..9271e9bd 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -1,14 +1,3 @@ -if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-client &>/dev/null ; then - export POWERLINE_COMMAND=powerline-client - elif which powerline &>/dev/null ; then - export POWERLINE_COMMAND=powerline - else - # `$0` is set to `-bash` when using SSH so that won't work - export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" - fi -fi - _powerline_init_tmux_support() { if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one @@ -46,7 +35,24 @@ _powerline_prompt() { return $last_exit_code } -test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_prompt*}" || - export PROMPT_COMMAND=$'_powerline_prompt\n'"${PROMPT_COMMAND}" +_powerline_setup_prompt() { + if test -z "${POWERLINE_COMMAND}" ; then + if which powerline-client &>/dev/null ; then + export POWERLINE_COMMAND=powerline-client + elif which powerline &>/dev/null ; then + export POWERLINE_COMMAND=powerline + else + # `$0` is set to `-bash` when using SSH so that won't work + export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" + fi + fi + test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_prompt*}" || + export PROMPT_COMMAND=$'_powerline_prompt\n'"${PROMPT_COMMAND}" +} -_powerline_init_tmux_support +if test -z "$POWERLINE_NO_BASH_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then + _powerline_setup_prompt +fi +if test -z "$POWERLINE_NO_BASH_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then + _powerline_init_tmux_support +fi diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index d925a6fc..50eb5302 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -1,39 +1,43 @@ function powerline-setup - if test -z "$POWERLINE_COMMAND" - if which powerline-client >/dev/null - set -g -x POWERLINE_COMMAND powerline-client - else if which powerline >/dev/null - set -g -x POWERLINE_COMMAND powerline - else - set -g -x POWERLINE_COMMAND (dirname (status -f))/../../../scripts/powerline + if test -z "$POWERLINE_NO_FISH_PROMPT$POWERLINE_NO_SHELL_PROMPT" + if test -z "$POWERLINE_COMMAND" + if which powerline-client >/dev/null + set -g -x POWERLINE_COMMAND powerline-client + else if which powerline >/dev/null + set -g -x POWERLINE_COMMAND powerline + else + set -g -x POWERLINE_COMMAND (dirname (status -f))/../../../scripts/powerline + end end + function --on-variable POWERLINE_COMMAND _powerline_update + set -l addargs "--last_exit_code=\$status --last_pipe_status=\$status --jobnum=(jobs -p | wc -l)" + eval " + function fish_prompt + $POWERLINE_COMMAND shell left $addargs + end + function fish_right_prompt + $POWERLINE_COMMAND shell right $addargs + end + " + end + _powerline_update end - function --on-variable POWERLINE_COMMAND _powerline_update - set -l addargs "--last_exit_code=\$status --last_pipe_status=\$status --jobnum=(jobs -p | wc -l)" - eval " - function fish_prompt - $POWERLINE_COMMAND shell left $addargs - end - function fish_right_prompt - $POWERLINE_COMMAND shell right $addargs - end - " - end - _powerline_update - if test -n "$TMUX" - if tmux refresh -S ^/dev/null - function _powerline_tmux_setenv - tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" - tmux refresh -S + if test -z "$POWERLINE_NO_FISH_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" + if test -n "$TMUX" + if tmux refresh -S ^/dev/null + function _powerline_tmux_setenv + tmux setenv -g TMUX_$argv[1]_(tmux display -p "#D" | tr -d "%") "$argv[2]" + tmux refresh -S + end + function --on-variable PWD _powerline_tmux_set_pwd + _powerline_tmux_setenv PWD "$PWD" + end + function --on-signal WINCH _powerline_tmux_set_columns + _powerline_tmux_setenv COLUMNS "$COLUMNS" + end + _powerline_tmux_set_columns + _powerline_tmux_set_pwd end - function --on-variable PWD _powerline_tmux_set_pwd - _powerline_tmux_setenv PWD "$PWD" - end - function --on-signal WINCH _powerline_tmux_set_columns - _powerline_tmux_setenv COLUMNS "$COLUMNS" - end - _powerline_tmux_set_columns - _powerline_tmux_set_pwd end end end diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 68e031e8..eb6acac1 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -14,8 +14,12 @@ if ! $?POWERLINE_COMMAND then setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline endif endif -alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' -alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' -alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' -alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" -alias precmd "`alias precmd` ; _powerline_set_prompt ; _powerline_set_rprompt" +if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) then + alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' + alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" +endif +if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' + alias precmd "`alias precmd` ; _powerline_set_prompt ; _powerline_set_rprompt" +endif diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 976190c1..47673f0f 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -150,6 +150,10 @@ _powerline_add_widget() { setopt promptpercent setopt promptsubst -_powerline_setup_prompt -_powerline_init_tmux_support -_powerline_init_modes_support +if test -z "$POWERLINE_NO_ZSH_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then + _powerline_setup_prompt + _powerline_init_modes_support +fi +if test -z "$POWERLINE_NO_ZSH_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then + _powerline_init_tmux_support +fi From 413edbc4f0a1e04428aa924df5a5c2837e828280 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 May 2014 17:50:00 +0400 Subject: [PATCH 0980/1472] Add space after `In` string in ipython Thus `In ` and `Out` have the same width --- powerline/config_files/themes/ipython/in.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/themes/ipython/in.json b/powerline/config_files/themes/ipython/in.json index ac979c5f..3d471ec5 100644 --- a/powerline/config_files/themes/ipython/in.json +++ b/powerline/config_files/themes/ipython/in.json @@ -7,7 +7,7 @@ }, { "type": "string", - "contents": "In[", + "contents": "In [", "draw_soft_divider": false, "highlight_group": ["prompt"] }, From f39b1dcf201cb73baf98bf84bcab3699ac725f4d Mon Sep 17 00:00:00 2001 From: Austin Beam Date: Mon, 5 May 2014 22:45:43 -0500 Subject: [PATCH 0981/1472] tmux: Enhance version checking for tmux tmux configuration has become very fragmented between versions due to a combination of new features and deprecation of older options. As such, version checking for tmux became a requirement to allow maximum functionality along with version-appropriate configuration. However, wrapping nearly every line with `if-shell` is tedious and becomes virtually unreadable. This enhancement to tmux version-checking creates a logical configuration file structure that reflects the development of tmux. As additional configurations are added and tmux development continues, this structure can be leveraged appropriately and extended as needed. Rather than having multiple `if-shell` checks for the same version, each version or version group with specific requirements can be checked only once. This leads to simpler and fewer `if-shell` version checks. It also reduces the ambiguity introduced by complex `if-shell` commands that include an 'else' conditional. A caveat to adding this enhancement is the additional requirement of the end user to add a tmux variable definition (`POWERLINE_BINDINGS_DIR`) to his/her `~/.tmux.conf` file. For existing tmux users leveraging powerline, this may prove to be a slight one-time nuisance immediately after upgrading. Without this definition, there is no way to determine the location of the additional tmux powerline config files that have been added with this enhancement. The docs have also been updated in this commit to reflect this initial configuration requirement change. Also added are some additional troubleshooting notes that point to definition of this variable as a potential issue. Powerline will continue to work without the definition of `POWERLINE_BINDINGS_DIR`, but some of the functionality will be reduced and the appearance incomplete (i.e. colors). Ultimately, the caveat above is a small price to pay in exchange for a framework that can be leveraged for proper version-appropriate configuration of tmux going forward. --- .../installation/troubleshooting-common.rst | 6 +++ docs/source/overview.rst | 11 ++++-- powerline/bindings/tmux/powerline.conf | 38 ++++++++++++------- .../bindings/tmux/powerline_tmux_1.8.conf | 5 +++ .../tmux/powerline_tmux_1.8_plus.conf | 5 +++ .../tmux/powerline_tmux_1.9_plus.conf | 8 ++++ .../tmux/powerline_tmux_legacy_common.conf | 11 ++++++ 7 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 powerline/bindings/tmux/powerline_tmux_1.8.conf create mode 100644 powerline/bindings/tmux/powerline_tmux_1.8_plus.conf create mode 100644 powerline/bindings/tmux/powerline_tmux_1.9_plus.conf create mode 100644 powerline/bindings/tmux/powerline_tmux_legacy_common.conf diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 2c777ff5..bbc40d27 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -9,6 +9,8 @@ I'm using tmux and Powerline looks like crap, what's wrong? * If you're using iTerm2, make sure that you have enabled the setting :guilabel:`Set locale variables automatically` in :menuselection:`Profiles --> Terminal --> Environment`. +* Check to ensure that ``POWERLINE_BINDINGS_DIR`` is properly defined in your + ``.tmux.conf`` file as indicated in the :ref:`tmux usage ` I’m using tmux/screen and Powerline is colorless ------------------------------------------------ @@ -18,6 +20,10 @@ I’m using tmux/screen and Powerline is colorless * Alternative: set :ref:`additional_escapes ` to ``"tmux"`` or ``"screen"``. Note that it is known to work perfectly in screen, but in tmux it may produce ugly spaces. +* For ``tmux``, check to ensure that ``POWERLINE_BINDINGS_DIR`` is properly + defined in your ``.tmux.conf`` file as indicated in the :ref:`tmux usage + ` + After an update something stopped working ----------------------------------------- diff --git a/docs/source/overview.rst b/docs/source/overview.rst index cc1dfbf2..a0c7eaf7 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -164,13 +164,18 @@ is the absolute path to your Powerline installation directory: set fish_function_path $fish_function_path "{repository_root}/powerline/bindings/fish" powerline-setup +.. _tmux-statusline: + Tmux statusline --------------- -Add the following line to your :file:`tmux.conf`, where ``{repository_root}`` is -the absolute path to your Powerline installation directory:: +Add the following lines to your :file:`.tmux.conf`, where ``{repository_root}`` +is the absolute path to your Powerline installation directory (please note that +the definition of the ``POWERLINE_BINDINGS_DIR`` variable is required for full +powerline support):: - source '{repository_root}/powerline/bindings/tmux/powerline.conf' + POWERLINE_BINDINGS_DIR="{repository_root}/powerline/bindings" + source "$POWERLINE_BINDINGS_DIR/tmux/powerline.conf" IPython prompt -------------- diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 6facec9c..55437d21 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,27 +1,37 @@ if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline-client" "set-environment -g POWERLINE_COMMAND powerline-client" "set-environment -g POWERLINE_COMMAND powerline"' run-shell "tmux set-environment -g TMUX_VERSION_MAJOR $(tmux -V | cut -d' ' -f2 | cut -d'.' -f1 | sed 's/[^0-9]*//g')" run-shell "tmux set-environment -g TMUX_VERSION_MINOR $(tmux -V | cut -d' ' -f2 | cut -d'.' -f2 | sed 's/[^0-9]*//g')" + +# Don't version-check for this core functionality -- anything too old to +# support these options likely won't work well with powerline set -g status on set -g status-utf8 on set -g status-interval 2 -set -g status-fg colour231 -set -g status-bg colour234 set -g status-left-length 20 -# Version check for 'client_prefix' format functionality -if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 8 \) ]' \ - "set -g status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)'" \ - "set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(eval $POWERLINE_COMMAND tmux left)'" set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" -# Version check for window-status-last-style and/or window-status-last-fg functionality + +# Legacy status-left definition to be overwritten for tmux Versions 1.8+ +set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]#(eval $POWERLINE_COMMAND tmux left)' + +# Simplify tmux version checking by using multiple config files. Source these +# config files based on the version in which tmux features were added and/or +# deprecated. By splitting these configuration options into separate files, +# less 'if-shell' commands are necessary and reading/editing of config files is +# much easier. + +# tmux Version 1.9 adds foo-style options if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 9 \) ]' \ - "set -g window-status-last-style fg=colour31" \ - 'if-shell "[ $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 8 ]" "set -g window-status-last-fg colour31"' -set-window-option -g window-status-fg colour249 -set-window-option -g window-status-activity-attr none -set-window-option -g window-status-bell-attr none -set-window-option -g window-status-activity-fg yellow -set-window-option -g window-status-bell-fg red + "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_1.9_plus.conf"" +# tmux Version 1.8 adds the 'client_prefix' format variable +if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 8 \) ]' \ + "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_1.8_plus.conf"" +# tmux Version 1.8 adds a legacy window-status-last-{attr,bg,fg} option +if-shell '[ $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -eq 8 ]' \ + "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_1.8.conf"" +# tmux Versions 1.8 and earlier use the legacy foo-{attr,bg,fg} options +if-shell '[ $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -le 8 ]' \ + "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_legacy_common.conf"" # vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_1.8.conf b/powerline/bindings/tmux/powerline_tmux_1.8.conf new file mode 100644 index 00000000..d94daf39 --- /dev/null +++ b/powerline/bindings/tmux/powerline_tmux_1.8.conf @@ -0,0 +1,5 @@ +# powerline_tmux_1.8.conf +# tmux Version 1.8 introduces window-status-last-{attr,bg,fg}, which is +# deprecated for versions 1.9+, thus only applicable to version 1.8. +set -g window-status-last-fg colour31 +# vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf b/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf new file mode 100644 index 00000000..a31bacfe --- /dev/null +++ b/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf @@ -0,0 +1,5 @@ +# powerline_tmux_1.8_plus.conf +# tmux Version 1.8 introduces the 'client_prefix' format variable, applicable +# for versions 1.8+ +set -g status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)' +# vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf b/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf new file mode 100644 index 00000000..619b26d5 --- /dev/null +++ b/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf @@ -0,0 +1,8 @@ +# powerline_tmux_1.9_plus.conf +# Version 1.9 introduces the foo-style options, applicable to version 1.9+ +set -g status-style fg=colour231,bg=colour234 +set -g window-status-last-style fg=colour31 +set-window-option -g window-status-style fg=colour249 +set-window-option -g window-status-activity-style fg=yellow,none +set-window-option -g window-status-bell-style fg=red,none +# vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_legacy_common.conf b/powerline/bindings/tmux/powerline_tmux_legacy_common.conf new file mode 100644 index 00000000..6baed844 --- /dev/null +++ b/powerline/bindings/tmux/powerline_tmux_legacy_common.conf @@ -0,0 +1,11 @@ +# powerline_tmux_legacy_common.conf +# tmux Version 1.8 and earlier (legacy) common options. The foo-{attr,bg,fg} +# options are deprecated starting with tmux Version 1.9. +set -g status-fg colour231 +set -g status-bg colour234 +set-window-option -g window-status-fg colour249 +set-window-option -g window-status-activity-attr none +set-window-option -g window-status-bell-attr none +set-window-option -g window-status-activity-fg yellow +set-window-option -g window-status-bell-fg red +# vim: ft=tmux From 43941e4d2100438a09680f5a40b0e3498598585d Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 11 May 2014 20:22:48 -0700 Subject: [PATCH 0982/1472] Enable testing on Python 3.4 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dadf667d..14195f22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "2.7" - "3.2" - "3.3" + - "3.4" install: tests/install.sh script: tests/test.sh From 7b1d7bbb9ed83ebde5d3578e8ea68e223c767f31 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 24 May 2014 13:44:58 +0400 Subject: [PATCH 0983/1472] Use scripts/powerline-config to source tmux configuration Ref #872 --- powerline/bindings/config.py | 107 ++++++++++++++++++ powerline/bindings/tmux/powerline.conf | 18 +-- ...mon.conf => powerline_tmux_1.8_minus.conf} | 0 scripts/powerline-config | 33 ++++++ setup.py | 1 + 5 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 powerline/bindings/config.py rename powerline/bindings/tmux/{powerline_tmux_legacy_common.conf => powerline_tmux_1.8_minus.conf} (100%) create mode 100755 scripts/powerline-config diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py new file mode 100644 index 00000000..83bab203 --- /dev/null +++ b/powerline/bindings/config.py @@ -0,0 +1,107 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import, unicode_literals, print_function + +from collections import namedtuple +import os +import subprocess +import re + + +TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) + + +def get_tmux_executable_name(): + '''Returns tmux executable name + + It should be defined in POWERLINE_TMUX_EXE environment variable, otherwise + it is simply “tmux”. + ''' + + return os.environ.get('POWERLINE_TMUX_EXE', 'tmux') + + +def _run_tmux(runner, args): + return runner([get_tmux_executable_name()] + list(args)) + + +def run_tmux_command(*args): + '''Run tmux command, ignoring the output''' + _run_tmux(subprocess.check_call, args) + + +def get_tmux_output(*args): + '''Run tmux command and return its output''' + return _run_tmux(subprocess.check_output, args) + + +NON_DIGITS = re.compile('[^0-9]+') +DIGITS = re.compile('[0-9]+') +NON_LETTERS = re.compile('[^a-z]+') + + +def get_tmux_version(): + version_string = get_tmux_output('-V') + _, version_string = version_string.split(' ') + version_string = version_string.strip() + major, minor = version_string.split('.') + suffix = DIGITS.subn('', minor)[0] or None + minor = NON_DIGITS.subn('', minor)[0] + return TmuxVersionInfo(int(major), int(minor), suffix) + + +CONFIG_FILE_NAME = re.compile(r'powerline_tmux_(?P\d+)\.(?P\d+)(?P[a-z]+)?(?:_(?Pplus|minus))?\.conf') +CONFIG_MATCHERS = { + None: (lambda a, b: a.major == b.major and a.minor == b.minor), + 'plus': (lambda a, b: a[:2] <= b[:2]), + 'minus': (lambda a, b: a[:2] >= b[:2]), +} +CONFIG_PRIORITY = { + None: 3, + 'plus': 2, + 'minus': 1, +} + + +def list_all_tmux_configs(): + '''List all version-specific tmux configuration files''' + directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmux') + for root, dirs, files in os.walk(directory): + dirs[:] = () + for fname in files: + match = CONFIG_FILE_NAME.match(fname) + if match: + assert match.group('suffix') is None + yield ( + os.path.join(root, fname), + CONFIG_MATCHERS[match.group('mod')], + CONFIG_PRIORITY[match.group('mod')], + TmuxVersionInfo( + int(match.group('major')), + int(match.group('minor')), + match.group('suffix'), + ), + ) + + +def get_tmux_configs(version): + '''Get tmux configuration suffix given parsed tmux version + + :param TmuxVersionInfo version: Parsed tmux version. + ''' + for fname, matcher, priority, file_version in list_all_tmux_configs(): + if matcher(file_version, version): + yield (fname, priority + file_version.minor * 10 + file_version.major * 10000) + + +def source_tmux_files(): + '''Source relevant version-specific tmux configuration files + + Files are sourced in the following order: + * First relevant files with older versions are sourced. + * If files for same versions are to be sourced then first _minus files are + sourced, then _plus files and then files without _minus or _plus suffixes. + ''' + version = get_tmux_version() + for fname, priority in sorted(get_tmux_configs(version), key=(lambda v: v[1])): + run_tmux_command('source', fname) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 55437d21..6735081e 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,6 +1,4 @@ if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline-client" "set-environment -g POWERLINE_COMMAND powerline-client" "set-environment -g POWERLINE_COMMAND powerline"' -run-shell "tmux set-environment -g TMUX_VERSION_MAJOR $(tmux -V | cut -d' ' -f2 | cut -d'.' -f1 | sed 's/[^0-9]*//g')" -run-shell "tmux set-environment -g TMUX_VERSION_MINOR $(tmux -V | cut -d' ' -f2 | cut -d'.' -f2 | sed 's/[^0-9]*//g')" # Don't version-check for this core functionality -- anything too old to # support these options likely won't work well with powerline @@ -19,19 +17,5 @@ set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour # Simplify tmux version checking by using multiple config files. Source these # config files based on the version in which tmux features were added and/or # deprecated. By splitting these configuration options into separate files, -# less 'if-shell' commands are necessary and reading/editing of config files is -# much easier. - -# tmux Version 1.9 adds foo-style options -if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 9 \) ]' \ - "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_1.9_plus.conf"" -# tmux Version 1.8 adds the 'client_prefix' format variable -if-shell '[ $TMUX_VERSION_MAJOR -gt 1 -o \( $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -ge 8 \) ]' \ - "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_1.8_plus.conf"" -# tmux Version 1.8 adds a legacy window-status-last-{attr,bg,fg} option -if-shell '[ $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -eq 8 ]' \ - "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_1.8.conf"" -# tmux Versions 1.8 and earlier use the legacy foo-{attr,bg,fg} options -if-shell '[ $TMUX_VERSION_MAJOR -eq 1 -a $TMUX_VERSION_MINOR -le 8 ]' \ - "source "$POWERLINE_BINDINGS_DIR/tmux/powerline_tmux_legacy_common.conf"" +run-shell "powerline-config tmux source" # vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_legacy_common.conf b/powerline/bindings/tmux/powerline_tmux_1.8_minus.conf similarity index 100% rename from powerline/bindings/tmux/powerline_tmux_legacy_common.conf rename to powerline/bindings/tmux/powerline_tmux_1.8_minus.conf diff --git a/scripts/powerline-config b/scripts/powerline-config new file mode 100755 index 00000000..151dc1d5 --- /dev/null +++ b/scripts/powerline-config @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +'''Script used to obtain powerline configuration''' + +import argparse + +try: + import powerline.bindings.config as config +except ImportError: + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) + import powerline.bindings.config as config # NOQA + + +TMUX_ACTIONS = { + 'source': (lambda args: config.source_tmux_files()), +} + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + subparsers = parser.add_subparsers() + tmux_parser = subparsers.add_parser('tmux', help='Tmux-specific commands') + tmux_parser.add_argument( + 'function', + choices=tuple(TMUX_ACTIONS.values()), + metavar='action', + type=(lambda v: TMUX_ACTIONS.get(v)), + help='If action is "source" then version-specific tmux configuration files are sourced.' + ) + + args = parser.parse_args() + + args.function(args) diff --git a/setup.py b/setup.py index 0b449011..e341ceec 100755 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ setup( scripts=[ 'scripts/powerline', 'scripts/powerline-lint', + 'scripts/powerline-config', ], keywords='', packages=find_packages(exclude=('tests', 'tests.*')), From c08340e0880a0a34e8391af06090466b1b5f2278 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 24 May 2014 13:59:04 +0400 Subject: [PATCH 0984/1472] Use POWERLINE_CONFIG_COMMAND environment variable --- powerline/bindings/tmux/powerline.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 6735081e..eb1176de 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,4 +1,5 @@ if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline-client" "set-environment -g POWERLINE_COMMAND powerline-client" "set-environment -g POWERLINE_COMMAND powerline"' +if-shell 'test -z "$POWERLINE_CONFIG_COMMAND"' 'set-environment -g POWERLINE_CONFIG_COMMAND powerline-config' # Don't version-check for this core functionality -- anything too old to # support these options likely won't work well with powerline @@ -17,5 +18,5 @@ set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour # Simplify tmux version checking by using multiple config files. Source these # config files based on the version in which tmux features were added and/or # deprecated. By splitting these configuration options into separate files, -run-shell "powerline-config tmux source" +run-shell "eval $POWERLINE_CONFIG_COMMAND tmux source" # vim: ft=tmux From 490398675f8761e28f6a48cf523fecb1a41488f0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 24 May 2014 13:59:20 +0400 Subject: [PATCH 0985/1472] Update documentation Closes #872 --- .../source/installation/troubleshooting-common.rst | 5 ----- docs/source/overview.rst | 14 ++++++++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index bbc40d27..31799d11 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -9,8 +9,6 @@ I'm using tmux and Powerline looks like crap, what's wrong? * If you're using iTerm2, make sure that you have enabled the setting :guilabel:`Set locale variables automatically` in :menuselection:`Profiles --> Terminal --> Environment`. -* Check to ensure that ``POWERLINE_BINDINGS_DIR`` is properly defined in your - ``.tmux.conf`` file as indicated in the :ref:`tmux usage ` I’m using tmux/screen and Powerline is colorless ------------------------------------------------ @@ -20,9 +18,6 @@ I’m using tmux/screen and Powerline is colorless * Alternative: set :ref:`additional_escapes ` to ``"tmux"`` or ``"screen"``. Note that it is known to work perfectly in screen, but in tmux it may produce ugly spaces. -* For ``tmux``, check to ensure that ``POWERLINE_BINDINGS_DIR`` is properly - defined in your ``.tmux.conf`` file as indicated in the :ref:`tmux usage - ` After an update something stopped working diff --git a/docs/source/overview.rst b/docs/source/overview.rst index a0c7eaf7..f681e65b 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -169,13 +169,15 @@ is the absolute path to your Powerline installation directory: Tmux statusline --------------- -Add the following lines to your :file:`.tmux.conf`, where ``{repository_root}`` -is the absolute path to your Powerline installation directory (please note that -the definition of the ``POWERLINE_BINDINGS_DIR`` variable is required for full -powerline support):: +Add the following lines to your :file:`.tmux.conf`, where ``{repository_root}`` +is the absolute path to your Powerline installation directory:: - POWERLINE_BINDINGS_DIR="{repository_root}/powerline/bindings" - source "$POWERLINE_BINDINGS_DIR/tmux/powerline.conf" + source "{repository_root}/tmux/powerline.conf" + +.. note:: + The availability of the ``powerline-config`` command is required for + powerline support. You may specify location of this script via + ``$POWERLINE_CONFIG_COMMAND`` environment variable. IPython prompt -------------- From 88400285688f3ea6f6fe0e101174ae69c3ac9847 Mon Sep 17 00:00:00 2001 From: Teddy Date: Sun, 25 May 2014 20:40:33 +0800 Subject: [PATCH 0986/1472] [tmux] Make battery segment use BAT1 if BAT0 was not found --- powerline/segments/common.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 6cfa07a7..d77c4aec 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1105,9 +1105,15 @@ class NowPlayingSegment(object): now_playing = NowPlayingSegment() -if os.path.exists('/sys/class/power_supply/BAT0/capacity'): +if os.path.exists('/sys/class/power_supply/'): + _linux_bat_fmt = '/sys/class/power_supply/{0}/capacity' + _linux_bat = 'BAT0' + if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): + _linux_bat = 'BAT1' + if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): + raise NotImplementedError def _get_capacity(pl): - with open('/sys/class/power_supply/BAT0/capacity', 'r') as f: + with open(_linux_bat_fmt.format(_linux_bat), 'r') as f: return int(float(f.readline().split()[0])) elif os.path.exists('/usr/bin/pmset'): def _get_capacity(pl): From 7c65ee97037b2fbbfeae8d34d0579fc168819104 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 27 May 2014 05:43:55 +0400 Subject: [PATCH 0987/1472] Catch NotImplementedError when importing module Fixes #883 --- powerline/segments/common.py | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index d77c4aec..5c22416c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1105,25 +1105,29 @@ class NowPlayingSegment(object): now_playing = NowPlayingSegment() -if os.path.exists('/sys/class/power_supply/'): - _linux_bat_fmt = '/sys/class/power_supply/{0}/capacity' - _linux_bat = 'BAT0' - if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): - _linux_bat = 'BAT1' - if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): - raise NotImplementedError - def _get_capacity(pl): - with open(_linux_bat_fmt.format(_linux_bat), 'r') as f: - return int(float(f.readline().split()[0])) -elif os.path.exists('/usr/bin/pmset'): - def _get_capacity(pl): - import re - battery_summary = run_cmd(pl, ['pmset', '-g', 'batt']) - battery_percent = re.search(r'(\d+)%', battery_summary).group(1) - return int(battery_percent) -else: - def _get_capacity(pl): +try: + if os.path.exists('/sys/class/power_supply/'): + _linux_bat_fmt = '/sys/class/power_supply/{0}/capacity' + _linux_bat = 'BAT0' + if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): + _linux_bat = 'BAT1' + if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): + raise NotImplementedError + def _get_capacity(pl): + with open(_linux_bat_fmt.format(_linux_bat), 'r') as f: + return int(float(f.readline().split()[0])) + else: raise NotImplementedError +except NotImplementedError: + if os.path.exists('/usr/bin/pmset'): + def _get_capacity(pl): + import re + battery_summary = run_cmd(pl, ['pmset', '-g', 'batt']) + battery_percent = re.search(r'(\d+)%', battery_summary).group(1) + return int(battery_percent) + else: + def _get_capacity(pl): + raise NotImplementedError def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥', empty_heart='♥'): From 17d12027929365e8ebcc69c32642068cc6208678 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 1 Jun 2014 20:00:36 +0400 Subject: [PATCH 0988/1472] Decode stdout in shell.run_cmd Closes #885 --- powerline/lib/shell.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index 121cea58..db16fd5d 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -1,6 +1,7 @@ # vim:fileencoding=utf-8:noet from subprocess import Popen, PIPE +from locale import getlocale, getdefaultlocale, LC_MESSAGES def run_cmd(pl, cmd, stdin=None): @@ -11,6 +12,8 @@ def run_cmd(pl, cmd, stdin=None): return None else: stdout, err = p.communicate(stdin) + encoding = getlocale(LC_MESSAGES)[1] or getdefaultlocale()[1] or 'utf-8' + stdout = stdout.decode(encoding) return stdout.strip() From 2c210bb649a65c43affbeefbbcbd27e38932da00 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 13 Jun 2014 03:49:42 +0400 Subject: [PATCH 0989/1472] Zpython got renamed to libzpython Not that I wanted this: cmake cannot be told I do not want `lib` prefix. `zsh/` prefix in any case should have been removed: zpython is not pretending it is a part of zsh now. Keeping old name for those who are still using zpython branch (i.e. nearly everybody since zpython got moved less then an hour ago). --- powerline/bindings/zsh/powerline.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 47673f0f..37546902 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -107,7 +107,7 @@ _powerline_setup_prompt() { fi done precmd_functions+=( _powerline_set_jobnum ) - if zmodload zsh/zpython &>/dev/null ; then + if zmodload libzpython &>/dev/null || zmodload zsh/zpython &>/dev/null ; then zpython 'from powerline.bindings.zsh import setup as _powerline_setup' zpython '_powerline_setup()' zpython 'del _powerline_setup' From 641b0e08a15d77406e4f2667a7cf559a3afbafb0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 15 Jun 2014 11:08:25 +0400 Subject: [PATCH 0990/1472] Junk double _POWERLINE_JOBNUM set _POWERLINE_JOBNUM was already set in precmd, no need to do this again. --- powerline/bindings/zsh/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 1df90956..ebeea7d9 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -55,7 +55,6 @@ class Args(object): @property def jobnum(self): - zsh.eval('integer _POWERLINE_JOBNUM=${(%):-%j}') return zsh.getvalue('_POWERLINE_JOBNUM') From 62601ad641a633f2e02f394fe674cbb425cb2b51 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 15 Jun 2014 11:09:09 +0400 Subject: [PATCH 0991/1472] Fix pipestatus segment in case of zsh+zpython --- powerline/bindings/zsh/__init__.py | 20 ++++++++++---------- powerline/bindings/zsh/powerline.zsh | 7 ++++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index ebeea7d9..0ffdee19 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -21,17 +21,10 @@ def get_var_config(var): class Args(object): + __slots__ = ('last_pipe_status', 'last_exit_code') ext = ['shell'] renderer_module = 'zsh_prompt' - @property - def last_exit_code(self): - return zsh.last_exit_code() - - @property - def last_pipe_status(self): - return zsh.pipestatus() - @property def config(self): try: @@ -92,6 +85,13 @@ class Environment(object): environ = Environment() +class ZshPowerline(ShellPowerline): + def precmd(self): + self.args.last_pipe_status = zsh.pipestatus() + self.args.last_exit_code = zsh.last_exit_code() + zsh.eval('_POWERLINE_PARSER_STATE="${(%):-%_}"') + + class Prompt(object): __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme') @@ -104,7 +104,6 @@ class Prompt(object): self.theme = theme def __str__(self): - zsh.eval('_POWERLINE_PARSER_STATE="${(%):-%_}"') segment_info = { 'args': self.args, 'environ': environ, @@ -144,7 +143,7 @@ def set_prompt(powerline, psvar, side, theme): def setup(): - powerline = ShellPowerline(Args()) + powerline = ZshPowerline(Args()) used_powerlines.append(powerline) used_powerlines.append(powerline) set_prompt(powerline, 'PS1', 'left', None) @@ -153,3 +152,4 @@ def setup(): set_prompt(powerline, 'RPS2', 'right', 'continuation') set_prompt(powerline, 'PS3', 'left', 'select') atexit.register(shutdown) + return powerline diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 37546902..1f358b36 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -99,6 +99,10 @@ _powerline_set_jobnum() { _POWERLINE_JOBNUM=${(%):-%j} } +_powerline_update_counter() { + zpython '_powerline.precmd()' +} + _powerline_setup_prompt() { emulate -L zsh for f in "${precmd_functions[@]}"; do @@ -108,8 +112,9 @@ _powerline_setup_prompt() { done precmd_functions+=( _powerline_set_jobnum ) if zmodload libzpython &>/dev/null || zmodload zsh/zpython &>/dev/null ; then + precmd_functions+=( _powerline_update_counter ) zpython 'from powerline.bindings.zsh import setup as _powerline_setup' - zpython '_powerline_setup()' + zpython '_powerline = _powerline_setup()' zpython 'del _powerline_setup' else local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' From 90afccb219d1c85fe0ed7a13139a438d528a566c Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 21:09:15 +0400 Subject: [PATCH 0992/1472] Add note about lags to troubleshooting-common.rst --- .../source/installation/troubleshooting-common.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 2c777ff5..c42a4238 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -117,3 +117,17 @@ Solution to this problem is simple: be sure that :file:`z.sh` is sourced strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background jobs are spawned by `z `_ after powerline has done its job. + +I am suffering bad lags before displaying shell prompt +------------------------------------------------------ + +To get rid of these lags there currently are two options: + +* Take ``powerline-daemon`` script and one of ``powerline-client`` + implementations from ``feature/daemon`` branch (all ``powerline-client`` + implementations leave in ``client`` folder: you need to either compile + ``powerline.c`` or install ``socat`` and use ``powerline.sh`` + (``powerline.py`` is much slower)). Fortunately this branch will be merged in + the future. +* Compile and install ``libzpython`` module that lives in + https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. From 61006d8fe130e058f36eff468a0ff7359530fbf8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 08:39:05 +0400 Subject: [PATCH 0993/1472] Add support for above lines as described in #462 Support is not mirrored in shell bindings yet --- powerline/__init__.py | 19 +++++++++++++ powerline/renderer.py | 20 ++++++++++++-- powerline/shell.py | 2 +- powerline/theme.py | 62 +++++++++++++++++++++++++++---------------- scripts/powerline | 34 +++++++++++++++++------- 5 files changed, 101 insertions(+), 36 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index c82163a9..1f7b71cc 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -413,6 +413,25 @@ class Powerline(object): pass return FailedUnicode(safe_unicode(e)) + def render_above_lines(self, *args, **kwargs): + '''Like .render(), but for ``self.renderer.render_above_lines()`` + ''' + try: + self.update_renderer() + for line in self.renderer.render_above_lines(*args, **kwargs): + yield line + except Exception as e: + try: + self.exception('Failed to render: {0}', str(e)) + except Exception as e: + # Updates e variable to new value, masking previous one. + # Normally it is the same exception (due to raise in case pl is + # unset), but it may also show error in logger. Note that latter + # is not logged by logger for obvious reasons, thus this also + # prevents us from seeing logger traceback. + pass + yield FailedUnicode(safe_unicode(e)) + def shutdown(self): '''Shut down all background threads. Must be run only prior to exiting current application. diff --git a/powerline/renderer.py b/powerline/renderer.py index 7445ff0f..002f777c 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -175,7 +175,20 @@ class Renderer(object): r['getcwd'] = lambda: r['environ']['PWD'] return r - def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): + def render_above_lines(self, **kwargs): + '''Render all segments in the {theme}/segments/above list + + Rendering happens in the reversed order. Parameters are the same as in + .render() method. + + :yield: rendered line. + ''' + + theme = self.get_theme(kwargs.get('matcher_info', None)) + for line in range(theme.get_line_number() - 1, 0, -1): + yield self.render(side=None, line=line, **kwargs) + + def render(self, mode=None, width=None, side=None, line=0, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -193,6 +206,9 @@ class Renderer(object): :param str side: One of ``left``, ``right``. Determines which side will be rendered. If not present all sides are rendered. + :param int line: + Line number for which segments should be obtained. Is counted from + zero (botmost line). :param bool output_raw: Changes the output: if this parameter is ``True`` then in place of one string this method outputs a pair ``(colored_string, @@ -203,7 +219,7 @@ class Renderer(object): Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side, self.get_segment_info(segment_info, mode)) + segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode segments = [self._get_highlighting(segment, mode) for segment in segments diff --git a/powerline/shell.py b/powerline/shell.py index 4b817ae1..bffa1df8 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -51,7 +51,7 @@ def get_argparser(parser=None, *args, **kwargs): parser = argparse.ArgumentParser p = parser(*args, **kwargs) p.add_argument('ext', nargs=1) - p.add_argument('side', nargs='?', choices=('left', 'right')) + p.add_argument('side', nargs='?', choices=('left', 'right', 'above', 'aboveleft')) p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) p.add_argument('-w', '--width', type=int) p.add_argument('--last_exit_code', metavar='INT', type=int) diff --git a/powerline/theme.py b/powerline/theme.py index d6b185f2..c6157e8f 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -2,6 +2,7 @@ from powerline.segment import gen_segment_getter from powerline.lib.unicode import u +import itertools def requires_segment_info(func): @@ -9,6 +10,13 @@ def requires_segment_info(func): return func +def new_empty_segment_line(): + return { + 'left': [], + 'right': [] + } + + class Theme(object): def __init__(self, ext, @@ -20,10 +28,7 @@ class Theme(object): shutdown_event=None): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) - self.segments = { - 'left': [], - 'right': [], - } + self.segments = [] self.EMPTY_SEGMENT = { 'contents': None, 'highlight': {'fg': False, 'bg': False, 'attr': 0} @@ -33,25 +38,29 @@ class Theme(object): if top_theme_config: theme_configs.append(top_theme_config) get_segment = gen_segment_getter(pl, ext, common_config['paths'], theme_configs, theme_config.get('default_module')) - for side in ['left', 'right']: - for segment in theme_config['segments'].get(side, []): - segment = get_segment(segment, side) - if not run_once: - if segment['startup']: - try: - segment['startup'](pl, shutdown_event) - except Exception as e: - pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) - continue - self.segments[side].append(segment) + for segdict in itertools.chain((theme_config['segments'],), + theme_config['segments'].get('above', ())): + self.segments.append(new_empty_segment_line()) + for side in ['left', 'right']: + for segment in segdict.get(side, []): + segment = get_segment(segment, side) + if not run_once: + if segment['startup']: + try: + segment['startup'](pl, shutdown_event) + except Exception as e: + pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) + continue + self.segments[-1][side].append(segment) def shutdown(self): - for segments in self.segments.values(): - for segment in segments: - try: - segment['shutdown']() - except TypeError: - pass + for line in self.segments: + for segments in line.values(): + for segment in segments: + try: + segment['shutdown']() + except TypeError: + pass def get_divider(self, side='left', type='soft'): '''Return segment divider.''' @@ -60,15 +69,22 @@ class Theme(object): def get_spaces(self): return self.spaces - def get_segments(self, side=None, segment_info=None): + def get_line_number(self): + return len(self.segments) + + def get_segments(self, side=None, line=0, segment_info=None): '''Return all segments. Function segments are called, and all segments get their before/after and ljust/rjust properties applied. + + :param int line: + Line number for which segments should be obtained. Is counted from + zero (botmost line). ''' for side in [side] if side else ['left', 'right']: parsed_segments = [] - for segment in self.segments[side]: + for segment in self.segments[line][side]: if segment['type'] == 'function': self.pl.prefix = segment['name'] try: diff --git a/scripts/powerline b/scripts/powerline index 74d0fc98..8b125217 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -10,6 +10,12 @@ except ImportError: sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) from powerline.shell import ShellPowerline, get_argparser, finish_args # NOQA +def write(output): + try: + sys.stdout.write(output) + except UnicodeEncodeError: + sys.stdout.write(output.encode('utf-8')) + if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() finish_args(args) @@ -17,13 +23,21 @@ if __name__ == '__main__': segment_info = {'args': args, 'environ': os.environ} if args.renderer_arg: segment_info.update(args.renderer_arg) - rendered = powerline.render( - width=args.width, - side=args.side, - segment_info=segment_info, - mode=os.environ.get('_POWERLINE_MODE'), - ) - try: - sys.stdout.write(rendered) - except UnicodeEncodeError: - sys.stdout.write(rendered.encode('utf-8')) + if args.side.startswith('above'): + for line in powerline.render_above_lines( + width=args.width, + segment_info=segment_info, + mode=os.environ.get('_POWERLINE_MODE'), + ): + write(line) + sys.stdout.write('\n') + args.side = args.side[len('above'):] + + if args.side: + rendered = powerline.render( + width=args.width, + side=args.side, + segment_info=segment_info, + mode=os.environ.get('_POWERLINE_MODE'), + ) + write(rendered) From eb014efddbf137543b95463f1058acc3cd4208b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 08:48:54 +0400 Subject: [PATCH 0994/1472] Support multiline prompt in bash --- powerline/bindings/bash/powerline.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 9271e9bd..e6cb88f7 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -28,9 +28,15 @@ _powerline_init_tmux_support() { fi } +_run_powerline() { + # Arguments: side, last_exit_code, jobnum + $POWERLINE_COMMAND shell $1 -w $COLUMNS -r bash_prompt --last_exit_code=$2 --jobnum=$3 +} + _powerline_prompt() { local last_exit_code=$? - PS1="$($POWERLINE_COMMAND shell left -r bash_prompt --last_exit_code=$last_exit_code --jobnum="$(jobs -p|wc -l)")" + local jobnum="$(jobs -p|wc -l)" + PS1="$(_run_powerline aboveleft $last_exit_code $jobnum)" _powerline_tmux_set_pwd return $last_exit_code } From 3b4a2b3520fbf1acfd3538635f76e7bcd04428c3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 08:52:46 +0400 Subject: [PATCH 0995/1472] Support multiline prompt in zsh Notes: - Unlike fish zsh outputs right prompt shifted by one to the left. Which means I have to subtract 1 from computed width. - PS2 and PS3 produce too lengthy prompts when fed with real width. Thus they are fed with PS1 prompt width like in ipython (out prompts). --- powerline/bindings/zsh/__init__.py | 24 +++++++++++++++++------- powerline/bindings/zsh/powerline.zsh | 18 ++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 0ffdee19..2bbf3184 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,4 +1,6 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals, division, print_function + import zsh import atexit from powerline.shell import ShellPowerline @@ -89,21 +91,22 @@ class ZshPowerline(ShellPowerline): def precmd(self): self.args.last_pipe_status = zsh.pipestatus() self.args.last_exit_code = zsh.last_exit_code() - zsh.eval('_POWERLINE_PARSER_STATE="${(%):-%_}"') class Prompt(object): - __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme') + __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme', 'above') - def __init__(self, powerline, side, theme, savedpsvar=None, savedps=None): + def __init__(self, powerline, side, theme, savedpsvar=None, savedps=None, above=False): self.powerline = powerline self.side = side + self.above = above self.savedpsvar = savedpsvar self.savedps = savedps self.args = powerline.args self.theme = theme def __str__(self): + zsh.eval('_POWERLINE_PARSER_STATE="${(%):-%_}"') segment_info = { 'args': self.args, 'environ': environ, @@ -111,7 +114,14 @@ class Prompt(object): 'local_theme': self.theme, 'parser_state': zsh.getvalue('_POWERLINE_PARSER_STATE'), } - r = self.powerline.render( + r = '' + if self.above: + for line in self.powerline.render_above_lines( + width=zsh.columns() - 1, + segment_info=segment_info, + ): + r += line + '\n' + r += self.powerline.render( width=zsh.columns(), side=self.side, segment_info=segment_info, @@ -131,13 +141,13 @@ class Prompt(object): self.powerline.shutdown() -def set_prompt(powerline, psvar, side, theme): +def set_prompt(powerline, psvar, side, theme, above=False): try: savedps = zsh.getvalue(psvar) except IndexError: savedps = None zpyvar = 'ZPYTHON_POWERLINE_' + psvar - prompt = Prompt(powerline, side, theme, psvar, savedps) + prompt = Prompt(powerline, side, theme, psvar, savedps, above) zsh.set_special_string(zpyvar, prompt) zsh.setvalue(psvar, '${' + zpyvar + '}') @@ -146,7 +156,7 @@ def setup(): powerline = ZshPowerline(Args()) used_powerlines.append(powerline) used_powerlines.append(powerline) - set_prompt(powerline, 'PS1', 'left', None) + set_prompt(powerline, 'PS1', 'left', None, above=True) set_prompt(powerline, 'RPS1', 'right', None) set_prompt(powerline, 'PS2', 'left', 'continuation') set_prompt(powerline, 'RPS2', 'right', 'continuation') diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 1f358b36..797c216f 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -111,21 +111,27 @@ _powerline_setup_prompt() { fi done precmd_functions+=( _powerline_set_jobnum ) - if zmodload libzpython &>/dev/null || zmodload zsh/zpython &>/dev/null ; then + if test -z "${POWERLINE_NO_ZSH_ZPYTHON}" && { zmodload libzpython || zmodload zsh/zpython } &>/dev/null ; then precmd_functions+=( _powerline_update_counter ) zpython 'from powerline.bindings.zsh import setup as _powerline_setup' zpython '_powerline = _powerline_setup()' zpython 'del _powerline_setup' else - local add_args='--last_exit_code=$? --last_pipe_status="$pipestatus"' + local add_args='--last_exit_code=$?' + add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' - local add_args_2=$add_args' -R parser_state=${(%%):-%_} -R local_theme=continuation' - PS1='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args')' + local new_args_2=' -R parser_state=${(%%):-%_}' + new_args_2+=' -R local_theme=continuation' + local add_args_3=$add_args' -R local_theme=select' + local add_args_2=$add_args$new_args_2 + add_args+=' --width=$(( COLUMNS - 1 ))' + local add_args_r2=$add_args$new_args_2 + PS1='$($POWERLINE_COMMAND shell aboveleft -r zsh_prompt '$add_args')' RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' PS2='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_2')' - RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args_2')' - PS3='$($POWERLINE_COMMAND shell left -r zsh_prompt -R local_theme=select '$add_args')' + RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args_r2')' + PS3='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_3')' fi } From 5df7b36c3a18b6a40deac3689a14c4b0a3727334 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 24 Jun 2014 21:22:09 +0400 Subject: [PATCH 0996/1472] Support multiline prompt in tcsh Notes: - I had to launch script twice because tcsh does not actually support multiline prompt: it squashes everything into one line. - Thus I had to add POWERLINE_TCSH_NO_ABOVE_PROMPT: running python twice is not nice for performance. - Due to 1) tcsh shifting rprompt by 1 and 2) tcsh not accepting %{%}-escaped color section at the very end of rprompt (actually it is accepting it, just makes user input colored as that section) I had to reduce computed width by 2. - As running powerline command in precmd modifies status code I had to use POWERLINE_STATUS variable to save status. - As tcsh does not accept names that start with underscore non-API powerline variables do not start with underscore. --- powerline/bindings/tcsh/powerline.tcsh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index eb6acac1..91b08993 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -19,7 +19,13 @@ if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) t alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" endif if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then - alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$?`"' - alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$?` "' - alias precmd "`alias precmd` ; _powerline_set_prompt ; _powerline_set_rprompt" + if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then + alias _powerline_above true + else + alias _powerline_above '$POWERLINE_COMMAND shell above --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' + endif + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' + alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' + alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' endif From 8849f9d3d6ffbc7bebad00aaa9135272a7d8e66f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 20:43:29 +0400 Subject: [PATCH 0997/1472] Support multiline prompt in fish Note: fish does not accept prompt strings that have width identical to the terminal width: it makes prompt be reduced to just `>`. --- powerline/bindings/fish/powerline-setup.fish | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 50eb5302..6cb807fa 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -10,15 +10,35 @@ function powerline-setup end end function --on-variable POWERLINE_COMMAND _powerline_update - set -l addargs "--last_exit_code=\$status --last_pipe_status=\$status --jobnum=(jobs -p | wc -l)" + set -l addargs "--last_exit_code=\$status" + set -l addargs "$addargs --last_pipe_status=\$status" + set -l addargs "$addargs --jobnum=(jobs -p | wc -l)" + set -l addargs "$addargs --width=\$_POWERLINE_COLUMNS" + set -l promptside + set -l rpromptpast + set -l columnsexpr + if test -z "$POWERLINE_NO_FISH_ABOVE$POWERLINE_NO_SHELL_ABOVE" + set promptside aboveleft + set rpromptpast 'echo -n " "' + set columnsexpr '(math $COLUMNS - 1)' + else + set promptside left + set rpromptpast + set columnsexpr '$COLUMNS' + end eval " function fish_prompt - $POWERLINE_COMMAND shell left $addargs + $POWERLINE_COMMAND shell $promptside $addargs end function fish_right_prompt $POWERLINE_COMMAND shell right $addargs + $rpromptpast + end + function --on-signal WINCH _powerline_set_columns + set -g _POWERLINE_COLUMNS $columnsexpr end " + _powerline_set_columns end _powerline_update end From f718719a3bc1d7b1d2d27a7f0cdd5d329de9a979 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 19:56:09 +0400 Subject: [PATCH 0998/1472] Update lint checker --- powerline/lint/__init__.py | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index ec4d4608..9f73b9a8 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -73,25 +73,31 @@ class DelayedEchoErr(EchoErr): class Spec(object): def __init__(self, **keys): - new_keys = {} - self.specs = list(keys.values()) - for k, v in keys.items(): - new_keys[k] = len(self.specs) - self.specs.append(v) - self.keys = new_keys + self.specs = [] + self.keys = {} self.checks = [] self.cmsg = '' self.isoptional = False self.uspecs = [] self.ufailmsg = lambda key: 'found unknown key: {0}'.format(key) - if keys: + self.did_type = False + self.update(**keys) + + def update(self, **keys): + for k, v in keys.items(): + self.keys[k] = len(self.specs) + self.specs.append(v) + if self.keys and not self.did_type: self.type(dict) + self.did_type = True + return self def copy(self): - return self.__class__().update(self.__dict__) + return self.__class__()._update(self.__dict__) - def update(self, d): + def _update(self, d): self.__dict__.update(d) + self.keys = copy(self.keys) self.checks = copy(self.checks) self.uspecs = copy(self.uspecs) self.specs = [spec.copy() for spec in self.specs] @@ -1005,6 +1011,13 @@ segments_spec = Spec().optional().list( lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(), ).func(check_full_segment_data), ).copy +segdict_spec=Spec( + left=segments_spec().context_message('Error while loading segments from left side (key {key})'), + right=segments_spec().context_message('Error while loading segments from right side (key {key})'), +).func( + lambda value, *args: (True, True, not (('left' in value) or ('right' in value))), + lambda value: 'segments dictionary must contain either left, right or both keys' +).context_message('Error while loading segments (key {key})').copy theme_spec = (Spec( default_module=segment_module_spec(), segment_data=Spec().unknown_spec( @@ -1016,13 +1029,7 @@ theme_spec = (Spec( contents=Spec().type(unicode).optional(), ), ).optional().context_message('Error while loading segment data (key {key})'), - segments=Spec( - left=segments_spec().context_message('Error while loading segments from left side (key {key})'), - right=segments_spec().context_message('Error while loading segments from right side (key {key})'), - ).func( - lambda value, *args: (True, True, not (('left' in value) or ('right' in value))), - lambda value: 'segments dictionary must contain either left, right or both keys' - ).context_message('Error while loading segments (key {key})'), + segments=segdict_spec().update(above=Spec().list(segdict_spec()).optional()), ).context_message('Error while loading theme')) From 9a4e1edfbcaffaebf08f6f685ac91e25aaf6fd1b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 20:03:24 +0400 Subject: [PATCH 0999/1472] Update documentation --- docs/source/configuration.rst | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 73c9943a..0ab55695 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -305,8 +305,22 @@ Themes step 2 is obviously avoided. ``segments`` - A dict with a ``left`` and a ``right`` list, consisting of segment - dicts. Each segment has the following options: + A dict with a ``left`` and a ``right`` lists, consisting of segment + dictionaries. Shell themes may also contain ``above`` list of dictionaries. + Each item in ``above`` list may have ``left`` and ``right`` keys like this + dictionary, but no ``above`` key. + + .. _config-themes-above: + + ``above`` list is used for multiline shell configurations. + + ``left`` and ``right`` lists are used for segments that should be put on the + left or right side in the output. Actual mechanizm of putting segments on + the left or the right depends on used renderer, but most renderers require + one to specify segment with :ref:`width ` ``auto`` + on either side to make generated line fill all of the available width. + + Each segment dictionary has the following options: ``type`` The segment type. Can be one of ``function`` (default), ``string`` @@ -368,6 +382,8 @@ Themes right (``r``). ``width`` + .. _config-themes-seg-width: + Enforces a specific width for this segment. This segment will work as a spacer if the width is set to ``auto``. @@ -566,6 +582,15 @@ configuration script was sourced (in fish case: after ``powerline-setup`` function was run). To disable specific feature support set one of these variables to some non-empty value. +If you do not want to disable prompt in shell, but yet do not want to launch +python twice to get :ref:`above ` lines you do not use in +tcsh you should set ``$POWERLINE_NO_TCSH_ABOVE`` or +``$POWERLINE_NO_SHELL_ABOVE`` variable. + +If you do not want to see additional space which is added to the right prompt in +fish in order to support multiline prompt you should set +``$POWERLINE_NO_FISH_ABOVE`` or ``$POWERLINE_NO_SHELL_ABOVE`` variables. + .. note:: Most supported shells’ configuration scripts check for additional From f02807ffa483fce01182195da634da2dcc9707bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 24 Jun 2014 20:36:45 +0400 Subject: [PATCH 1000/1472] Rename test_configuration to test_provided_config_files --- tests/{test_configuration.py => test_provided_config_files.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_configuration.py => test_provided_config_files.py} (100%) diff --git a/tests/test_configuration.py b/tests/test_provided_config_files.py similarity index 100% rename from tests/test_configuration.py rename to tests/test_provided_config_files.py From 515df615bf0c7e15734d4d451d4164a4cc8b4043 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 24 Jun 2014 23:36:50 +0400 Subject: [PATCH 1001/1472] Add some basic tests --- tests/lib/config_mock.py | 23 ++++++ tests/test_config_reload.py | 16 +---- tests/test_configuration.py | 138 ++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 tests/test_configuration.py diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index a0c083df..1a27d7cd 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -4,6 +4,7 @@ from powerline.renderer import Renderer from powerline.lib.config import ConfigLoader from powerline import Powerline from copy import deepcopy +from time import sleep from functools import wraps @@ -96,6 +97,15 @@ class SimpleRenderer(Renderer): return '<{fg} {bg} {attr}>'.format(fg=fg and fg[0], bg=bg and bg[0], attr=attr) +class EvenSimplerRenderer(Renderer): + def hlstyle(self, fg=None, bg=None, attr=None): + return '{{{fg}{bg}{attr}}}'.format( + fg=fg and fg[0] or '-', + bg=bg and bg[0] or '-', + attr=attr if attr else '', + ) + + class TestPowerline(Powerline): _created = False @@ -111,7 +121,12 @@ renderer = SimpleRenderer def get_powerline(**kwargs): + global renderer watcher = Watcher() + if kwargs.pop('simpler_renderer', False): + renderer = EvenSimplerRenderer + else: + renderer = SimpleRenderer pl = TestPowerline( ext='test', renderer_module='tests.lib.config_mock', @@ -138,3 +153,11 @@ def swap_attributes(cfg_container, powerline_module, replaces): setattr(powerline_module, attr, val) replaces[attr] = old_val return replaces + + +def add_watcher_events(p, *args, **kwargs): + p._watcher._reset(args) + while not p._will_create_renderer(): + sleep(kwargs.get('interval', 0.1)) + if not kwargs.get('wait', True): + return diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 282d611f..f3ce58aa 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -1,10 +1,10 @@ # vim:fileencoding=utf-8:noet from __future__ import unicode_literals import powerline as powerline_module -import time +from time import sleep from tests import TestCase from tests.lib import replace_item -from tests.lib.config_mock import swap_attributes, get_powerline, pop_events +from tests.lib.config_mock import swap_attributes, get_powerline, pop_events, add_watcher_events from copy import deepcopy @@ -92,18 +92,6 @@ config = { } -def sleep(interval): - time.sleep(interval) - - -def add_watcher_events(p, *args, **kwargs): - p._watcher._reset(args) - while not p._will_create_renderer(): - sleep(kwargs.get('interval', 0.1)) - if not kwargs.get('wait', True): - return - - class TestConfigReload(TestCase): def assertAccessEvents(self, *args): self.assertEqual(set(pop_events()), set(args)) diff --git a/tests/test_configuration.py b/tests/test_configuration.py new file mode 100644 index 00000000..ed6996ad --- /dev/null +++ b/tests/test_configuration.py @@ -0,0 +1,138 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals, absolute_import, division +import powerline as powerline_module +from tests import TestCase +from tests.lib import replace_item +from tests.lib.config_mock import swap_attributes, get_powerline, pop_events +from functools import wraps +from copy import deepcopy + + +config = { + 'config': { + 'common': { + 'dividers': { + 'left': { + 'hard': '>>', + 'soft': '>', + }, + 'right': { + 'hard': '<<', + 'soft': '<', + }, + }, + 'spaces': 0, + 'interval': 0, + }, + 'ext': { + 'test': { + 'theme': 'default', + 'colorscheme': 'default', + }, + }, + }, + 'colors': { + 'colors': { + 'col1': 1, + 'col2': 2, + 'col3': 3, + 'col4': 4, + }, + 'gradients': { + }, + }, + 'colorschemes/test/default': { + 'groups': { + 'str1': {'fg': 'col1', 'bg': 'col2', 'attr': ['bold']}, + 'str2': {'fg': 'col3', 'bg': 'col4', 'attr': ['underline']}, + }, + }, + 'themes/test/default': { + 'segments': { + 'left': [ + { + 'type': 'string', + 'contents': 's', + 'width': 'auto', + 'highlight_group': ['str1'], + }, + { + 'type': 'string', + 'contents': 'g', + 'highlight_group': ['str2'], + }, + ], + 'right': [ + { + 'type': 'string', + 'contents': 'f', + 'width': 'auto', + 'align': 'right', + 'highlight_group': ['str2'], + }, + ], + }, + }, +} + + +def add_p_arg(func): + @wraps(func) + def f(self): + with get_powerline(run_once=True, simpler_renderer=True) as p: + func(self, p) + return f + + +class TestSingleLine(TestCase): + def assertRenderEqual(self, p, output, **kwargs): + self.assertEqual(p.render(**kwargs).replace(' ', ' '), output) + + def assertRenderLinesEqual(self, p, output, **kwargs): + self.assertEqual([l.replace(' ', ' ') for l in p.render_above_lines(**kwargs)], output) + + @add_p_arg + def test_without_above(self, p): + self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}') + self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', width=10) + # self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344} f {--}', width=11) + self.assertEqual(list(p.render_above_lines()), []) + + def test_with_above(self): + with replace_item(globals(), 'config', deepcopy(config)): + old_segments = deepcopy(config['themes/test/default']['segments']) + config['themes/test/default']['segments']['above'] = [old_segments] + with get_powerline(run_once=True, simpler_renderer=True) as p: + self.assertRenderLinesEqual(p, [ + '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + ]) + self.assertRenderLinesEqual(p, [ + '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + ], width=10) + + config['themes/test/default']['segments']['above'] = [old_segments] * 2 + with get_powerline(run_once=True, simpler_renderer=True) as p: + self.assertRenderLinesEqual(p, [ + '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + ]) + self.assertRenderLinesEqual(p, [ + '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + ], width=10) + + +replaces = {} + + +def setUpModule(): + global replaces + replaces = swap_attributes(globals(), powerline_module, replaces) + + +tearDownModule = setUpModule + + +if __name__ == '__main__': + from tests import main + main() From 46d58d39fa071059b792ec4dc822b7161b636481 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 19:27:21 +0400 Subject: [PATCH 1002/1472] Fix typo in VimRenderer.render segment_info should have been initialized with self.segment_info, not updated: new values must have priority. Refactored the result so that static values are defined in VimRenderer.segment_info. --- powerline/renderers/vim.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 14496a1e..b948dc0e 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -30,6 +30,9 @@ class VimRenderer(Renderer): character_translations = Renderer.character_translations.copy() character_translations[ord('%')] = '%%' + segment_info = Renderer.segment_info.copy() + segment_info.update(environ=environ) + def __init__(self, *args, **kwargs): if not hasattr(vim, 'strwidth'): # Hope nobody want to change this at runtime @@ -84,16 +87,15 @@ class VimRenderer(Renderer): mode = mode_translations.get(mode, mode) else: mode = 'nc' - segment_info = { + segment_info = self.segment_info.copy() + segment_info.update({ 'window': window, 'mode': mode, 'window_id': window_id, 'winnr': winnr, - 'environ': environ, - } + }) segment_info['buffer'] = segment_info['window'].buffer segment_info['bufnr'] = segment_info['buffer'].number - segment_info.update(self.segment_info) winwidth = segment_info['window'].width statusline = super(VimRenderer, self).render( mode=mode, From 6f6c1fb90c51107835523048dfaeaac21f913c4a Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 19:55:20 +0400 Subject: [PATCH 1003/1472] Add tests --- powerline/vim.py | 4 ++-- tests/lib/config_mock.py | 15 ++++++++---- tests/test_configuration.py | 47 ++++++++++++++++++++++++++++++++++++- tests/vim.py | 7 ++++++ 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/powerline/vim.py b/powerline/vim.py index 517bae82..a26f5e40 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -23,8 +23,8 @@ def _override_from(config, override_varname): class VimPowerline(Powerline): - def __init__(self, pyeval='PowerlinePyeval'): - super(VimPowerline, self).__init__('vim') + def __init__(self, pyeval='PowerlinePyeval', **kwargs): + super(VimPowerline, self).__init__('vim', **kwargs) self.last_window_id = 1 self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 1a27d7cd..2ad78853 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -121,16 +121,23 @@ renderer = SimpleRenderer def get_powerline(**kwargs): + return get_powerline_raw( + TestPowerline, + ext='test', + renderer_module='tests.lib.config_mock', + logger=Logger(), + **kwargs + ) + + +def get_powerline_raw(PowerlineClass, **kwargs): global renderer watcher = Watcher() if kwargs.pop('simpler_renderer', False): renderer = EvenSimplerRenderer else: renderer = SimpleRenderer - pl = TestPowerline( - ext='test', - renderer_module='tests.lib.config_mock', - logger=Logger(), + pl = PowerlineClass( config_loader=ConfigLoader(load=load_json_config, watcher=watcher, run_once=kwargs.get('run_once')), **kwargs ) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index ed6996ad..d6e64f40 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,11 +1,15 @@ # vim:fileencoding=utf-8:noet from __future__ import unicode_literals, absolute_import, division +import tests.vim as vim_module import powerline as powerline_module from tests import TestCase from tests.lib import replace_item from tests.lib.config_mock import swap_attributes, get_powerline, pop_events +from tests.lib.config_mock import get_powerline_raw from functools import wraps from copy import deepcopy +import sys +import os config = { @@ -29,6 +33,10 @@ config = { 'theme': 'default', 'colorscheme': 'default', }, + 'vim': { + 'theme': 'default', + 'colorscheme': 'default', + }, }, }, 'colors': { @@ -47,6 +55,11 @@ config = { 'str2': {'fg': 'col3', 'bg': 'col4', 'attr': ['underline']}, }, }, + 'colorschemes/vim/default': { + 'groups': { + 'environment': {'fg': 'col3', 'bg': 'col4', 'attr': ['underline']}, + }, + }, 'themes/test/default': { 'segments': { 'left': [ @@ -73,6 +86,19 @@ config = { ], }, }, + 'themes/vim/default': { + 'default_module': 'powerline.segments.common', + 'segments': { + 'left': [ + { + 'name': 'environment', + 'args': { + 'variable': 'TEST', + }, + }, + ], + }, + }, } @@ -122,15 +148,34 @@ class TestSingleLine(TestCase): ], width=10) +class TestVim(TestCase): + def test_environ_update(self): + # Regression test: test that segment obtains environment from vim, not + # from os.environ. + from powerline.vim import VimPowerline + with vim_module._with('environ', TEST='abc'): + with get_powerline_raw(VimPowerline) as powerline: + window = vim_module.current.window + window_id = 1 + winnr = window.number + self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>') + vim_module._environ['TEST'] = 'def' + self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>') + + replaces = {} def setUpModule(): global replaces replaces = swap_attributes(globals(), powerline_module, replaces) + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) -tearDownModule = setUpModule +def tearDownModule(): + global replaces + replaces = swap_attributes(globals(), powerline_module, replaces) + sys.path.pop(0) if __name__ == '__main__': diff --git a/tests/vim.py b/tests/vim.py index f447f490..ef37db71 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -11,6 +11,9 @@ options = { } _last_bufnr = 0 _highlights = {} +from collections import defaultdict as _defaultdict +_environ = _defaultdict(lambda: '') +del _defaultdict _thread_id = None @@ -169,6 +172,8 @@ def eval(expr): return vars[expr[2:]] elif expr.startswith('&'): return options[expr[1:]] + elif expr.startswith('$'): + return _environ[expr[1:]] elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) return '0' @@ -708,6 +713,8 @@ def _with(key, *args, **kwargs): return _WithDict(options, **kwargs) elif key == 'globals': return _WithDict(vars, **kwargs) + elif key == 'environ': + return _WithDict(_environ, **kwargs) elif key == 'split': return _WithSplit() From fc8ad831d4f570d591ecf100023dcdbe3fcc2538 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 20:40:24 +0400 Subject: [PATCH 1004/1472] Update tmux data even when prompt is disabled Fixes #467 --- powerline/bindings/bash/powerline.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index e6cb88f7..18b5023a 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -26,6 +26,8 @@ _powerline_init_tmux_support() { return 0 } fi + test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_tmux_set_pwd*}" || + export PROMPT_COMMAND=$'_powerline_tmux_set_pwd\n'"${PROMPT_COMMAND}" } _run_powerline() { @@ -37,7 +39,6 @@ _powerline_prompt() { local last_exit_code=$? local jobnum="$(jobs -p|wc -l)" PS1="$(_run_powerline aboveleft $last_exit_code $jobnum)" - _powerline_tmux_set_pwd return $last_exit_code } From 1498fc714c7bebcbc9f84aefb97e8af8a8f9f082 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 20:59:39 +0400 Subject: [PATCH 1005/1472] Move readlines function to powerline.lib.shell --- powerline/lib/shell.py | 25 +++++++++++++++++++++++-- powerline/lib/vcs/git.py | 10 +--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index db16fd5d..dd995b48 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -1,9 +1,15 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals, division, print_function + from subprocess import Popen, PIPE from locale import getlocale, getdefaultlocale, LC_MESSAGES +def _get_shell_encoding(): + return getlocale(LC_MESSAGES)[1] or getdefaultlocale()[1] or 'utf-8' + + def run_cmd(pl, cmd, stdin=None): try: p = Popen(cmd, stdout=PIPE, stdin=PIPE) @@ -12,11 +18,26 @@ def run_cmd(pl, cmd, stdin=None): return None else: stdout, err = p.communicate(stdin) - encoding = getlocale(LC_MESSAGES)[1] or getdefaultlocale()[1] or 'utf-8' - stdout = stdout.decode(encoding) + stdout = stdout.decode(_get_shell_encoding()) return stdout.strip() def asrun(pl, ascript): '''Run the given AppleScript and return the standard output and error.''' return run_cmd(pl, ['osascript', '-'], ascript) + + +def readlines(cmd, cwd): + '''Run command and read its output, line by line + + :param list cmd: + Command which will be run. + :param str cwd: + Working directory of the command which will be run. + ''' + p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) + encoding = _get_shell_encoding() + p.stderr.close() + with p.stdout: + for line in p.stdout: + yield line[:-1].decode(encoding) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 77d1f387..e9e16b8b 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -7,6 +7,7 @@ import sys import re from powerline.lib.vcs import get_branch_name as _get_branch_name, get_file_status +from powerline.lib.shell import readlines _ref_pat = re.compile(br'ref:\s*refs/heads/(.+)') @@ -144,15 +145,6 @@ try: def branch(self): return get_branch_name(self.directory) except ImportError: - from subprocess import Popen, PIPE - - def readlines(cmd, cwd): - p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) - p.stderr.close() - with p.stdout: - for line in p.stdout: - yield line[:-1].decode('utf-8') - class Repository(object): __slots__ = ('directory', 'ignore_event') From ed267933edf8b6e2e2b63d11ece7457943fe9646 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 21:00:12 +0400 Subject: [PATCH 1006/1472] Add documentation for powerline.lib.shell.run_cmd --- powerline/lib/shell.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index dd995b48..a74affce 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -11,6 +11,17 @@ def _get_shell_encoding(): def run_cmd(pl, cmd, stdin=None): + '''Run command and return its stdout, stripped + + If running command fails returns None and logs failure to ``pl`` argument. + + :param PowerlineLogger pl: + Logger used to log failures. + :param list cmd: + Command which will be run. + :param str stdin: + String passed to command. May be None. + ''' try: p = Popen(cmd, stdout=PIPE, stdin=PIPE) except OSError as e: From afa6b83815a2dca53b87b0c292d9cb20ce6beafc Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 21:00:38 +0400 Subject: [PATCH 1007/1472] Explicitly specify shell=False when using powerline.lib.shell.run_cmd --- powerline/lib/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index a74affce..a0077fc0 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -23,7 +23,7 @@ def run_cmd(pl, cmd, stdin=None): String passed to command. May be None. ''' try: - p = Popen(cmd, stdout=PIPE, stdin=PIPE) + p = Popen(cmd, shell=False, stdout=PIPE, stdin=PIPE) except OSError as e: pl.exception('Could not execute command ({0}): {1}', e, cmd) return None From 685161fc3157026b7cb03e36eb8787b00103fe92 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 21:05:58 +0400 Subject: [PATCH 1008/1472] Incorporate Windows hack found by @kovidgoyal Ref #36 Fixes #498 --- powerline/lib/shell.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index a0077fc0..ea777770 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -4,6 +4,14 @@ from __future__ import absolute_import, unicode_literals, division, print_functi from subprocess import Popen, PIPE from locale import getlocale, getdefaultlocale, LC_MESSAGES +from functools import partial +import sys + + +if sys.platform.startswith('win32'): + # Prevent windows from launching consoles when calling commands + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx + Popen = partial(Popen, creationflags=0x08000000) def _get_shell_encoding(): From 1d9cdc8ff0b1a35ecf90d815a827b081d32e3970 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 21:18:46 +0400 Subject: [PATCH 1009/1472] Fix inotify tree watcher bug Change made by @kovidgoyal. Comment: Fix a bug in the inotify tree watcher that incorrectly marked a tree as unchanged if an ignored event happens after a non-ignored event. This allows an optimisation in the git backed to be used (ignoring changes to .git/index.lock). -- Not including actual commit as I do not think removing pygit2 backend is a good idea. Worse, removing pygit2 backend in the same commit fix is added is definitely bad idea. --- powerline/lib/tree_watcher.py | 3 ++- powerline/lib/vcs/git.py | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index 608e2946..c0ff4c3c 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -119,7 +119,8 @@ class INotifyTreeWatcher(INotify): return path = self.watched_rmap.get(wd, None) if path is not None: - self.modified = not self.ignore_event(path, name) + if not self.ignore_event(path, name): + self.modified = True if mask & self.CREATE: # A new sub-directory might have been created, monitor it. try: diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 77d1f387..98bf3964 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -56,22 +56,18 @@ def do_status(directory, path, func): return func(directory, path) -def ignore_event(path, name): - # Ignore changes to the index.lock file, since they happen frequently and - # dont indicate an actual change in the working tree status - return False - return path.endswith('.git') and name == 'index.lock' - - try: import pygit2 as git class Repository(object): - __slots__ = ('directory', 'ignore_event') + __slots__ = ('directory',) def __init__(self, directory): self.directory = os.path.abspath(directory) - self.ignore_event = ignore_event + + @staticmethod + def ignore_event(path, name): + return False def do_status(self, directory, path): if path: @@ -154,11 +150,17 @@ except ImportError: yield line[:-1].decode('utf-8') class Repository(object): - __slots__ = ('directory', 'ignore_event') + __slots__ = ('directory',) def __init__(self, directory): self.directory = os.path.abspath(directory) - self.ignore_event = ignore_event + + @staticmethod + def ignore_event(path, name): + # Ignore changes to the index.lock file, since they happen + # frequently and dont indicate an actual change in the working tree + # status + return path.endswith('.git') and name == 'index.lock' def _gitcmd(self, directory, *args): return readlines(('git',) + args, directory) From 17469f051b6310563ce8efc4d79075771e6dadc1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 25 Jun 2014 21:34:57 +0400 Subject: [PATCH 1010/1472] Set tmux PWD after PROMPT_COMMAND, do not require it at the start Should fix failing bash bindings test --- powerline/bindings/bash/powerline.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 18b5023a..7094ae5f 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -26,8 +26,8 @@ _powerline_init_tmux_support() { return 0 } fi - test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_tmux_set_pwd*}" || - export PROMPT_COMMAND=$'_powerline_tmux_set_pwd\n'"${PROMPT_COMMAND}" + test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND/_powerline_tmux_set_pwd}" || + export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n_powerline_tmux_set_pwd' } _run_powerline() { From 699f04d50c22b081b959bbcf8ead4518cd8bfc5e Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 27 Jun 2014 19:22:10 +0400 Subject: [PATCH 1011/1472] Improve battery support: - Use dbus+UPower if available (code taken from kovidgoyal@7f73453 and refactored). - Use any battery from /sys/class/power_supply: replace hardcoded BAT0/BAT1 with cycle. - Use pmset. - Use win32com.client with Win32_Battery object (code taken from an [SO question][1] and refactored). - Use ctypes + GetSystemPowerStatus (from [the same question][1]). Completely untested (only know that syntax is correct), will probably have to hear replies from users to merge. [1]: http://stackoverflow.com/a/21083571/273566 Closes #677. Fixes #820. --- powerline/segments/common.py | 159 +++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 17 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5c22416c..02236ed5 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -5,10 +5,11 @@ from __future__ import unicode_literals, absolute_import, division import os import sys import re +import socket from datetime import datetime -import socket from multiprocessing import cpu_count as _cpu_count +from functools import partial from powerline.lib import add_divider_highlight_group from powerline.lib.shell import asrun, run_cmd @@ -1105,29 +1106,153 @@ class NowPlayingSegment(object): now_playing = NowPlayingSegment() -try: - if os.path.exists('/sys/class/power_supply/'): - _linux_bat_fmt = '/sys/class/power_supply/{0}/capacity' - _linux_bat = 'BAT0' - if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): - _linux_bat = 'BAT1' - if not os.path.exists(_linux_bat_fmt.format(_linux_bat)): - raise NotImplementedError - def _get_capacity(pl): - with open(_linux_bat_fmt.format(_linux_bat), 'r') as f: - return int(float(f.readline().split()[0])) +def _get_battery(pl): + try: + import dbus + except ImportError: + pl.debug('Not using DBUS+UPower as dbus is not available') else: - raise NotImplementedError -except NotImplementedError: - if os.path.exists('/usr/bin/pmset'): + try: + bus = dbus.SystemBus() + except Exception as e: + pl.exception('Failed to connect to system bus: {0}', str(e)) + else: + interface = 'org.freedesktop.UPower' + try: + up = bus.get_object(interface, '/org/freedesktop/UPower') + except dbus.exceptions.DBusException as e: + if getattr(e, '_dbus_error_name', '').endswidth('ServiceUnknown'): + pl.debug('Not using DBUS+UPower as UPower is not available via dbus') + else: + pl.exception('Failed to get UPower service with dbus: {0}', str(e)) + else: + devinterface = 'org.freedesktop.DBus.Properties' + devtype_name = interface + '.Device' + for devpath in up.EnumerateDevices(dbus_interface=interface): + dev = bus.get_object(interface, devpath) + devget = lambda what: dev.Get( + devtype_name, + what, + dbus_interface=devinterface + ) + if int(devget('Type'))!= 2: + pl.debug('Not using DBUS+UPower with {0}: invalid type', devpath) + continue + if not bool(devget('IsPresent')): + pl.debug('Not using DBUS+UPower with {0}: not present', devpath) + continue + if not bool(devget('PowerSupply')): + pl.debug('Not using DBUS+UPower with {0}: not a power supply', devpath) + continue + pl.debug('Using DBUS+UPower with {0}', devpath) + return lambda pl: float(partial( + dbus.Interface(dev, dbus_interface=devinterface).Get, + devtype_name, + 'Percentage' + )) + pl.debug('Not using DBUS+UPower as no batteries were found') + + if os.path.isdir('/sys/class/power_supply'): + linux_bat_fmt = '/sys/class/power_supply/{0}/capacity' + for linux_bat in os.listdir('/sys/class/power_supply'): + cap_path = linux_bat_fmt.format(linux_bat) + if linux_bat.startswith('BAT') and os.path.exists(cap_path): + pl.debug('Using /sys/class/power_supply with battery {0}', linux_bat) + + def _get_capacity(pl): + with open(cap_path, 'r') as f: + return int(float(f.readline().split()[0])) + + return _get_capacity + pl.debug('Not using /sys/class/power_supply as no batteries were found') + else: + pl.debug('Not using /sys/class/power_supply: no directory') + + try: + from shutil import which # Python-3.3 and later + except ImportError: + pl.info('Using dumb “which” which only checks for file in /usr/bin') + which = lambda f: (lambda fp: os.path.exists(fp) and fp)(os.path.join('/usr/bin', f)) + + if which('pmset'): + pl.debug('Using pmset') + def _get_capacity(pl): import re battery_summary = run_cmd(pl, ['pmset', '-g', 'batt']) battery_percent = re.search(r'(\d+)%', battery_summary).group(1) return int(battery_percent) + + return _get_capacity else: + pl.debug('Not using pmset: executable not found') + + if sys.platform.startswith('win'): + # From http://stackoverflow.com/a/21083571/273566, reworked + try: + from win32com.client import GetObject + except ImportError: + pl.debug('Not using win32com.client as it is not available') + else: + try: + wmi = GetObject('winmgmts:') + except Exception as e: + pl.exception('Failed to run GetObject from win32com.client: {0}', str(e)) + else: + for battery in wmi.InstancesOf('Win32_Battery'): + pl.debug('Using win32com.client with Win32_Battery') + + def _get_capacity(pl): + # http://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx + return battery.EstimatedChargeRemaining + + return _get_capacity + pl.debug('Not using win32com.client as no batteries were found') + + from ctypes import Structure, c_byte, c_ulong, windll, byref + + class PowerClass(Structure): + _fields_ = [ + ('ACLineStatus', c_byte), + ('BatteryFlag', c_byte), + ('BatteryLifePercent', c_byte), + ('Reserved1', c_byte), + ('BatteryLifeTime', c_ulong), + ('BatteryFullLifeTime', c_ulong) + ] + def _get_capacity(pl): - raise NotImplementedError + powerclass = PowerClass() + result = windll.kernel32.GetSystemPowerStatus(byref(powerclass)) + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa372693(v=vs.85).aspx + if result: + return None + return powerclass.BatteryLifePercent + + if _get_capacity() is None: + pl.debug('Not using GetSystemPowerStatus because it failed') + else: + pl.debug('Using GetSystemPowerStatus') + + return _get_capacity + + raise NotImplementedError + + +def _get_capacity(pl): + global _get_capacity + + def _failing_get_capacity(pl): + raise NotImplementedError + + try: + _get_capacity = _get_battery(pl) + except NotImplementedError: + _get_capacity = _failing_get_capacity + except Exception as e: + pl.exception('Exception while obtaining battery capacity getter: {0}', str(e)) + _get_capacity = _failing_get_capacity + return _get_capacity(pl) def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥', empty_heart='♥'): @@ -1151,7 +1276,7 @@ def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥ try: capacity = _get_capacity(pl) except NotImplementedError: - pl.warn('Unable to get battery capacity.') + pl.info('Unable to get battery capacity.') return None ret = [] if gamify: From 070bfab3e331709187a6bd1967836d7965b18fa8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 17:36:05 +0400 Subject: [PATCH 1012/1472] Fix quotes in powerline.conf Fixes #901 --- powerline/bindings/tmux/powerline.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index eb1176de..a55b4856 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -18,5 +18,5 @@ set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour # Simplify tmux version checking by using multiple config files. Source these # config files based on the version in which tmux features were added and/or # deprecated. By splitting these configuration options into separate files, -run-shell "eval $POWERLINE_CONFIG_COMMAND tmux source" +run-shell 'eval $POWERLINE_CONFIG_COMMAND tmux source' # vim: ft=tmux From 43ca0e828d6c847da37d2e95a5d1068b0b3646a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 17:36:41 +0400 Subject: [PATCH 1013/1472] Fix documentation --- docs/source/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index f681e65b..23b16942 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -172,7 +172,7 @@ Tmux statusline Add the following lines to your :file:`.tmux.conf`, where ``{repository_root}`` is the absolute path to your Powerline installation directory:: - source "{repository_root}/tmux/powerline.conf" + source "{repository_root}/powerline/bindings/tmux/powerline.conf" .. note:: The availability of the ``powerline-config`` command is required for From 95a2bef8d902c38b02c5464d9d9aeb3603a55c08 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 19:03:50 +0400 Subject: [PATCH 1014/1472] Take directory setting from TMUX_CONFIG_DIRECTORY --- powerline/bindings/config.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 83bab203..ab21d035 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -10,6 +10,8 @@ import re TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) +TMUX_CONFIG_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmux') + def get_tmux_executable_name(): '''Returns tmux executable name @@ -65,7 +67,7 @@ CONFIG_PRIORITY = { def list_all_tmux_configs(): '''List all version-specific tmux configuration files''' - directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmux') + directory = TMUX_CONFIG_DIRECTORY for root, dirs, files in os.walk(directory): dirs[:] = () for fname in files: From 27003bc00322f97194db69ddddce576a19dda0b4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 19:20:06 +0400 Subject: [PATCH 1015/1472] Move all variables that should be replaced to powerline/config.py --- powerline/__init__.py | 4 +--- powerline/bindings/config.py | 4 ++-- powerline/config.py | 8 ++++++++ 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 powerline/config.py diff --git a/powerline/__init__.py b/powerline/__init__.py index 1f7b71cc..bc023f61 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -8,13 +8,11 @@ import logging from powerline.colorscheme import Colorscheme from powerline.lib.config import ConfigLoader from powerline.lib.unicode import safe_unicode, FailedUnicode +from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR from threading import Lock, Event -DEFAULT_SYSTEM_CONFIG_DIR = None - - def find_config_file(search_paths, config_file): config_file += '.json' for path in search_paths: diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index ab21d035..16722a98 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -7,11 +7,11 @@ import os import subprocess import re +from powerline.config import TMUX_CONFIG_DIRECTORY + TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) -TMUX_CONFIG_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmux') - def get_tmux_executable_name(): '''Returns tmux executable name diff --git a/powerline/config.py b/powerline/config.py new file mode 100644 index 00000000..53e530f4 --- /dev/null +++ b/powerline/config.py @@ -0,0 +1,8 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import, unicode_literals, print_function +import os + +BINDINGS_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'bindings') +TMUX_CONFIG_DIRECTORY = os.path.join(BINDINGS_DIRECTORY, 'tmux') +DEFAULT_SYSTEM_CONFIG_DIR = None From c570a9806513784341a7cb7caa72f4a8957d4c78 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 19:01:12 +0400 Subject: [PATCH 1016/1472] Add watcher option Ref #818 --- docs/source/configuration.rst | 13 +++++ powerline/__init__.py | 46 +++++++++++----- powerline/lib/config.py | 41 +++++++++++++- powerline/lib/file_watcher.py | 52 ++++++++++++------ powerline/lib/vcs/__init__.py | 36 +++++++------ powerline/lib/vcs/bzr.py | 20 +++++-- powerline/lib/vcs/git.py | 97 ++++++++++++++++------------------ powerline/lib/vcs/mercurial.py | 22 ++++++-- powerline/lint/__init__.py | 5 +- powerline/lint/inspect.py | 2 + powerline/segment.py | 14 +++-- powerline/segments/common.py | 7 +-- powerline/segments/vim.py | 12 +++-- powerline/theme.py | 7 ++- tests/lib/config_mock.py | 7 ++- tests/test_config_reload.py | 1 + tests/test_configuration.py | 3 +- tests/test_lib.py | 23 ++++---- tests/test_segments.py | 60 +++++++++++++-------- 19 files changed, 313 insertions(+), 155 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 0ab55695..b8e70ce6 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -128,6 +128,19 @@ Common configuration is a subdictionary that is a value of ``common`` key in letters, Cyrillic letters). Valid values: any positive integer; it is suggested that you only set it to 1 (default) or 2. +``watcher`` + Select filesystem watcher. Variants are + + ======= =================================== + Variant Description + ======= =================================== + auto Selects most performant watcher. + inotify Select inotify watcher. Linux only. + stat Select stat-based polling watcher. + ======= =================================== + + Default is ``auto``. + .. _config-common-additional_escapes: ``additional_escapes`` diff --git a/powerline/__init__.py b/powerline/__init__.py index bc023f61..83678be4 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -63,7 +63,7 @@ class PowerlineLogger(object): _fallback_logger = None -def _get_fallback_logger(): +def get_fallback_logger(): global _fallback_logger if _fallback_logger: return _fallback_logger @@ -179,15 +179,31 @@ class Powerline(object): self.common_config = config['common'] if self.common_config != self.prev_common_config: common_config_differs = True + self.prev_common_config = self.common_config - self.common_config['paths'] = [os.path.expanduser(path) for path in self.common_config.get('paths', [])] + + self.common_config.setdefault('paths', []) + self.common_config.setdefault('watcher', 'auto') + self.common_config.setdefault('log_level', 'WARNING') + self.common_config.setdefault('log_format', '%(asctime)s:%(levelname)s:%(message)s') + self.common_config.setdefault('term_truecolor', False) + self.common_config.setdefault('ambiwidth', 1) + self.common_config.setdefault('additional_escapes', None) + self.common_config.setdefault('reload_config', True) + self.common_config.setdefault('interval', None) + self.common_config.setdefault('log_file', None) + + self.common_config['paths'] = [ + os.path.expanduser(path) for path in self.common_config['paths'] + ] + self.import_paths = self.common_config['paths'] if not self.logger: - log_format = self.common_config.get('log_format', '%(asctime)s:%(levelname)s:%(message)s') + log_format = self.common_config['log_format'] formatter = logging.Formatter(log_format) - level = getattr(logging, self.common_config.get('log_level', 'WARNING')) + level = getattr(logging, self.common_config['log_level']) handler = self.get_log_handler() handler.setLevel(level) handler.setFormatter(formatter) @@ -198,15 +214,17 @@ class Powerline(object): if not self.pl: self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext) - if not self.config_loader.pl: - self.config_loader.pl = self.pl + self.config_loader.pl = self.pl + + if not self.run_once: + self.config_loader.set_watcher(self.common_config['watcher']) self.renderer_options.update( pl=self.pl, - term_truecolor=self.common_config.get('term_truecolor', False), - ambiwidth=self.common_config.get('ambiwidth', 1), - tmux_escape=self.common_config.get('additional_escapes') == 'tmux', - screen_escape=self.common_config.get('additional_escapes') == 'screen', + term_truecolor=self.common_config['term_truecolor'], + ambiwidth=self.common_config['ambiwidth'], + tmux_escape=self.common_config['additional_escapes'] == 'tmux', + screen_escape=self.common_config['additional_escapes'] == 'screen', theme_kwargs={ 'ext': self.ext, 'common_config': self.common_config, @@ -215,8 +233,8 @@ class Powerline(object): }, ) - if not self.run_once and self.common_config.get('reload_config', True): - interval = self.common_config.get('interval', None) + if not self.run_once and self.common_config['reload_config']: + interval = self.common_config['interval'] self.config_loader.set_interval(interval) self.run_loader_update = (interval is None) if interval is not None and not self.config_loader.is_alive(): @@ -278,7 +296,7 @@ class Powerline(object): :return: logging.Handler subclass. ''' - log_file = self.common_config.get('log_file', None) + log_file = self.common_config['log_file'] if log_file: log_file = os.path.expanduser(log_file) log_dir = os.path.dirname(log_file) @@ -473,5 +491,5 @@ class Powerline(object): def exception(self, msg, *args, **kwargs): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' - pl = getattr(self, 'pl', None) or _get_fallback_logger() + pl = getattr(self, 'pl', None) or get_fallback_logger() return pl.exception(msg, *args, **kwargs) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index de2d9902..8dd50afe 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -28,14 +28,41 @@ class DummyWatcher(object): pass +class DeferredWatcher(object): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + self.calls = [] + + def __call__(self, *args, **kwargs): + self.calls.append(('__call__', args, kwargs)) + + def watch(self, *args, **kwargs): + self.calls.append(('watch', args, kwargs)) + + def unwatch(self, *args, **kwargs): + self.calls.append(('unwatch', args, kwargs)) + + def transfer_calls(self, watcher): + for attr, args, kwargs in self.calls: + getattr(watcher, attr)(*args, **kwargs) + + class ConfigLoader(MultiRunnedThread): - def __init__(self, shutdown_event=None, watcher=None, load=load_json_config, run_once=False): + def __init__(self, shutdown_event=None, watcher=None, watcher_type=None, load=load_json_config, run_once=False): super(ConfigLoader, self).__init__() self.shutdown_event = shutdown_event or Event() if run_once: self.watcher = DummyWatcher() + self.watcher_type = 'dummy' else: - self.watcher = watcher or create_file_watcher() + self.watcher = watcher or DeferredWatcher() + if watcher: + if not watcher_type: + raise ValueError('When specifying watcher you must also specify watcher type') + self.watcher_type = watcher_type + else: + self.watcher_type = 'deferred' self._load = load self.pl = None @@ -47,6 +74,16 @@ class ConfigLoader(MultiRunnedThread): self.missing = defaultdict(set) self.loaded = {} + def set_watcher(self, watcher_type, force=False): + if watcher_type == self.watcher_type: + return + watcher = create_file_watcher(self.pl, watcher_type) + with self.lock: + if self.watcher_type == 'deferred': + self.watcher.transfer_calls(watcher) + self.watcher = watcher + self.watcher_type = watcher_type + def set_pl(self, pl): self.pl = pl diff --git a/powerline/lib/file_watcher.py b/powerline/lib/file_watcher.py index 21524e3b..ffb337f2 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/file_watcher.py @@ -19,8 +19,6 @@ def realpath(path): class INotifyWatch(INotify): - is_stat_based = False - def __init__(self, expire_time=10): super(INotifyWatch, self).__init__() self.watches = {} @@ -147,8 +145,6 @@ class INotifyWatch(INotify): class StatWatch(object): - is_stat_based = True - def __init__(self): self.watches = {} self.lock = RLock() @@ -184,29 +180,51 @@ class StatWatch(object): self.watches.clear() -def create_file_watcher(use_stat=False, expire_time=10): +def create_file_watcher(pl, watcher_type='auto', expire_time=10): ''' - Create an object that can watch for changes to specified files. To use: + Create an object that can watch for changes to specified files - watcher = create_file_watcher() - watcher(path1) # Will return True if path1 has changed since the last time this was called. Always returns True the first time. - watcher.unwatch(path1) + Use ``.__call__()`` method of the returned object to start watching the file + or check whether file has changed since last call. - Uses inotify if available, otherwise tracks mtimes. expire_time is the - number of minutes after the last query for a given path for the inotify - watch for that path to be automatically removed. This conserves kernel + Use ``.unwatch()`` method of the returned object to stop watching the file. + + Uses inotify if available, otherwise tracks mtimes. expire_time is the + number of minutes after the last query for a given path for the inotify + watch for that path to be automatically removed. This conserves kernel resources. + + :param PowerlineLogger pl: + Logger. + :param str watcher_type: + One of ``inotify`` (linux only), ``stat``, ``auto``. Determines what + watcher will be used. ``auto`` will use ``inotify`` if available. + :param int expire_time: + Number of minutes since last ``.__call__()`` before inotify watcher will + stop watching given file. ''' - if use_stat: + if watcher_type == 'stat': + pl.debug('Using requested stat-based watcher', prefix='watcher') return StatWatch() - try: + if watcher_type == 'inotify': + # Explicitly selected inotify watcher: do not catch INotifyError then. + pl.debug('Using requested inotify watcher', prefix='watcher') return INotifyWatch(expire_time=expire_time) - except INotifyError: - pass + + if sys.platform.startswith('linux'): + try: + pl.debug('Trying to use inotify watcher', prefix='watcher') + return INotifyWatch(expire_time=expire_time) + except INotifyError: + pl.info('Failed to create inotify watcher', prefix='watcher') + + pl.debug('Using stat-based watcher') return StatWatch() + if __name__ == '__main__': - watcher = create_file_watcher() + from powerline import get_fallback_logger + watcher = create_file_watcher(get_fallback_logger()) print ('Using watcher: %s' % watcher.__class__.__name__) print ('Watching %s, press Ctrl-C to quit' % sys.argv[-1]) watcher.watch(sys.argv[-1]) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index ff971c5d..1029bc3f 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -23,22 +23,20 @@ def generate_directories(path): _file_watcher = None -def file_watcher(): +def file_watcher(create_watcher): global _file_watcher if _file_watcher is None: - from powerline.lib.file_watcher import create_file_watcher - _file_watcher = create_file_watcher() + _file_watcher = create_watcher() return _file_watcher _branch_watcher = None -def branch_watcher(): +def branch_watcher(create_watcher): global _branch_watcher if _branch_watcher is None: - from powerline.lib.file_watcher import create_file_watcher - _branch_watcher = create_file_watcher() + _branch_watcher = create_watcher() return _branch_watcher @@ -47,11 +45,11 @@ branch_lock = Lock() file_status_lock = Lock() -def get_branch_name(directory, config_file, get_func): +def get_branch_name(directory, config_file, get_func, create_watcher): global branch_name_cache with branch_lock: # Check if the repo directory was moved/deleted - fw = branch_watcher() + fw = branch_watcher(create_watcher) is_watched = fw.is_watched(directory) try: changed = fw(directory) @@ -117,7 +115,7 @@ class FileStatusCache(dict): file_status_cache = FileStatusCache() -def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_func, extra_ignore_files=()): +def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_func, create_watcher, extra_ignore_files=()): global file_status_cache keypath = file_path if os.path.isabs(file_path) else os.path.join(directory, file_path) file_status_cache.update_maps(keypath, directory, dirstate_file, ignore_file_name, extra_ignore_files) @@ -129,7 +127,7 @@ def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_f return ans # Check if any relevant files have changed - file_changed = file_watcher() + file_changed = file_watcher(create_watcher) changed = False # Check if dirstate has changed try: @@ -217,7 +215,7 @@ vcs_props = ( ) -def guess(path): +def guess(path, create_watcher): for directory in generate_directories(path): for vcs, vcs_dir, check in vcs_props: repo_dir = os.path.join(directory, vcs_dir) @@ -227,20 +225,28 @@ def guess(path): try: if vcs not in globals(): globals()[vcs] = getattr(__import__('powerline.lib.vcs', fromlist=[vcs]), vcs) - return globals()[vcs].Repository(directory) + return globals()[vcs].Repository(directory, create_watcher) except: pass return None +def get_fallback_create_watcher(): + from powerline.lib.file_watcher import create_file_watcher + from powerline import get_fallback_logger + from functools import partial + return partial(create_file_watcher, get_fallback_logger(), 'auto') + + def debug(): '''Test run guess(), repo.branch() and repo.status() - To use run python -c "from powerline.lib.vcs import debug; debug()" - some_file_to_watch ''' + To use:: + python -c "from powerline.lib.vcs import debug; debug()" some_file_to_watch. + ''' import sys dest = sys.argv[-1] - repo = guess(os.path.abspath(dest)) + repo = guess(os.path.abspath(dest), get_fallback_create_watcher) if repo is None: print ('%s is not a recognized vcs repo' % dest) raise SystemExit(1) diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index 9734572b..f8818bdc 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -39,10 +39,11 @@ state = None class Repository(object): - def __init__(self, directory): + def __init__(self, directory, create_watcher): if isinstance(directory, bytes): directory = directory.decode(sys.getfilesystemencoding() or sys.getdefaultencoding() or 'utf-8') self.directory = os.path.abspath(directory) + self.create_watcher = create_watcher def status(self, path=None): '''Return status of repository or file. @@ -57,8 +58,14 @@ class Repository(object): those returned by bzr status -S ''' if path is not None: - return get_file_status(self.directory, os.path.join(self.directory, '.bzr', 'checkout', 'dirstate'), - path, '.bzrignore', self.do_status) + return get_file_status( + directory=self.directory, + dirstate_file=os.path.join(self.directory, '.bzr', 'checkout', 'dirstate'), + file_path=path, + ignore_file_name='.bzrignore', + get_func=self.do_status, + create_watcher=self.create_watcher, + ) return self.do_status(self.directory, path) def do_status(self, directory, path): @@ -93,4 +100,9 @@ class Repository(object): def branch(self): config_file = os.path.join(self.directory, '.bzr', 'branch', 'branch.conf') - return get_branch_name(self.directory, config_file, branch_name_from_config_file) + return get_branch_name( + directory=self.directory, + config_file=config_file, + get_func=branch_name_from_config_file, + create_watcher=self.create_watcher, + ) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 5339ca4e..3bf60562 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -6,7 +6,7 @@ import os import sys import re -from powerline.lib.vcs import get_branch_name as _get_branch_name, get_file_status +from powerline.lib.vcs import get_branch_name, get_file_status from powerline.lib.shell import readlines @@ -40,32 +40,57 @@ def git_directory(directory): return path -def get_branch_name(base_dir): - head = os.path.join(git_directory(base_dir), 'HEAD') - return _get_branch_name(base_dir, head, branch_name_from_config_file) +class GitRepository(object): + __slots__ = ('directory', 'create_watcher') + def __init__(self, directory, create_watcher): + self.directory = os.path.abspath(directory) + self.create_watcher = create_watcher -def do_status(directory, path, func): - if path: - gitd = git_directory(directory) - # We need HEAD as without it using fugitive to commit causes the - # current file's status (and only the current file) to not be updated - # for some reason I cannot be bothered to figure out. - return get_file_status( - directory, os.path.join(gitd, 'index'), - path, '.gitignore', func, extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('logs/HEAD', 'info/exclude'))) - return func(directory, path) + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :First column: working directory status (D: dirty / space) + :Second column: index status (I: index dirty / space) + :Third column: presence of untracked files (U: untracked files / space) + :None: repository clean + + With file argument: returns status of this file. Output is + equivalent to the first two columns of "git status --porcelain" + (except for merge statuses as they are not supported by libgit2). + ''' + if path: + gitd = git_directory(self.directory) + # We need HEAD as without it using fugitive to commit causes the + # current file's status (and only the current file) to not be updated + # for some reason I cannot be bothered to figure out. + return get_file_status( + directory=self.directory, + dirstate_file=os.path.join(gitd, 'index'), + file_path=path, + ignore_file_name='.gitignore', + get_func=self.do_status, + create_watcher=self.create_watcher, + extra_ignore_files=tuple(os.path.join(gitd, x) for x in ('logs/HEAD', 'info/exclude')), + ) + return self.do_status(self.directory, path) + + def branch(self): + head = os.path.join(git_directory(self.directory), 'HEAD') + return get_branch_name( + directory=self.directory, + config_file=head, + get_func=branch_name_from_config_file, + create_watcher=self.create_watcher, + ) try: import pygit2 as git - class Repository(object): - __slots__ = ('directory',) - - def __init__(self, directory): - self.directory = os.path.abspath(directory) - + class Repository(GitRepository): @staticmethod def ignore_event(path, name): return False @@ -121,32 +146,8 @@ try: index_column = 'I' r = wt_column + index_column + untracked_column return r if r != ' ' else None - - def status(self, path=None): - '''Return status of repository or file. - - Without file argument: returns status of the repository: - - :First column: working directory status (D: dirty / space) - :Second column: index status (I: index dirty / space) - :Third column: presence of untracked files (U: untracked files / space) - :None: repository clean - - With file argument: returns status of this file. Output is - equivalent to the first two columns of "git status --porcelain" - (except for merge statuses as they are not supported by libgit2). - ''' - return do_status(self.directory, path, self.do_status) - - def branch(self): - return get_branch_name(self.directory) except ImportError: - class Repository(object): - __slots__ = ('directory',) - - def __init__(self, directory): - self.directory = os.path.abspath(directory) - + class Repository(GitRepository): @staticmethod def ignore_event(path, name): # Ignore changes to the index.lock file, since they happen @@ -182,9 +183,3 @@ except ImportError: r = wt_column + index_column + untracked_column return r if r != ' ' else None - - def status(self, path=None): - return do_status(self.directory, path, self.do_status) - - def branch(self): - return get_branch_name(self.directory) diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index 2aa890cd..cebd7960 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -18,15 +18,16 @@ def branch_name_from_config_file(directory, config_file): class Repository(object): - __slots__ = ('directory', 'ui') + __slots__ = ('directory', 'ui', 'create_watcher') statuses = 'MARDUI' repo_statuses = (1, 1, 1, 1, 2) repo_statuses_str = (None, 'D ', ' U', 'DU') - def __init__(self, directory): + def __init__(self, directory, create_watcher): self.directory = os.path.abspath(directory) self.ui = ui.ui() + self.create_watcher = create_watcher def _repo(self, directory): # Cannot create this object once and use always: when repository updates @@ -47,8 +48,14 @@ class Repository(object): "U"nknown, "I"gnored, (None)Clean. ''' if path: - return get_file_status(self.directory, os.path.join(self.directory, '.hg', 'dirstate'), - path, '.hgignore', self.do_status) + return get_file_status( + directory=self.directory, + dirstate_file=os.path.join(self.directory, '.hg', 'dirstate'), + file_path=path, + ignore_file_name='.hgignore', + get_func=self.do_status, + create_watcher=self.create_watcher, + ) return self.do_status(self.directory, path) def do_status(self, directory, path): @@ -69,4 +76,9 @@ class Repository(object): def branch(self): config_file = os.path.join(self.directory, '.hg', 'branch') - return get_branch_name(self.directory, config_file, branch_name_from_config_file) + return get_branch_name( + directory=self.directory, + config_file=config_file, + get_func=branch_name_from_config_file, + create_watcher=self.create_watcher, + ) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 9f73b9a8..928f144e 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -460,13 +460,14 @@ main_spec = (Spec( # only for existence of the path, not for it being a directory paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))), lambda value: 'path does not exist: {0}'.format(value)).optional(), - log_file=Spec().type(str).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(os.path.expanduser(value)))), + log_file=Spec().type(unicode).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(os.path.expanduser(value)))), lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))).optional(), log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)), lambda value: 'unknown debugging level {0}'.format(value)).optional(), - log_format=Spec().type(str).optional(), + log_format=Spec().type(unicode).optional(), interval=Spec().either(Spec().cmp('gt', 0.0), Spec().type(type(None))).optional(), reload_config=Spec().type(bool).optional(), + watcher=Spec().type(unicode).oneof(set(('auto', 'inotify', 'stat'))).optional(), ).context_message('Error while loading common configuration (key {key})'), ext=Spec( vim=Spec( diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 294f2f59..345f1a5c 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -25,6 +25,8 @@ def getconfigargspec(obj): if (arg == 'self' or (arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) or + (arg == 'create_watcher' and + getattr(obj, 'powerline_requires_filesystem_watcher', None)) or (arg == 'pl') or (method.startswith('render') and (1 if argspec.args[0] == 'self' else 0) + i == len(argspec.args)) or arg in args): diff --git a/powerline/segment.py b/powerline/segment.py index b842c62a..cf495c87 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,6 +1,8 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals, division, print_function + +from powerline.lib.file_watcher import create_file_watcher import sys @@ -52,15 +54,15 @@ def get_attr_func(contents_func, key, args): return None else: if args is None: - return lambda : func() + return lambda: func() else: return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) -def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): +def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=None): data = { 'default_module': default_module or 'powerline.segments.' + ext, - 'path': path, + 'path': common_config['paths'], } def get_key(segment, module, key, default=None): @@ -90,6 +92,10 @@ def gen_segment_getter(pl, ext, path, theme_configs, default_module=None): startup_func = get_attr_func(_contents_func, 'startup', args) shutdown_func = get_attr_func(_contents_func, 'shutdown', None) + if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): + create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) + args['create_watcher'] = create_watcher + if hasattr(_contents_func, 'powerline_requires_segment_info'): contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) else: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 5c22416c..775b38e2 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -18,7 +18,7 @@ from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docs from powerline.lib.monotonic import monotonic from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.unicode import u -from powerline.theme import requires_segment_info +from powerline.theme import requires_segment_info, requires_filesystem_watcher from collections import namedtuple @@ -51,8 +51,9 @@ def hostname(pl, segment_info, only_if_ssh=False, exclude_domain=False): return socket.gethostname() +@requires_filesystem_watcher @requires_segment_info -def branch(pl, segment_info, status_colors=False): +def branch(pl, segment_info, create_watcher, status_colors=False): '''Return the current VCS branch. :param bool status_colors: @@ -61,7 +62,7 @@ def branch(pl, segment_info, status_colors=False): Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. ''' name = segment_info['getcwd']() - repo = guess(path=name) + repo = guess(path=name, create_watcher=create_watcher) if repo is not None: branch = repo.branch() scol = ['branch'] diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 9161c729..f1647dee 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -10,7 +10,7 @@ except ImportError: from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar) -from powerline.theme import requires_segment_info +from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status from powerline.lib.humanize_bytes import humanize_bytes @@ -368,8 +368,9 @@ def modified_buffers(pl, text='+ ', join_str=','): return None +@requires_filesystem_watcher @requires_segment_info -def branch(pl, segment_info, status_colors=False): +def branch(pl, segment_info, create_watcher, status_colors=False): '''Return the current working branch. :param bool status_colors: @@ -382,7 +383,7 @@ def branch(pl, segment_info, status_colors=False): name = segment_info['buffer'].name skip = not (name and (not vim_getbufoption(segment_info, 'buftype'))) if not skip: - repo = guess(path=name) + repo = guess(path=name, create_watcher=create_watcher) if repo is not None: branch = repo.branch() scol = ['branch'] @@ -396,8 +397,9 @@ def branch(pl, segment_info, status_colors=False): }] +@requires_filesystem_watcher @requires_segment_info -def file_vcs_status(pl, segment_info): +def file_vcs_status(pl, segment_info, create_watcher): '''Return the VCS status for this buffer. Highlight groups used: ``file_vcs_status``. @@ -405,7 +407,7 @@ def file_vcs_status(pl, segment_info): name = segment_info['buffer'].name skip = not (name and (not vim_getbufoption(segment_info, 'buftype'))) if not skip: - repo = guess(path=name) + repo = guess(path=name, create_watcher=create_watcher) if repo is not None: status = repo.status(os.path.relpath(name, repo.directory)) if not status: diff --git a/powerline/theme.py b/powerline/theme.py index c6157e8f..6450fab3 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -10,6 +10,11 @@ def requires_segment_info(func): return func +def requires_filesystem_watcher(func): + func.powerline_requires_filesystem_watcher = True + return func + + def new_empty_segment_line(): return { 'left': [], @@ -37,7 +42,7 @@ class Theme(object): theme_configs = [theme_config] if top_theme_config: theme_configs.append(top_theme_config) - get_segment = gen_segment_getter(pl, ext, common_config['paths'], theme_configs, theme_config.get('default_module')) + get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module')) for segdict in itertools.chain((theme_config['segments'],), theme_config['segments'].get('above', ())): self.segments.append(new_empty_segment_line()) diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 2ad78853..f4f50624 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -138,7 +138,12 @@ def get_powerline_raw(PowerlineClass, **kwargs): else: renderer = SimpleRenderer pl = PowerlineClass( - config_loader=ConfigLoader(load=load_json_config, watcher=watcher, run_once=kwargs.get('run_once')), + config_loader=ConfigLoader( + load=load_json_config, + watcher=watcher, + watcher_type='test', + run_once=kwargs.get('run_once') + ), **kwargs ) pl._watcher = watcher diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index f3ce58aa..4d33128e 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -23,6 +23,7 @@ config = { }, 'spaces': 0, 'interval': 0, + 'watcher': 'test', }, 'ext': { 'test': { diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d6e64f40..343f90ba 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -4,7 +4,7 @@ import tests.vim as vim_module import powerline as powerline_module from tests import TestCase from tests.lib import replace_item -from tests.lib.config_mock import swap_attributes, get_powerline, pop_events +from tests.lib.config_mock import swap_attributes, get_powerline from tests.lib.config_mock import get_powerline_raw from functools import wraps from copy import deepcopy @@ -27,6 +27,7 @@ config = { }, 'spaces': 0, 'interval': 0, + 'watcher': 'test', }, 'ext': { 'test': { diff --git a/tests/test_lib.py b/tests/test_lib.py index 6c306615..0bbae4e4 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -3,9 +3,11 @@ from __future__ import division from powerline.lib import mergedicts, add_divider_highlight_group, REMOVE_THIS_KEY from powerline.lib.humanize_bytes import humanize_bytes -from powerline.lib.vcs import guess +from powerline.lib.vcs import guess, get_fallback_create_watcher from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic +from powerline.lib.file_watcher import create_file_watcher, INotifyError +from powerline import get_fallback_logger import threading import os import sys @@ -384,9 +386,9 @@ class TestFilesystemWatchers(TestCase): self.fail('The change to {0} was not detected'.format(path)) def test_file_watcher(self): - from powerline.lib.file_watcher import create_file_watcher - w = create_file_watcher(use_stat=False) - if w.is_stat_based: + try: + w = create_file_watcher(pl=get_fallback_logger(), watcher_type='inotify') + except INotifyError: raise SkipTest('This test is not suitable for a stat based file watcher') f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) with open(f1, 'wb'): @@ -485,7 +487,8 @@ class TestVCS(TestCase): self.assertEqual(ans, q) def test_git(self): - repo = guess(path=GIT_REPO) + create_watcher = get_fallback_create_watcher() + repo = guess(path=GIT_REPO, create_watcher=create_watcher) self.assertNotEqual(repo, None) self.assertEqual(repo.branch(), 'master') self.assertEqual(repo.status(), None) @@ -520,7 +523,8 @@ class TestVCS(TestCase): if use_mercurial: def test_mercurial(self): - repo = guess(path=HG_REPO) + create_watcher = get_fallback_create_watcher() + repo = guess(path=HG_REPO, create_watcher=create_watcher) self.assertNotEqual(repo, None) self.assertEqual(repo.branch(), 'default') self.assertEqual(repo.status(), None) @@ -536,7 +540,8 @@ class TestVCS(TestCase): if use_bzr: def test_bzr(self): - repo = guess(path=BZR_REPO) + create_watcher = get_fallback_create_watcher() + repo = guess(path=BZR_REPO, create_watcher=create_watcher) self.assertNotEqual(repo, None, 'No bzr repo found. Do you have bzr installed?') self.assertEqual(repo.branch(), 'test_powerline') self.assertEqual(repo.status(), None) @@ -587,7 +592,7 @@ class TestVCS(TestCase): os.mkdir(d) call(['bzr', 'init', '-q'], cwd=d) call(['bzr', 'nick', '-q', x], cwd=d) - repo = guess(path=d) + repo = guess(path=d, create_watcher=create_watcher) self.assertEqual(repo.branch(), x) self.assertFalse(repo.status()) if x == 'b1': @@ -598,7 +603,7 @@ class TestVCS(TestCase): os.rename(os.path.join(BZR_REPO, 'b'), os.path.join(BZR_REPO, 'b2')) for x, y in (('b1', 'b2'), ('b2', 'b1')): d = os.path.join(BZR_REPO, x) - repo = guess(path=d) + repo = guess(path=d, create_watcher=create_watcher) self.do_branch_rename_test(repo, y) if x == 'b1': self.assertFalse(repo.status()) diff --git a/tests/test_segments.py b/tests/test_segments.py index ddd264f2..497a58ab 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -3,9 +3,11 @@ from __future__ import unicode_literals from powerline.segments import shell, common +from powerline.lib.vcs import get_fallback_create_watcher import tests.vim as vim_module import sys import os +from functools import partial from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl from tests import TestCase @@ -13,6 +15,16 @@ from tests import TestCase vim = None +def get_dummy_guess(**kwargs): + if 'directory' in kwargs: + def guess(path, create_watcher): + return Args(branch=lambda: os.path.basename(path), **kwargs) + else: + def guess(path, create_watcher): + return Args(branch=lambda: os.path.basename(path), directory=path, **kwargs) + return guess + + class TestShell(TestCase): def test_last_status(self): pl = Pl() @@ -201,23 +213,25 @@ class TestCommon(TestCase): def test_branch(self): pl = Pl() + create_watcher = get_fallback_create_watcher() segment_info = {'getcwd': os.getcwd} - with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')): + branch = partial(common.branch, pl=pl, create_watcher=create_watcher) + with replace_attr(common, 'guess', get_dummy_guess(status=lambda: None, directory='/tmp/tests')): with replace_attr(common, 'tree_status', lambda repo, pl: None): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [{'highlight_group': ['branch'], 'contents': 'tests'}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) - with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')): + with replace_attr(common, 'guess', get_dummy_guess(status=lambda: 'D ', directory='/tmp/tests')): with replace_attr(common, 'tree_status', lambda repo, pl: 'D '): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [{'highlight_group': ['branch'], 'contents': 'tests'}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=True), + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [{'highlight_group': ['branch'], 'contents': 'tests'}]) - with replace_attr(common, 'guess', lambda path: None): - self.assertEqual(common.branch(pl=pl, segment_info=segment_info, status_colors=False), None) + with replace_attr(common, 'guess', lambda path, create_watcher: None): + self.assertEqual(branch(segment_info=segment_info, status_colors=False), None) def test_cwd(self): new_os = new_module('os', path=os.path, sep='/') @@ -705,32 +719,36 @@ class TestVim(TestCase): def test_branch(self): pl = Pl() + create_watcher = get_fallback_create_watcher() + branch = partial(vim.branch, pl=pl, create_watcher=create_watcher) with vim_module._with('buffer', '/foo') as segment_info: - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)): + with replace_attr(vim, 'guess', get_dummy_guess(status=lambda: None)): with replace_attr(vim, 'tree_status', lambda repo, pl: None): - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)): + with replace_attr(vim, 'guess', get_dummy_guess(status=lambda: 'DU')): with replace_attr(vim, 'tree_status', lambda repo, pl: 'DU'): - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=False), + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True), + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) def test_file_vcs_status(self): pl = Pl() + create_watcher = get_fallback_create_watcher() + file_vcs_status = partial(vim.file_vcs_status, pl=pl, create_watcher=create_watcher) with vim_module._with('buffer', '/foo') as segment_info: - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): - self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), + with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')): + self.assertEqual(file_vcs_status(segment_info=segment_info), [{'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'}]) - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)): - self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None) + with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: None)): + self.assertEqual(file_vcs_status(segment_info=segment_info), None) with vim_module._with('buffer', '/bar') as segment_info: with vim_module._with('bufoptions', buftype='nofile'): - with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)): - self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None) + with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')): + self.assertEqual(file_vcs_status(segment_info=segment_info), None) old_cwd = None From abb4ed4efccb0f53cab28ddd6e1628707bf02fa7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 21:28:45 +0400 Subject: [PATCH 1017/1472] Support .git files with newline at the end Fixes #876 --- powerline/lib/vcs/git.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 3bf60562..a8b36aea 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -33,6 +33,8 @@ def git_directory(directory): if not raw.startswith(b'gitdir: '): raise IOError('invalid gitfile format') raw = raw[8:].decode(sys.getfilesystemencoding() or 'utf-8') + if raw[-1] == '\n': + raw = raw[:-1] if not raw: raise IOError('no path in gitfile') return os.path.abspath(os.path.join(directory, raw)) From 803963af1550d2604d49266c9ec7e1beef235c58 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 21:45:08 +0400 Subject: [PATCH 1018/1472] Fix git symlink handling --- powerline/lib/vcs/git.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index a8b36aea..27a1e71a 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -80,9 +80,10 @@ class GitRepository(object): return self.do_status(self.directory, path) def branch(self): - head = os.path.join(git_directory(self.directory), 'HEAD') + directory = git_directory(self.directory) + head = os.path.join(directory, 'HEAD') return get_branch_name( - directory=self.directory, + directory=directory, config_file=head, get_func=branch_name_from_config_file, create_watcher=self.create_watcher, From 317f4be43f06f29601a6fc2a01054d386bb4e798 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 21:45:24 +0400 Subject: [PATCH 1019/1472] Add tests for git symlinks --- tests/test_lib.py | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 0bbae4e4..153ab9fa 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -7,6 +7,7 @@ from powerline.lib.vcs import guess, get_fallback_create_watcher from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic from powerline.lib.file_watcher import create_file_watcher, INotifyError +from powerline.lib.vcs.git import git_directory from powerline import get_fallback_logger import threading import os @@ -508,18 +509,38 @@ class TestVCS(TestCase): os.remove(os.path.join(GIT_REPO, 'file')) # Test changing branch self.assertEqual(repo.branch(), 'master') - call(['git', 'branch', 'branch1'], cwd=GIT_REPO) - call(['git', 'checkout', '-q', 'branch1'], cwd=GIT_REPO) - self.do_branch_rename_test(repo, 'branch1') - # For some reason the rest of this test fails on travis and only on - # travis, and I can't figure out why - if 'TRAVIS' in os.environ: - raise SkipTest('Part of this test fails on Travis for unknown reasons') - call(['git', 'branch', 'branch2'], cwd=GIT_REPO) - call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) - self.do_branch_rename_test(repo, 'branch2') - call(['git', 'checkout', '-q', '--detach', 'branch1'], cwd=GIT_REPO) - self.do_branch_rename_test(repo, lambda b: re.match(r'^[a-f0-9]+$', b)) + try: + call(['git', 'branch', 'branch1'], cwd=GIT_REPO) + call(['git', 'checkout', '-q', 'branch1'], cwd=GIT_REPO) + self.do_branch_rename_test(repo, 'branch1') + # For some reason the rest of this test fails on travis and only on + # travis, and I can't figure out why + if 'TRAVIS' in os.environ: + raise SkipTest('Part of this test fails on Travis for unknown reasons') + call(['git', 'branch', 'branch2'], cwd=GIT_REPO) + call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) + self.do_branch_rename_test(repo, 'branch2') + call(['git', 'checkout', '-q', '--detach', 'branch1'], cwd=GIT_REPO) + self.do_branch_rename_test(repo, lambda b: re.match(r'^[a-f0-9]+$', b)) + finally: + call(['git', 'checkout', '-q', 'master'], cwd=GIT_REPO) + + def test_git_sym(self): + create_watcher = get_fallback_create_watcher() + dotgit = os.path.join(GIT_REPO, '.git') + spacegit = os.path.join(GIT_REPO, ' .git ') + os.rename(dotgit, spacegit) + try: + with open(dotgit, 'w') as F: + F.write('gitdir: .git \n') + gitdir = git_directory(GIT_REPO) + self.assertTrue(os.path.isdir(gitdir)) + self.assertEqual(gitdir, os.path.abspath(spacegit)) + repo = guess(path=GIT_REPO, create_watcher=create_watcher) + self.assertEqual(repo.branch(), 'master') + finally: + os.remove(dotgit) + os.rename(spacegit, dotgit) if use_mercurial: def test_mercurial(self): From 4d4da5683811eb7a24e866eb78a05d19659a80e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 21:46:12 +0400 Subject: [PATCH 1020/1472] Check whether it runs fine in travis --- tests/test_lib.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 153ab9fa..4e108e09 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -515,8 +515,6 @@ class TestVCS(TestCase): self.do_branch_rename_test(repo, 'branch1') # For some reason the rest of this test fails on travis and only on # travis, and I can't figure out why - if 'TRAVIS' in os.environ: - raise SkipTest('Part of this test fails on Travis for unknown reasons') call(['git', 'branch', 'branch2'], cwd=GIT_REPO) call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) self.do_branch_rename_test(repo, 'branch2') From d7d8108230e18c1984d9443152ad4abd04f53d4f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 21:54:21 +0400 Subject: [PATCH 1021/1472] Fix error in python-3* that happens while checking branch name --- tests/test_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 4e108e09..b554f004 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -519,7 +519,7 @@ class TestVCS(TestCase): call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) self.do_branch_rename_test(repo, 'branch2') call(['git', 'checkout', '-q', '--detach', 'branch1'], cwd=GIT_REPO) - self.do_branch_rename_test(repo, lambda b: re.match(r'^[a-f0-9]+$', b)) + self.do_branch_rename_test(repo, lambda b: re.match(br'^[a-f0-9]+$', b)) finally: call(['git', 'checkout', '-q', 'master'], cwd=GIT_REPO) From 623395a0130299c5bfcecd5505be3855c8a764ee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 22:18:45 +0400 Subject: [PATCH 1022/1472] Remove outdated comment --- tests/test_lib.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index b554f004..c69d97b6 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -513,8 +513,6 @@ class TestVCS(TestCase): call(['git', 'branch', 'branch1'], cwd=GIT_REPO) call(['git', 'checkout', '-q', 'branch1'], cwd=GIT_REPO) self.do_branch_rename_test(repo, 'branch1') - # For some reason the rest of this test fails on travis and only on - # travis, and I can't figure out why call(['git', 'branch', 'branch2'], cwd=GIT_REPO) call(['git', 'checkout', '-q', 'branch2'], cwd=GIT_REPO) self.do_branch_rename_test(repo, 'branch2') From 2e5c66c2cc738beac7183aa1e44d1bcc4dbf0b98 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Jul 2014 10:17:14 +0400 Subject: [PATCH 1023/1472] Use CIE2000 to determine color distances, compute gradients in Lab Slow --- tools/generate_gradients.py | 63 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 80adae15..9441f92f 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -8,6 +8,9 @@ import json from powerline.colorscheme import cterm_to_hex from itertools import groupby import argparse +from colormath.color_objects import sRGBColor, LabColor +from colormath.color_conversions import convert_color +from colormath.color_diff import delta_e_cie2000 try: from __builtin__ import unicode @@ -22,15 +25,20 @@ def num2(s): return (False, [float(v) for v in s.partition(' ')[::2]]) -def rgbint_to_rgb(rgbint): - return ((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF) +def rgbint_to_lab(rgbint): + rgb = sRGBColor((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF, + is_upscaled=True) + return convert_color(rgb, LabColor) + + +cterm_to_lab = tuple((rgbint_to_lab(v) for v in cterm_to_hex)) def color(s): if len(s) <= 3: - return rgbint_to_rgb(cterm_to_hex[int(s)]) + return cterm_to_lab[int(s)] else: - return rgbint_to_rgb(int(s, 16)) + return rgbint_to_lab(int(s, 16)) def nums(s): @@ -53,34 +61,42 @@ def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset): return start_value + ((offset - start_offset) * (stop_value - start_value) / (stop_offset - start_offset)) -def gradient(DATA): +def lab_gradient(slab, elab, soff, eoff, off): + svals = slab.get_value_tuple() + evals = elab.get_value_tuple() + return LabColor(*[linear_gradient(start_value, end_value, soff, eoff, off) + for start_value, end_value in zip(svals, evals)]) + + +def generate_gradient_function(DATA): def gradient_function(y): initial_offset = 0 for offset, start, end in DATA: if y <= offset: - return [linear_gradient(start[i], end[i], initial_offset, offset, y) for i in range(3)] + return lab_gradient(start, end, initial_offset, offset, y) initial_offset = offset return gradient_function -def get_rgb(*args): - return "%02x%02x%02x" % args +def get_upscaled_values(rgb): + return [min(max(0, i), 255) for i in rgb.get_upscaled_value_tuple()] -def col_distance(rgb1, rgb2): - return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3))) +def get_rgb(lab): + rgb = convert_color(lab, sRGBColor) + rgb = sRGBColor(*get_upscaled_values(rgb), is_upscaled=True) + return rgb.get_rgb_hex()[1:] -def find_color(urgb, colors, ctrans): - cur_distance = 3 * (255 ** 2 + 1) +def find_color(ulab, colors, ctrans): + cur_distance = float('inf') cur_color = None i = 0 - for crgbint in colors: - crgb = rgbint_to_rgb(crgbint) - dist = col_distance(urgb, crgb) + for clab in colors: + dist = delta_e_cie2000(ulab, clab) if dist < cur_distance: cur_distance = dist - cur_color = (ctrans(i), crgb) + cur_color = (ctrans(i), clab) i += 1 return cur_color @@ -89,7 +105,8 @@ def print_color(color): if type(color) is int: colstr = '5;' + str(color) else: - colstr = '2;' + ';'.join((str(int(round(i))) for i in color)) + rgb = convert_color(color, sRGBColor) + colstr = '2;' + ';'.join((str(i) for i in get_upscaled_values(rgb))) sys.stdout.write('\033[48;' + colstr + 'm ') @@ -131,15 +148,15 @@ else: steps = [i * step for i in range(1, maxweight + 1)] data = [(weight, args.gradient[i - 1], args.gradient[i]) for weight, i in zip(steps, range(1, len(args.gradient)))] -gr_func = gradient(data) +gr_func = generate_gradient_function(data) gradient = [gr_func(y) for y in range(0, m)] palettes = { - '16': (cterm_to_hex[:16], lambda c: c), - '256': (cterm_to_hex, lambda c: c), - None: (cterm_to_hex[16:], lambda c: c + 16), + '16': (cterm_to_lab[:16], lambda c: c), + '256': (cterm_to_lab, lambda c: c), + None: (cterm_to_lab[16:], lambda c: c + 16), } -r = [get_rgb(*col) for col in gradient] -r2 = [find_color(col, *palettes[args.palette])[0] for col in gradient] +r = [get_rgb(lab) for lab in gradient] +r2 = [find_color(lab, *palettes[args.palette])[0] for lab in gradient] r3 = [i[0] for i in groupby(r2)] print(json.dumps(r)) print(json.dumps(r2)) From e74ca2933060b8060b0b3ad7b3cbc44bd6f65489 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Jul 2014 11:22:30 +0400 Subject: [PATCH 1024/1472] Use new script to regenerate dark_GREEN_Orange_red Used command was tools/generate_gradients.py 22 58 94 52 --weights '60 15 10 2' as earlier. --- powerline/config_files/colors.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 5b8a7645..e1c484c4 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -75,8 +75,8 @@ }, "gradients": { "dark_GREEN_Orange_red": [ - [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, 94, 94, 94, 94, 94, 94, 52], - ["005f00", "015f00", "025f00", "035f00", "045f00", "055f00", "065f00", "075f00", "085f00", "095f00", "0b5f00", "0c5f00", "0d5f00", "0e5f00", "0f5f00", "105f00", "115f00", "125f00", "135f00", "145f00", "165f00", "175f00", "185f00", "195f00", "1a5f00", "1b5f00", "1c5f00", "1d5f00", "1e5f00", "1f5f00", "215f00", "225f00", "235f00", "245f00", "255f00", "265f00", "275f00", "285f00", "295f00", "2a5f00", "2c5f00", "2d5f00", "2e5f00", "2f5f00", "305f00", "315f00", "325f00", "335f00", "345f00", "355f00", "375f00", "385f00", "395f00", "3a5f00", "3b5f00", "3c5f00", "3d5f00", "3e5f00", "3f5f00", "415f00", "425f00", "435f00", "445f00", "455f00", "465f00", "475f00", "485f00", "495f00", "4a5f00", "4c5f00", "4d5f00", "4e5f00", "4f5f00", "505f00", "515f00", "525f00", "535f00", "545f00", "555f00", "575f00", "585f00", "595f00", "5a5f00", "5b5f00", "5c5f00", "5d5f00", "5e5f00", "615f00", "655f00", "685f00", "6c5f00", "6f5f00", "735f00", "765f00", "7a5f00", "7d5f00", "815f00", "845f00", "815200", "702900"] + [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, 94, 94, 94, 94, 94, 94, 88, 52], + ["006000", "006000", "006000", "006000", "006000", "006000", "006000", "006000", "006000", "036000", "076000", "0a6000", "0d6000", "106000", "126000", "146000", "166000", "186000", "1a6000", "1b6000", "1d6000", "1e6000", "206000", "216000", "236000", "246000", "256000", "266000", "286000", "296000", "2a6000", "2b6000", "2c6100", "2d6100", "2f6100", "306100", "316100", "326100", "336100", "346100", "356100", "366100", "376100", "386100", "386100", "396100", "3a6100", "3b6100", "3c6100", "3d6100", "3e6100", "3f6100", "406100", "406100", "416100", "426000", "436000", "446000", "456000", "456000", "466000", "476000", "486000", "496000", "496000", "4a6000", "4b6000", "4c6000", "4d6000", "4d6000", "4e6000", "4f6000", "506000", "506000", "516000", "526000", "536000", "536000", "546000", "556000", "566000", "566000", "576000", "586000", "596000", "596000", "5a6000", "5d6000", "616000", "646000", "686000", "6b6000", "6f6000", "726000", "766000", "796000", "7d6000", "806000", "7e5500", "6f3105", "5d0001"] ], "GREEN_Orange_red": [ [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1], From 8b084bf60afd20d804bd5184b1773aa6e9b2c187 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Jul 2014 11:55:54 +0400 Subject: [PATCH 1025/1472] Also use CIE2000 in colors_find --- tools/colors_find.py | 49 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/tools/colors_find.py b/tools/colors_find.py index 6ba1489d..cf1ba1c5 100755 --- a/tools/colors_find.py +++ b/tools/colors_find.py @@ -1,39 +1,58 @@ #!/usr/bin/env python +from __future__ import division, print_function import sys import os +from colormath.color_objects import sRGBColor, LabColor +from colormath.color_conversions import convert_color +from colormath.color_diff import delta_e_cie2000 -def get_color(name, rgb): - return name, (int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16)) +def get_lab(name, rgb): + rgb = sRGBColor(int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16), + is_upscaled=True) + lab = convert_color(rgb, LabColor) + return name, lab with open(os.path.join(os.path.dirname(__file__), 'colors.map'), 'r') as f: - colors = [get_color(*line.split('\t')) for line in f] + colors = [get_lab(*line.split('\t')) for line in f] -urgb = get_color(None, sys.argv[1])[1] - - -def col_distance(rgb1, rgb2): - return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3))) +ulab = get_lab(None, sys.argv[1])[1] def find_color(urgb, colors): cur_distance = 3 * (255 ** 2 + 1) cur_color = None - for color, crgb in colors: - dist = col_distance(urgb, crgb) + for color, clab in colors: + dist = delta_e_cie2000(ulab, clab) if dist < cur_distance: cur_distance = dist - cur_color = (color, crgb) + cur_color = (color, clab) return cur_color -cur_color = find_color(urgb, colors) +cur_color = find_color(ulab, colors) -print urgb, ':', cur_color -col_1 = ';2;' + ';'.join((str(i) for i in urgb)) + 'm' -col_2 = ';2;' + ';'.join((str(i) for i in cur_color[1])) + 'm' +def lab_to_csi(lab): + rgb = convert_color(lab, sRGBColor) + colstr = ';2;' + ';'.join((str(i) for i in get_upscaled_values(rgb))) + return colstr + 'm' + + +def get_upscaled_values(rgb): + return [min(max(0, i), 255) for i in rgb.get_upscaled_value_tuple()] + + +def get_rgb(lab): + rgb = convert_color(lab, sRGBColor) + rgb = sRGBColor(*get_upscaled_values(rgb), is_upscaled=True) + return rgb.get_rgb_hex()[1:] + +print(get_rgb(ulab), ':', cur_color[0], ':', get_rgb(cur_color[1])) + +col_1 = lab_to_csi(ulab) +col_2 = lab_to_csi(cur_color[1]) sys.stdout.write('\033[48' + col_1 + '\033[38' + col_2 + 'abc\033[0m <-- bg:urgb, fg:crgb\n') sys.stdout.write('\033[48' + col_2 + '\033[38' + col_1 + 'abc\033[0m <-- bg:crgb, fg:urgb\n') From 473f647fc8a599cbe3aa08060de1701831734c05 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Jul 2014 22:42:28 +0400 Subject: [PATCH 1026/1472] Fix test_local_overrides.vim test broken by better-gradient branch --- tests/test_local_overrides.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index a58578fb..e7392adc 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -30,7 +30,7 @@ catch cquit endtry -if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                 %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_160_15485749_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold#  1%#Pl_22_24320_252_13684944_NONE#:1  ' +if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                 %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_160_15485749_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold#  1%#Pl_22_24576_252_13684944_NONE#:1  ' call writefile(['Unexpected result', result], 'message.fail') cquit endif From d1c2980ca4c495867860855253ebd1a754a63ec6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 20:12:25 +0400 Subject: [PATCH 1027/1472] Add a way to omit computing colors for terminal --- tools/generate_gradients.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 9441f92f..807f0338 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -53,6 +53,7 @@ p.add_argument('-r', '--range', metavar='V1 V2', type=num2, help='Use this range p.add_argument('-s', '--show', action='store_true', help='If present output gradient sample') p.add_argument('-p', '--palette', choices=('16', '256'), help='Use this palette. Defaults to 240-color palette (256 colors without first 16)') p.add_argument('-w', '--weights', metavar='INT INT ...', type=nums, help='Adjust weights of colors. Number of weights must be equal to number of colors') +p.add_argument('-C', '--omit-terminal', action='store_true', help='If present do not compute values for terminal') args = p.parse_args() @@ -156,16 +157,19 @@ palettes = { None: (cterm_to_lab[16:], lambda c: c + 16), } r = [get_rgb(lab) for lab in gradient] -r2 = [find_color(lab, *palettes[args.palette])[0] for lab in gradient] -r3 = [i[0] for i in groupby(r2)] +if not args.omit_terminal: + r2 = [find_color(lab, *palettes[args.palette])[0] for lab in gradient] + r3 = [i[0] for i in groupby(r2)] print(json.dumps(r)) -print(json.dumps(r2)) -print(json.dumps(r3)) +if not args.omit_terminal: + print(json.dumps(r2)) + print(json.dumps(r3)) if args.show: print_colors(args.gradient, args.num_output) print_colors(gradient, args.num_output) - print_colors(r2, args.num_output) - print_colors(r3, args.num_output) + if not args.omit_terminal: + print_colors(r2, args.num_output) + print_colors(r3, args.num_output) if not args.range and args.num_output >= 32 and (args.num_output - 1) // 10 >= 4 and (args.num_output - 1) % 10 == 0: sys.stdout.write('0') sys.stdout.write(''.join(('%*u' % (args.num_output // 10, i) for i in range(10, 101, 10)))) From ca0a8a2659702f9820f28b38c01bc9fc971db916 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 20:22:07 +0400 Subject: [PATCH 1028/1472] Refactor generate_gradients.py --- tools/generate_gradients.py | 118 ++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 51 deletions(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index 807f0338..d98ef90e 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -45,19 +45,6 @@ def nums(s): return [int(i) for i in s.split()] -p = argparse.ArgumentParser(description=__doc__) -p.add_argument('gradient', nargs='*', metavar='COLOR', type=color, help='List of colors (either indexes from 8-bit palette or 24-bit RGB in hexadecimal notation)') -p.add_argument('-n', '--num_items', metavar='INT', type=int, help='Number of items in resulting list', default=101) -p.add_argument('-N', '--num_output', metavar='INT', type=int, help='Number of characters in sample', default=101) -p.add_argument('-r', '--range', metavar='V1 V2', type=num2, help='Use this range when outputting scale') -p.add_argument('-s', '--show', action='store_true', help='If present output gradient sample') -p.add_argument('-p', '--palette', choices=('16', '256'), help='Use this palette. Defaults to 240-color palette (256 colors without first 16)') -p.add_argument('-w', '--weights', metavar='INT INT ...', type=nums, help='Adjust weights of colors. Number of weights must be equal to number of colors') -p.add_argument('-C', '--omit-terminal', action='store_true', help='If present do not compute values for terminal') - -args = p.parse_args() - - def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset): return start_value + ((offset - start_offset) * (stop_value - start_value) / (stop_offset - start_offset)) @@ -133,62 +120,91 @@ def dec_scale_generator(num): return r -m = args.num_items +def compute_steps(gradient, weights): + maxweight = len(gradient) - 1 + if weights: + weight_sum = sum(weights) + norm_weights = [100.0 * weight / weight_sum for weight in weights] + steps = [0] + for weight in norm_weights: + steps.append(steps[-1] + weight) + steps.pop(0) + steps.pop(0) + else: + step = m / maxweight + steps = [i * step for i in range(1, maxweight + 1)] + return steps -maxweight = len(args.gradient) - 1 -if args.weights: - weight_sum = sum(args.weights) - norm_weights = [100.0 * weight / weight_sum for weight in args.weights] - steps = [0] - for weight in norm_weights: - steps.append(steps[-1] + weight) - steps.pop(0) - steps.pop(0) -else: - step = m / maxweight - steps = [i * step for i in range(1, maxweight + 1)] -data = [(weight, args.gradient[i - 1], args.gradient[i]) for weight, i in zip(steps, range(1, len(args.gradient)))] -gr_func = generate_gradient_function(data) -gradient = [gr_func(y) for y in range(0, m)] palettes = { '16': (cterm_to_lab[:16], lambda c: c), '256': (cterm_to_lab, lambda c: c), None: (cterm_to_lab[16:], lambda c: c + 16), } -r = [get_rgb(lab) for lab in gradient] -if not args.omit_terminal: - r2 = [find_color(lab, *palettes[args.palette])[0] for lab in gradient] - r3 = [i[0] for i in groupby(r2)] -print(json.dumps(r)) -if not args.omit_terminal: - print(json.dumps(r2)) - print(json.dumps(r3)) -if args.show: - print_colors(args.gradient, args.num_output) - print_colors(gradient, args.num_output) - if not args.omit_terminal: - print_colors(r2, args.num_output) - print_colors(r3, args.num_output) - if not args.range and args.num_output >= 32 and (args.num_output - 1) // 10 >= 4 and (args.num_output - 1) % 10 == 0: + + +def show_scale(rng, num_output): + if not rng and num_output >= 32 and (num_output - 1) // 10 >= 4 and (num_output - 1) % 10 == 0: sys.stdout.write('0') - sys.stdout.write(''.join(('%*u' % (args.num_output // 10, i) for i in range(10, 101, 10)))) + sys.stdout.write(''.join(('%*u' % (num_output // 10, i) for i in range(10, 101, 10)))) sys.stdout.write('\n') else: - if args.range: - vmin, vmax = args.range[1] - isint = args.range[0] + if rng: + vmin, vmax = rng[1] + isint = rng[0] else: isint = True vmin = 0 vmax = 100 s = '' lasts = ' ' + str(vmax) - while len(s) + len(lasts) < args.num_output: + while len(s) + len(lasts) < num_output: curpc = len(s) + 1 if s else 0 - curval = vmin + curpc * (vmax - vmin) / args.num_output + curval = vmin + curpc * (vmax - vmin) / num_output if isint: curval = int(round(curval)) s += str(curval) + ' ' sys.stdout.write(s[:-1] + lasts + '\n') - sys.stdout.write(dec_scale_generator(args.num_output) + '\n') + sys.stdout.write(dec_scale_generator(num_output) + '\n') + + +if __name__ == '__main__': + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('gradient', nargs='*', metavar='COLOR', type=color, help='List of colors (either indexes from 8-bit palette or 24-bit RGB in hexadecimal notation)') + p.add_argument('-n', '--num_items', metavar='INT', type=int, help='Number of items in resulting list', default=101) + p.add_argument('-N', '--num_output', metavar='INT', type=int, help='Number of characters in sample', default=101) + p.add_argument('-r', '--range', metavar='V1 V2', type=num2, help='Use this range when outputting scale') + p.add_argument('-s', '--show', action='store_true', help='If present output gradient sample') + p.add_argument('-p', '--palette', choices=('16', '256'), help='Use this palette. Defaults to 240-color palette (256 colors without first 16)') + p.add_argument('-w', '--weights', metavar='INT INT ...', type=nums, help='Adjust weights of colors. Number of weights must be equal to number of colors') + p.add_argument('-C', '--omit-terminal', action='store_true', help='If present do not compute values for terminal') + + args = p.parse_args() + + m = args.num_items + + steps = compute_steps(args.gradient, args.weights) + + data = [(weight, args.gradient[i - 1], args.gradient[i]) + for weight, i in zip(steps, range(1, len(args.gradient)))] + gr_func = generate_gradient_function(data) + gradient = [gr_func(y) for y in range(0, m)] + + r = [get_rgb(lab) for lab in gradient] + if not args.omit_terminal: + r2 = [find_color(lab, *palettes[args.palette])[0] for lab in gradient] + r3 = [i[0] for i in groupby(r2)] + + print(json.dumps(r)) + if not args.omit_terminal: + print(json.dumps(r2)) + print(json.dumps(r3)) + + if args.show: + print_colors(args.gradient, args.num_output) + print_colors(gradient, args.num_output) + if not args.omit_terminal: + print_colors(r2, args.num_output) + print_colors(r3, args.num_output) + + show_scale(args.range, args.num_output) From a68e01ac24c5a4ddf7f3918c58c9b54d51ceffc7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 8 Jul 2014 18:12:54 +0400 Subject: [PATCH 1029/1472] Reverse JSON output and add trailing comma Makes it easier to copy-paste output to colors.json. --- tools/generate_gradients.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index d98ef90e..a667f301 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -2,7 +2,7 @@ # vim:fileencoding=utf-8:noet '''Gradients generator ''' -from __future__ import division +from __future__ import division, unicode_literals import sys import json from powerline.colorscheme import cterm_to_hex @@ -195,16 +195,16 @@ if __name__ == '__main__': r2 = [find_color(lab, *palettes[args.palette])[0] for lab in gradient] r3 = [i[0] for i in groupby(r2)] - print(json.dumps(r)) if not args.omit_terminal: - print(json.dumps(r2)) - print(json.dumps(r3)) + print(json.dumps(r3) + ',') + print(json.dumps(r2) + ',') + print(json.dumps(r)) if args.show: print_colors(args.gradient, args.num_output) - print_colors(gradient, args.num_output) if not args.omit_terminal: - print_colors(r2, args.num_output) print_colors(r3, args.num_output) + print_colors(r2, args.num_output) + print_colors(gradient, args.num_output) show_scale(args.range, args.num_output) From 39316c429ba63496eeb15436fb98008d126fc758 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 19:37:59 +0400 Subject: [PATCH 1030/1472] Reverse the gradient in battery segment Closes #910 --- powerline/segments/common.py | 10 +++++++--- tests/test_segments.py | 14 +++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 775b38e2..e69e0bf6 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1162,18 +1162,22 @@ def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥ 'contents': full_heart * numer, 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 99, + # Using zero as “nothing to worry about”: it is least alert color. + 'gradient_level': 0, }) ret.append({ 'contents': empty_heart * (denom - numer), 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 1, + # Using a hundred as it is most alert color. + 'gradient_level': 100, }) else: ret.append({ 'contents': format.format(capacity=(capacity / 100.0)), 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': capacity, + # Gradients are “least alert – most alert” by default, capacity has + # the opposite semantics. + 'gradient_level': 100 - capacity, }) return ret diff --git a/tests/test_segments.py b/tests/test_segments.py index 497a58ab..5a370460 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -533,30 +533,30 @@ class TestCommon(TestCase): self.assertEqual(common.battery(pl=pl), [{ 'contents': '86%', 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 86 + 'gradient_level': 14, }]) self.assertEqual(common.battery(pl=pl, format='{capacity:.2f}'), [{ 'contents': '0.86', 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 86 + 'gradient_level': 14, }]) self.assertEqual(common.battery(pl=pl, steps=7), [{ 'contents': '86%', 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 86 + 'gradient_level': 14, }]) self.assertEqual(common.battery(pl=pl, gamify=True), [ { 'contents': '♥♥♥♥', 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 99 + 'gradient_level': 0 }, { 'contents': '♥', 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 1 + 'gradient_level': 100 } ]) self.assertEqual(common.battery(pl=pl, gamify=True, full_heart='+', empty_heart='-', steps='10'), [ @@ -564,13 +564,13 @@ class TestCommon(TestCase): 'contents': '++++++++', 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 99 + 'gradient_level': 0 }, { 'contents': '--', 'draw_inner_divider': False, 'highlight_group': ['battery_gradient', 'battery'], - 'gradient_level': 1 + 'gradient_level': 100 } ]) From 2f7c44c29cf388d3ade7f760783ce06ceb93256b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 19:38:12 +0400 Subject: [PATCH 1031/1472] Document how to create gradient colors --- docs/source/configuration.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index b8e70ce6..9517d7a7 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -240,6 +240,9 @@ Color definitions * A list of cterm color indicies. * A list of hex color strings. + It is expected that you define gradients from least alert color to most + alert or use non-alert colors. + .. _config-colorschemes: Colorschemes From a0a5b44173ad7767b02f4a6e6b6d3d284b77c96a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 19:57:28 +0400 Subject: [PATCH 1032/1472] Use battery_full and battery_empty for displaying hearts Red hearts for battery full are still more optimal in case white hearts are for empty (though I do not understand why they have to be red). Cannot agree about per cents though: red is better for empty then white is if there are no related parts to compare. --- .../config_files/colorschemes/tmux/default.json | 2 ++ powerline/segments/common.py | 17 ++++++++++++----- tests/test_segments.py | 8 ++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json index b718a2ed..20b122d7 100644 --- a/powerline/config_files/colorschemes/tmux/default.json +++ b/powerline/config_files/colorschemes/tmux/default.json @@ -25,6 +25,8 @@ "environment": { "fg": "gray8", "bg": "gray0" }, "battery": { "fg": "gray8", "bg": "gray0" }, "battery_gradient": { "fg": "white_red", "bg": "gray0" }, + "battery_full": { "fg": "red", "bg": "gray0" }, + "battery_empty": { "fg": "white", "bg": "gray0" }, "now_playing": { "fg": "gray10", "bg": "black" } } } diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e69e0bf6..79b24c52 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1140,14 +1140,21 @@ def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥ Number of discrete steps to show between 0% and 100% capacity if gamify is True. :param bool gamify: - Measure in hearts (♥) instead of percentages. + Measure in hearts (♥) instead of percentages. For full hearts + ``battery_full`` highlighting group is preferred, for empty hearts there + is ``battery_empty``. :param str full_heart: Heart displayed for “full” part of battery. :param str empty_heart: Heart displayed for “used” part of battery. It is also displayed using - another gradient level, so it is OK for it to be the same as full_heart. + another gradient level and highlighting group, so it is OK for it to be + the same as full_heart as long as necessary highlighting groups are + defined. - Highlight groups used: ``battery_gradient`` (gradient), ``battery``. + ``battery_gradient`` and ``battery`` groups are used in any case, first is + preferred. + + Highlight groups used: ``battery_full`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_empty`` or ``battery_gradient`` (gradient) or ``battery``. ''' try: capacity = _get_capacity(pl) @@ -1161,14 +1168,14 @@ def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥ ret.append({ 'contents': full_heart * numer, 'draw_inner_divider': False, - 'highlight_group': ['battery_gradient', 'battery'], + 'highlight_group': ['battery_full', 'battery_gradient', 'battery'], # Using zero as “nothing to worry about”: it is least alert color. 'gradient_level': 0, }) ret.append({ 'contents': empty_heart * (denom - numer), 'draw_inner_divider': False, - 'highlight_group': ['battery_gradient', 'battery'], + 'highlight_group': ['battery_empty', 'battery_gradient', 'battery'], # Using a hundred as it is most alert color. 'gradient_level': 100, }) diff --git a/tests/test_segments.py b/tests/test_segments.py index 5a370460..95a38782 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -549,13 +549,13 @@ class TestCommon(TestCase): { 'contents': '♥♥♥♥', 'draw_inner_divider': False, - 'highlight_group': ['battery_gradient', 'battery'], + 'highlight_group': ['battery_full', 'battery_gradient', 'battery'], 'gradient_level': 0 }, { 'contents': '♥', 'draw_inner_divider': False, - 'highlight_group': ['battery_gradient', 'battery'], + 'highlight_group': ['battery_empty', 'battery_gradient', 'battery'], 'gradient_level': 100 } ]) @@ -563,13 +563,13 @@ class TestCommon(TestCase): { 'contents': '++++++++', 'draw_inner_divider': False, - 'highlight_group': ['battery_gradient', 'battery'], + 'highlight_group': ['battery_full', 'battery_gradient', 'battery'], 'gradient_level': 0 }, { 'contents': '--', 'draw_inner_divider': False, - 'highlight_group': ['battery_gradient', 'battery'], + 'highlight_group': ['battery_empty', 'battery_gradient', 'battery'], 'gradient_level': 100 } ]) From 551c9f8bed936af23e543fce8ad01856231c1d82 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 20:00:33 +0400 Subject: [PATCH 1033/1472] Add two more colors to white_red gradient cterm colors Picked up with tools/generate_gradients.py 231 223 216 209 196 --- powerline/config_files/colors.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index e1c484c4..4a357ac0 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -103,7 +103,7 @@ ["19b4fe", "1bb2fc", "1db1fa", "1faff8", "22aef6", "24adf4", "26abf2", "29aaf0", "2ba9ee", "2da7ec", "30a6ea", "32a5e8", "34a3e6", "36a2e4", "39a0e2", "3b9fe1", "3d9edf", "409cdd", "429bdb", "449ad9", "4798d7", "4997d5", "4b96d3", "4d94d1", "5093cf", "5292cd", "5490cb", "578fc9", "598dc7", "5b8cc6", "5e8bc4", "6089c2", "6288c0", "6487be", "6785bc", "6984ba", "6b83b8", "6e81b6", "7080b4", "727eb2", "757db0", "777cae", "797aac", "7b79ab", "7e78a9", "8076a7", "8275a5", "8574a3", "8772a1", "89719f", "8c709d", "8e6e9b", "906d99", "926b97", "956a95", "976993", "996791", "9c668f", "9e658e", "a0638c", "a3628a", "a56188", "a75f86", "a95e84", "ac5c82", "ae5b80", "b05a7e", "b3587c", "b5577a", "b75678", "ba5476", "bc5374", "be5273", "c05071", "c34f6f", "c54e6d", "c74c6b", "ca4b69", "cc4967", "ce4865", "d14763", "d34561", "d5445f", "d7435d", "da415b", "dc4059", "de3f58", "e13d56", "e33c54", "e53a52", "e83950", "ea384e", "ec364c", "ee354a", "f13448", "f33246", "f53144", "f83042", "fa2e40"] ], "white_red": [ - [231, 223, 216, 209, 196], + [231, 255, 223, 216, 209, 202, 196], ["ffffff", "fffe61", "fffcc4", "fffb28", "fff98b", "fff7ef", "fff651", "fff4b4", "fff318", "fff17b", "ffefdf", "ffee41", "ffeca4", "ffeb08", "ffe96b", "ffe7cf", "ffe631", "ffe494", "ffe2f8", "ffe15b", "ffdfbf", "ffde21", "ffdc84", "ffdae8", "ffd94b", "ffd7af", "ffd602", "ffd455", "ffd2aa", "ffd0fd", "ffcf50", "ffcda5", "ffcbf8", "ffca4b", "ffc8a0", "ffc6f3", "ffc546", "ffc39b", "ffc1ee", "ffc041", "ffbe96", "ffbce9", "ffbb3c", "ffb991", "ffb7e4", "ffb637", "ffb48c", "ffb2df", "ffb132", "ffaf87", "ffadda", "ffac2d", "ffaa82", "ffa8d5", "ffa728", "ffa57d", "ffa3d0", "ffa223", "ffa078", "ff9ecb", "ff9d1e", "ff9b73", "ff99c6", "ff9819", "ff966e", "ff94c1", "ff9314", "ff9169", "ff8fbc", "ff8e0f", "ff8c64", "ff8ab7", "ff890a", "ff875f", "ff81f4", "ff7c8a", "ff771f", "ff71b5", "ff6c4c", "ff66e1", "ff6177", "ff5c0c", "ff56a2", "ff5139", "ff4bce", "ff4664", "ff40f9", "ff3b8f", "ff3626", "ff30bb", "ff2b51", "ff25e6", "ff207c", "ff1b13", "ff15a8", "ff103e", "ff0ad3", "ff0569", "ff0000"] ] } From af2d73ad7bda63c57aa601d951c43237403814ca Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 7 Jul 2014 20:37:07 +0400 Subject: [PATCH 1034/1472] Regenerate gui colors for white_red gradient Command used to generate colors: tools/generate_gradients.py -C 231 255 223 216 209 202 196 --weights='1 1 1 1 1 1 3' --- powerline/config_files/colors.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 4a357ac0..6a8283b9 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -104,7 +104,7 @@ ], "white_red": [ [231, 255, 223, 216, 209, 202, 196], - ["ffffff", "fffe61", "fffcc4", "fffb28", "fff98b", "fff7ef", "fff651", "fff4b4", "fff318", "fff17b", "ffefdf", "ffee41", "ffeca4", "ffeb08", "ffe96b", "ffe7cf", "ffe631", "ffe494", "ffe2f8", "ffe15b", "ffdfbf", "ffde21", "ffdc84", "ffdae8", "ffd94b", "ffd7af", "ffd602", "ffd455", "ffd2aa", "ffd0fd", "ffcf50", "ffcda5", "ffcbf8", "ffca4b", "ffc8a0", "ffc6f3", "ffc546", "ffc39b", "ffc1ee", "ffc041", "ffbe96", "ffbce9", "ffbb3c", "ffb991", "ffb7e4", "ffb637", "ffb48c", "ffb2df", "ffb132", "ffaf87", "ffadda", "ffac2d", "ffaa82", "ffa8d5", "ffa728", "ffa57d", "ffa3d0", "ffa223", "ffa078", "ff9ecb", "ff9d1e", "ff9b73", "ff99c6", "ff9819", "ff966e", "ff94c1", "ff9314", "ff9169", "ff8fbc", "ff8e0f", "ff8c64", "ff8ab7", "ff890a", "ff875f", "ff81f4", "ff7c8a", "ff771f", "ff71b5", "ff6c4c", "ff66e1", "ff6177", "ff5c0c", "ff56a2", "ff5139", "ff4bce", "ff4664", "ff40f9", "ff3b8f", "ff3626", "ff30bb", "ff2b51", "ff25e6", "ff207c", "ff1b13", "ff15a8", "ff103e", "ff0ad3", "ff0569", "ff0000"] + ["ffffff", "fefefe", "fdfdfd", "fdfdfd", "fcfcfc", "fbfbfb", "fafafa", "fafafa", "f9f9f9", "f8f8f8", "f7f7f7", "f7f7f7", "f6f6f6", "f5f5f5", "f4f4f4", "f4f3f4", "f3f3f3", "f2f2f2", "f1f1f1", "f0f0f0", "f0f0f0", "efefef", "eeeeee", "efecea", "f1eae4", "f2e8de", "f3e6d8", "f5e4d3", "f6e2cd", "f7e0c7", "f8dec2", "f9dcbc", "fadab6", "fad8b1", "fbd5ac", "fbd2a9", "fbcea5", "fbcaa1", "fbc79e", "fbc39a", "fbc097", "fbbc93", "fbb88f", "fbb58c", "fab188", "faad85", "faaa81", "fba67e", "fba37a", "fb9f76", "fb9c73", "fb986f", "fb946c", "fb9168", "fa8d65", "fa8961", "fa865c", "fa8256", "fb7f4f", "fb7b48", "fb7841", "fb743a", "fb7133", "fb6d2c", "fa6a23", "fa661a", "fa620e", "fa5f03", "fa5d03", "fa5b03", "fa5a03", "fa5803", "fa5703", "fa5503", "fa5303", "fa5103", "fa4f03", "fa4e03", "fa4c03", "fa4a04", "fa4804", "fa4604", "fa4404", "fa4204", "fa3f04", "fa3d04", "fa3b04", "fa3805", "fa3605", "fa3305", "fb3105", "fb2e05", "fb2a05", "fb2705", "fb2306", "fb1f06", "fb1b06", "fb1506", "fb0e06", "fa0506", "fa0007"] ] } } From e2a49417b6f4065909af41783bc1f59063931a83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 8 Jul 2014 18:09:21 +0400 Subject: [PATCH 1035/1472] Default line_percent_gradient to (light|dark)_green_gray gradients dark_green_gray gradient was generated with tools/generate_gradients.py 70 247 . It is used for non-insert modes in the current window. light_green_gray gradient was generated with ./tools/generate_gradients.py 148 250 . It is used for insert mode in the current window. --- powerline/config_files/colors.json | 8 ++++++++ powerline/config_files/colorschemes/vim/default.json | 7 ++++--- tests/test_local_overrides.vim | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 6a8283b9..68b69f6e 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -105,6 +105,14 @@ "white_red": [ [231, 255, 223, 216, 209, 202, 196], ["ffffff", "fefefe", "fdfdfd", "fdfdfd", "fcfcfc", "fbfbfb", "fafafa", "fafafa", "f9f9f9", "f8f8f8", "f7f7f7", "f7f7f7", "f6f6f6", "f5f5f5", "f4f4f4", "f4f3f4", "f3f3f3", "f2f2f2", "f1f1f1", "f0f0f0", "f0f0f0", "efefef", "eeeeee", "efecea", "f1eae4", "f2e8de", "f3e6d8", "f5e4d3", "f6e2cd", "f7e0c7", "f8dec2", "f9dcbc", "fadab6", "fad8b1", "fbd5ac", "fbd2a9", "fbcea5", "fbcaa1", "fbc79e", "fbc39a", "fbc097", "fbbc93", "fbb88f", "fbb58c", "fab188", "faad85", "faaa81", "fba67e", "fba37a", "fb9f76", "fb9c73", "fb986f", "fb946c", "fb9168", "fa8d65", "fa8961", "fa865c", "fa8256", "fb7f4f", "fb7b48", "fb7841", "fb743a", "fb7133", "fb6d2c", "fa6a23", "fa661a", "fa620e", "fa5f03", "fa5d03", "fa5b03", "fa5a03", "fa5803", "fa5703", "fa5503", "fa5303", "fa5103", "fa4f03", "fa4e03", "fa4c03", "fa4a04", "fa4804", "fa4604", "fa4404", "fa4204", "fa3f04", "fa3d04", "fa3b04", "fa3805", "fa3605", "fa3305", "fb3105", "fb2e05", "fb2a05", "fb2705", "fb2306", "fb1f06", "fb1b06", "fb1506", "fb0e06", "fa0506", "fa0007"] + ], + "dark_green_gray": [ + [70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247], + ["51b000", "52b000", "54b000", "55b002", "56b007", "57b00d", "58b011", "59af15", "5aaf18", "5caf1b", "5daf1e", "5eaf21", "5faf23", "60ae25", "61ae27", "62ae2a", "63ae2c", "64ae2e", "65ae30", "66ae31", "67ad33", "68ad35", "69ad37", "69ad38", "6aad3a", "6bad3c", "6cac3d", "6dac3f", "6eac40", "6fac42", "70ac44", "70ac45", "71ab47", "72ab48", "73ab49", "74ab4b", "75ab4c", "75ab4e", "76aa4f", "77aa51", "78aa52", "79aa53", "79aa55", "7aaa56", "7ba957", "7ca959", "7ca95a", "7da95b", "7ea95d", "7fa95e", "7fa85f", "80a861", "81a862", "81a863", "82a865", "83a766", "83a767", "84a768", "85a76a", "85a76b", "86a66c", "87a66d", "87a66f", "88a670", "89a671", "89a672", "8aa574", "8ba575", "8ba576", "8ca577", "8da579", "8da47a", "8ea47b", "8ea47c", "8fa47d", "90a47f", "90a380", "91a381", "91a382", "92a384", "93a385", "93a286", "94a287", "94a288", "95a28a", "95a18b", "96a18c", "97a18d", "97a18e", "98a190", "98a091", "99a092", "99a093", "9aa094", "9aa096", "9b9f97", "9b9f98", "9c9f99", "9c9f9a", "9d9e9c", "9d9e9d"] + ], + "light_green_gray": [ + [148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 187, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250], + ["a3d900", "a4d800", "a4d800", "a5d805", "a5d80d", "a6d714", "a6d719", "a6d71d", "a7d621", "a7d625", "a8d628", "a8d62b", "a8d52e", "a9d531", "a9d533", "aad536", "aad438", "aad43a", "abd43d", "abd33f", "abd341", "acd343", "acd345", "acd247", "add249", "add24b", "add14d", "aed14f", "aed151", "aed152", "afd054", "afd056", "afd058", "b0d059", "b0cf5b", "b0cf5d", "b1cf5e", "b1ce60", "b1ce62", "b1ce63", "b2ce65", "b2cd67", "b2cd68", "b3cd6a", "b3cc6b", "b3cc6d", "b3cc6e", "b4cc70", "b4cb71", "b4cb73", "b4cb75", "b5ca76", "b5ca78", "b5ca79", "b5ca7a", "b6c97c", "b6c97d", "b6c97f", "b6c880", "b6c882", "b7c883", "b7c885", "b7c786", "b7c788", "b7c789", "b8c68a", "b8c68c", "b8c68d", "b8c68f", "b8c590", "b9c591", "b9c593", "b9c494", "b9c496", "b9c497", "b9c498", "bac39a", "bac39b", "bac39d", "bac29e", "bac29f", "bac2a1", "bac2a2", "bac1a4", "bbc1a5", "bbc1a6", "bbc0a8", "bbc0a9", "bbc0aa", "bbc0ac", "bbbfad", "bbbfae", "bbbfb0", "bbbeb1", "bcbeb3", "bcbeb4", "bcbdb5", "bcbdb7", "bcbdb8", "bcbdb9", "bcbcbb"] ] } } diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 2cdef8db..4d2fb4a5 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -25,7 +25,7 @@ "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4" }, "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, "line_percent": { "fg": "gray9", "bg": "gray4" }, - "line_percent_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, + "line_percent_gradient": { "fg": "dark_green_gray", "bg": "gray4" }, "position": { "fg": "gray9", "bg": "gray4" }, "position_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, @@ -55,7 +55,7 @@ "gray9": "gray4", "gray10": "gray5", "white": "gray6", - "green_yellow_red": "gray5" + "dark_green_gray": "gray5" } }, "i": { @@ -71,7 +71,8 @@ "gray8": "mediumcyan", "gray9": "mediumcyan", "gray10": "mediumcyan", - "green_yellow_red": "gray5" + "green_yellow_red": "gray5", + "dark_green_gray": "light_green_gray" }, "groups": { "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }, diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index e7392adc..c054df78 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -30,7 +30,7 @@ catch cquit endtry -if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                 %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_160_15485749_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold#  1%#Pl_22_24576_252_13684944_NONE#:1  ' +if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                 %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_247_10329757_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold#  1%#Pl_22_24576_252_13684944_NONE#:1  ' call writefile(['Unexpected result', result], 'message.fail') cquit endif From d478c239a70be9bc826b0c969d10215224ef26bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Jul 2014 22:49:47 +0400 Subject: [PATCH 1036/1472] Enable pypy tests in .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 14195f22..4eccbf31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "3.2" - "3.3" - "3.4" + - "pypy" install: tests/install.sh script: tests/test.sh From 6c0018b7a3141ca616dabc1fc46a209eb34fbb13 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 19:16:27 +0400 Subject: [PATCH 1037/1472] Purge use_errno argument from function prototype calls - It is already contained in prototype definition. - PyPy is not able to run code with use_errno there. --- powerline/lib/inotify.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index 0e8211eb..c2b25444 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -46,17 +46,17 @@ def load_inotify(): # inotify_add_watch() prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) add_watch = prototype(('inotify_add_watch', libc), ( - (1, "fd"), (1, "pathname"), (1, "mask")), use_errno=True) + (1, "fd"), (1, "pathname"), (1, "mask"))) # inotify_rm_watch() prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) rm_watch = prototype(('inotify_rm_watch', libc), ( - (1, "fd"), (1, "wd")), use_errno=True) + (1, "fd"), (1, "wd"))) # read() prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) read = prototype(('read', libc), ( - (1, "fd"), (1, "buf"), (1, "count")), use_errno=True) + (1, "fd"), (1, "buf"), (1, "count"))) _inotify = (init1, add_watch, rm_watch, read) return _inotify From 19195159adc5ec0df35fc8cb7064c6f2f0b570f5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 19:29:56 +0400 Subject: [PATCH 1038/1472] Only install mercurial and bazaar if using CPython --- tests/install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/install.sh b/tests/install.sh index 510e6a96..05d2e3d2 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -3,8 +3,10 @@ pip install . pip install psutil if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 - pip install mercurial - pip install --allow-external bzr --allow-unverified bzr bzr + if python -c 'import platform, sys; sys.exit(1 - (platform.python_implementation() == "CPython"))' ; then + pip install mercurial + pip install --allow-external bzr --allow-unverified bzr bzr + fi if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 pip install unittest2 argparse From 871ce5727b1721abc1d48f9f9d2d7f6d9a5e7b30 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 19:49:37 +0400 Subject: [PATCH 1039/1472] Set use_mercurial and use_bzr to False in PyPy --- tests/test_lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index c69d97b6..fa22da41 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -13,6 +13,7 @@ import threading import os import sys import re +import platform from time import sleep from subprocess import call, PIPE from functools import partial @@ -466,7 +467,8 @@ class TestFilesystemWatchers(TestCase): os.rename(f, f + '1') changed() -use_mercurial = use_bzr = sys.version_info < (3, 0) +use_mercurial = use_bzr = (sys.version_info < (3, 0) + and platform.python_implementation() == 'CPython') class TestVCS(TestCase): From 585153466cdc433abd271fefa789939865efa46f Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 20:19:11 +0400 Subject: [PATCH 1040/1472] Replace on_*_change with callbacks generator --- powerline/__init__.py | 50 +++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 83678be4..374f82df 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -83,6 +83,13 @@ def get_fallback_logger(): return _fallback_logger +def _generate_change_callback(lock, key, dictionary): + def on_file_change(path): + with lock: + dictionary[key] = True + return on_file_change + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -136,12 +143,16 @@ class Powerline(object): self.find_config_file = lambda cfg_path: find_config_file(config_paths, cfg_path) self.cr_kwargs_lock = Lock() - self.create_renderer_kwargs = { - 'load_main': True, - 'load_colors': True, - 'load_colorscheme': True, - 'load_theme': True, - } + self.create_renderer_kwargs = {} + self.cr_callbacks = {} + for key in ('main', 'colors', 'colorscheme', 'theme'): + self.create_renderer_kwargs['load_' + key] = True + self.cr_callbacks[key] = _generate_change_callback( + self.cr_kwargs_lock, + 'load_' + key, + self.create_renderer_kwargs + ) + self.shutdown_event = shutdown_event or Event() self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event, run_once=run_once) self.run_loader_update = False @@ -324,7 +335,7 @@ class Powerline(object): def _load_config(self, cfg_path, type): '''Load configuration and setup watches.''' - function = getattr(self, 'on_' + type + '_change') + function = self.cr_callbacks[type] try: path = self.find_config_file(cfg_path) except IOError: @@ -334,7 +345,7 @@ class Powerline(object): return self.config_loader.load(path) def _purge_configs(self, type): - function = getattr(self, 'on_' + type + '_change') + function = self.cr_callbacks[type] self.config_loader.unregister_functions(set((function,))) self.config_loader.unregister_missing(set(((self.find_config_file, function),))) @@ -457,31 +468,10 @@ class Powerline(object): self.renderer.shutdown() except AttributeError: pass - functions = ( - self.on_main_change, - self.on_colors_change, - self.on_colorscheme_change, - self.on_theme_change, - ) + functions = tuple(self.cr_callbacks.values()) self.config_loader.unregister_functions(set(functions)) self.config_loader.unregister_missing(set(((find_config_file, function) for function in functions))) - def on_main_change(self, path): - with self.cr_kwargs_lock: - self.create_renderer_kwargs['load_main'] = True - - def on_colors_change(self, path): - with self.cr_kwargs_lock: - self.create_renderer_kwargs['load_colors'] = True - - def on_colorscheme_change(self, path): - with self.cr_kwargs_lock: - self.create_renderer_kwargs['load_colorscheme'] = True - - def on_theme_change(self, path): - with self.cr_kwargs_lock: - self.create_renderer_kwargs['load_theme'] = True - def __enter__(self): return self From c36e14fd3c72704ad03bf2db13bf443c8c5a9bc4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 20:23:41 +0400 Subject: [PATCH 1041/1472] Move get_config_paths out from Powerline class --- powerline/__init__.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 374f82df..bbb8e0b3 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -90,6 +90,24 @@ def _generate_change_callback(lock, key, dictionary): return on_file_change +def get_config_paths(): + '''Get configuration paths from environment variables. + + Uses $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS according to the XDG specification. + + :return: list of paths + ''' + config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) + config_path = os.path.join(config_home, 'powerline') + config_paths = [config_path] + config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR) + if config_dirs is not None: + config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')]) + plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') + config_paths.append(plugin_path) + return config_paths + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -321,17 +339,12 @@ class Powerline(object): def get_config_paths(): '''Get configuration paths. + Should be overridden in subclasses in order to provide a way to override + used paths. + :return: list of paths ''' - config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) - config_path = os.path.join(config_home, 'powerline') - config_paths = [config_path] - config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR) - if config_dirs is not None: - config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')]) - plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') - config_paths.append(plugin_path) - return config_paths + return get_config_paths() def _load_config(self, cfg_path, type): '''Load configuration and setup watches.''' From 289094362e8c4e55f721b86e87d25b82f5948e03 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 20:27:24 +0400 Subject: [PATCH 1042/1472] Rename find_config_file to _find_config_file --- powerline/__init__.py | 6 +++--- powerline/lint/__init__.py | 2 +- tests/lib/config_mock.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index bbb8e0b3..33438d13 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -13,7 +13,7 @@ from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR from threading import Lock, Event -def find_config_file(search_paths, config_file): +def _find_config_file(search_paths, config_file): config_file += '.json' for path in search_paths: config_file_path = os.path.join(path, config_file) @@ -158,7 +158,7 @@ class Powerline(object): self.renderer_module = self.renderer_module[:-1] config_paths = self.get_config_paths() - self.find_config_file = lambda cfg_path: find_config_file(config_paths, cfg_path) + self.find_config_file = lambda cfg_path: _find_config_file(config_paths, cfg_path) self.cr_kwargs_lock = Lock() self.create_renderer_kwargs = {} @@ -483,7 +483,7 @@ class Powerline(object): pass functions = tuple(self.cr_callbacks.values()) self.config_loader.unregister_functions(set(functions)) - self.config_loader.unregister_missing(set(((find_config_file, function) for function in functions))) + self.config_loader.unregister_missing(set(((self.find_config_file, function) for function in functions))) def __enter__(self): return self diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 928f144e..01f12cb7 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8:noet from powerline.lint.markedjson import load -from powerline import find_config_file, Powerline +from powerline import _find_config_file as find_config_file, Powerline from powerline.lib.config import load_json_config from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index f4f50624..22dad715 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -22,7 +22,7 @@ def load_json_config(config_file_path, *args, **kwargs): raise IOError(config_file_path) -def find_config_file(config, search_paths, config_file): +def _find_config_file(config, search_paths, config_file): if config_file.endswith('raise') and config_file not in config: raise IOError('fcf:' + config_file) return config_file @@ -158,7 +158,7 @@ def swap_attributes(cfg_container, powerline_module, replaces): config_container = cfg_container if not replaces: replaces = { - 'find_config_file': lambda *args: find_config_file(config_container['config'], *args), + '_find_config_file': lambda *args: _find_config_file(config_container['config'], *args), } for attr, val in replaces.items(): old_val = getattr(powerline_module, attr) From 768e64bcdb6df49282082b848e6a45c305bcfe65 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 20:32:39 +0400 Subject: [PATCH 1043/1472] Add generate_config_finder function --- powerline/__init__.py | 20 ++++++++++++++++++-- powerline/lint/__init__.py | 9 +++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 33438d13..950c0f34 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -108,6 +108,23 @@ def get_config_paths(): return config_paths +def generate_config_finder(get_config_paths=get_config_paths): + '''Generate find_config_file function + + This function will find .json file given its path. + + :param function get_config_paths: + Function that being called with no arguments will return a list of paths + that should be searched for configuration files. + + :return: + Function that being given configuration file name will return full path + to it or raise IOError if it failed to find the file. + ''' + config_paths = get_config_paths() + return lambda cfg_path: _find_config_file(config_paths, cfg_path) + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -157,8 +174,7 @@ class Powerline(object): elif self.renderer_module[-1] == '.': self.renderer_module = self.renderer_module[:-1] - config_paths = self.get_config_paths() - self.find_config_file = lambda cfg_path: _find_config_file(config_paths, cfg_path) + self.find_config_file = generate_config_finder(self.get_config_paths) self.cr_kwargs_lock = Lock() self.create_renderer_kwargs = {} diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 01f12cb7..611f0606 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8:noet from powerline.lint.markedjson import load -from powerline import _find_config_file as find_config_file, Powerline +from powerline import generate_config_finder, get_config_paths from powerline.lib.config import load_json_config from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes @@ -1035,7 +1035,8 @@ theme_spec = (Spec( def check(path=None, debug=False): - search_paths = [path] if path else Powerline.get_config_paths() + search_paths = [path] if path else get_config_paths() + find_config_file = generate_config_finder(lambda: search_paths) logger = logging.getLogger('powerline-lint') logger.setLevel(logging.DEBUG if debug else logging.ERROR) @@ -1092,7 +1093,7 @@ def check(path=None, debug=False): hadproblem = False try: - main_config = load_json_config(find_config_file(search_paths, 'config'), load=load_config, open_file=open_file) + main_config = load_json_config(find_config_file('config'), load=load_config, open_file=open_file) except IOError: main_config = {} sys.stderr.write('\nConfiguration file not found: config.json\n') @@ -1108,7 +1109,7 @@ def check(path=None, debug=False): import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] try: - colors_config = load_json_config(find_config_file(search_paths, 'colors'), load=load_config, open_file=open_file) + colors_config = load_json_config(find_config_file('colors'), load=load_config, open_file=open_file) except IOError: colors_config = {} sys.stderr.write('\nConfiguration file not found: colors.json\n') From c891d75c2bf12c3a5c2b19a1fb06079d69def0a7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 20:43:50 +0400 Subject: [PATCH 1044/1472] Move _load_config body to a separate function --- powerline/__init__.py | 44 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 950c0f34..ef6a4824 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -125,6 +125,36 @@ def generate_config_finder(get_config_paths=get_config_paths): return lambda cfg_path: _find_config_file(config_paths, cfg_path) +def load_config(cfg_path, find_config_file, config_loader, loader_callback=None): + '''Load configuration file and setup watches + + Watches are only set up if loader_callback is not None. + + :param str cfg_path: + Path for configuration file that should be loaded. + :param function find_config_file: + Function that finds configuration file. Check out the description of + the return value of ``generate_config_finder`` function. + :param ConfigLoader config_loader: + Configuration file loader class instance. + :param function loader_callback: + Function that will be called by config_loader when change to + configuration file is detected. + + :return: Configuration file contents. + ''' + try: + path = find_config_file(cfg_path) + except IOError: + if loader_callback: + config_loader.register_missing(find_config_file, loader_callback, cfg_path) + raise + else: + if loader_callback: + config_loader.register(loader_callback, path) + return config_loader.load(path) + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -364,14 +394,12 @@ class Powerline(object): def _load_config(self, cfg_path, type): '''Load configuration and setup watches.''' - function = self.cr_callbacks[type] - try: - path = self.find_config_file(cfg_path) - except IOError: - self.config_loader.register_missing(self.find_config_file, function, cfg_path) - raise - self.config_loader.register(function, path) - return self.config_loader.load(path) + return load_config( + cfg_path, + self.find_config_file, + self.config_loader, + self.cr_callbacks[type] + ) def _purge_configs(self, type): function = self.cr_callbacks[type] From 0fc7856b1be82a5261dbbfa23d857fa395b71607 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 20:45:28 +0400 Subject: [PATCH 1045/1472] Rename create_renderer_kwargs to cr_kwargs for consistency --- powerline/__init__.py | 20 ++++++++++---------- tests/lib/config_mock.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index ef6a4824..f485b422 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -207,14 +207,14 @@ class Powerline(object): self.find_config_file = generate_config_finder(self.get_config_paths) self.cr_kwargs_lock = Lock() - self.create_renderer_kwargs = {} + self.cr_kwargs = {} self.cr_callbacks = {} for key in ('main', 'colors', 'colorscheme', 'theme'): - self.create_renderer_kwargs['load_' + key] = True + self.cr_kwargs['load_' + key] = True self.cr_callbacks[key] = _generate_change_callback( self.cr_kwargs_lock, 'load_' + key, - self.create_renderer_kwargs + self.cr_kwargs ) self.shutdown_event = shutdown_event or Event() @@ -460,23 +460,23 @@ class Powerline(object): '''Updates/creates a renderer if needed.''' if self.run_loader_update: self.config_loader.update() - create_renderer_kwargs = None + cr_kwargs = None with self.cr_kwargs_lock: - if self.create_renderer_kwargs: - create_renderer_kwargs = self.create_renderer_kwargs.copy() - if create_renderer_kwargs: + if self.cr_kwargs: + cr_kwargs = self.cr_kwargs.copy() + if cr_kwargs: try: - self.create_renderer(**create_renderer_kwargs) + self.create_renderer(**cr_kwargs) except Exception as e: self.exception('Failed to create renderer: {0}', str(e)) if hasattr(self, 'renderer'): with self.cr_kwargs_lock: - self.create_renderer_kwargs.clear() + self.cr_kwargs.clear() else: raise else: with self.cr_kwargs_lock: - self.create_renderer_kwargs.clear() + self.cr_kwargs.clear() def render(self, *args, **kwargs): '''Update/create renderer if needed and pass all arguments further to diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 22dad715..30672cba 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -114,7 +114,7 @@ class TestPowerline(Powerline): return local_themes def _will_create_renderer(self): - return self.create_renderer_kwargs + return self.cr_kwargs renderer = SimpleRenderer From 8f442ac01bff011e0c4ec5782165a7b7d7f29d25 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 21:04:33 +0400 Subject: [PATCH 1046/1472] Move some code from Powerline.create_renderer to create_logger --- powerline/__init__.py | 66 ++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index f485b422..3a627276 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -155,6 +155,42 @@ def load_config(cfg_path, find_config_file, config_loader, loader_callback=None) return config_loader.load(path) +def _get_log_handler(common_config): + '''Get log handler. + + :param dict common_config: + Configuration dictionary used to create handler. + + :return: logging.Handler subclass. + ''' + log_file = common_config['log_file'] + if log_file: + log_file = os.path.expanduser(log_file) + log_dir = os.path.dirname(log_file) + if not os.path.isdir(log_dir): + os.mkdir(log_dir) + return logging.FileHandler(log_file) + else: + return logging.StreamHandler() + + +def create_logger(common_config): + '''Create logger according to provided configuration + ''' + log_format = common_config['log_format'] + formatter = logging.Formatter(log_format) + + level = getattr(logging, common_config['log_level']) + handler = _get_log_handler(common_config) + handler.setLevel(level) + handler.setFormatter(formatter) + + logger = logging.getLogger('powerline') + logger.setLevel(level) + logger.addHandler(handler) + return logger + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -275,17 +311,7 @@ class Powerline(object): self.import_paths = self.common_config['paths'] if not self.logger: - log_format = self.common_config['log_format'] - formatter = logging.Formatter(log_format) - - level = getattr(logging, self.common_config['log_level']) - handler = self.get_log_handler() - handler.setLevel(level) - handler.setFormatter(formatter) - - self.logger = logging.getLogger('powerline') - self.logger.setLevel(level) - self.logger.addHandler(handler) + self.logger = create_logger(self.common_config) if not self.pl: self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext) @@ -363,24 +389,6 @@ class Powerline(object): else: self.renderer = renderer - def get_log_handler(self): - '''Get log handler. - - :param dict common_config: - Common configuration. - - :return: logging.Handler subclass. - ''' - log_file = self.common_config['log_file'] - if log_file: - log_file = os.path.expanduser(log_file) - log_dir = os.path.dirname(log_file) - if not os.path.isdir(log_dir): - os.mkdir(log_dir) - return logging.FileHandler(log_file) - else: - return logging.StreamHandler() - @staticmethod def get_config_paths(): '''Get configuration paths. From 3d8ce55e014527fc00ce738d7ab4281253ecbd30 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 21:11:35 +0400 Subject: [PATCH 1047/1472] Move code that sets config.json/common defaults to a separate function --- powerline/__init__.py | 44 +++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 3a627276..2d4fba9c 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -191,6 +191,35 @@ def create_logger(common_config): return logger +def finish_common_config(common_config): + '''Add default values to common config and expand ~ in paths + + :param dict common_config: + Common configuration, as it was just loaded. + + :return: + Copy of common configuration with all configuration keys and expanded + paths. + ''' + common_config = common_config.copy() + common_config.setdefault('paths', []) + common_config.setdefault('watcher', 'auto') + common_config.setdefault('log_level', 'WARNING') + common_config.setdefault('log_format', '%(asctime)s:%(levelname)s:%(message)s') + common_config.setdefault('term_truecolor', False) + common_config.setdefault('ambiwidth', 1) + common_config.setdefault('additional_escapes', None) + common_config.setdefault('reload_config', True) + common_config.setdefault('interval', None) + common_config.setdefault('log_file', None) + + common_config['paths'] = [ + os.path.expanduser(path) for path in common_config['paths'] + ] + + return common_config + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -293,20 +322,7 @@ class Powerline(object): self.prev_common_config = self.common_config - self.common_config.setdefault('paths', []) - self.common_config.setdefault('watcher', 'auto') - self.common_config.setdefault('log_level', 'WARNING') - self.common_config.setdefault('log_format', '%(asctime)s:%(levelname)s:%(message)s') - self.common_config.setdefault('term_truecolor', False) - self.common_config.setdefault('ambiwidth', 1) - self.common_config.setdefault('additional_escapes', None) - self.common_config.setdefault('reload_config', True) - self.common_config.setdefault('interval', None) - self.common_config.setdefault('log_file', None) - - self.common_config['paths'] = [ - os.path.expanduser(path) for path in self.common_config['paths'] - ] + self.common_config = finish_common_config(self.common_config) self.import_paths = self.common_config['paths'] From 44ce483ee5c816d4242019f98fa9d0569e0a52b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 21:13:39 +0400 Subject: [PATCH 1048/1472] Use powerline.shell.run_cmd to get tmux version Closes #912 --- powerline/bindings/config.py | 24 ++++++++++++++++++------ scripts/powerline-config | 6 ++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 16722a98..3591903a 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -8,6 +8,9 @@ import subprocess import re from powerline.config import TMUX_CONFIG_DIRECTORY +from powerline.lib.config import ConfigLoader +from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config +from powerline.lib.shell import run_cmd TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) @@ -32,9 +35,9 @@ def run_tmux_command(*args): _run_tmux(subprocess.check_call, args) -def get_tmux_output(*args): +def get_tmux_output(pl, *args): '''Run tmux command and return its output''' - return _run_tmux(subprocess.check_output, args) + return _run_tmux(lambda cmd: run_cmd(pl, cmd), args) NON_DIGITS = re.compile('[^0-9]+') @@ -42,8 +45,8 @@ DIGITS = re.compile('[0-9]+') NON_LETTERS = re.compile('[^a-z]+') -def get_tmux_version(): - version_string = get_tmux_output('-V') +def get_tmux_version(pl): + version_string = get_tmux_output(pl, '-V') _, version_string = version_string.split(' ') version_string = version_string.strip() major, minor = version_string.split('.') @@ -96,7 +99,7 @@ def get_tmux_configs(version): yield (fname, priority + file_version.minor * 10 + file_version.major * 10000) -def source_tmux_files(): +def source_tmux_files(pl, args): '''Source relevant version-specific tmux configuration files Files are sourced in the following order: @@ -104,6 +107,15 @@ def source_tmux_files(): * If files for same versions are to be sourced then first _minus files are sourced, then _plus files and then files without _minus or _plus suffixes. ''' - version = get_tmux_version() + version = get_tmux_version(pl) for fname, priority in sorted(get_tmux_configs(version), key=(lambda v: v[1])): run_tmux_command('source', fname) + + +def create_powerline_logger(args): + find_config_file = generate_config_finder() + config_loader = ConfigLoader(run_once=True) + config = load_config('config', find_config_file, config_loader) + common_config = finish_common_config(config['common']) + logger = create_logger(common_config) + return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config') diff --git a/scripts/powerline-config b/scripts/powerline-config index 151dc1d5..028ab783 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -12,7 +12,7 @@ except ImportError: TMUX_ACTIONS = { - 'source': (lambda args: config.source_tmux_files()), + 'source': config.source_tmux_files, } @@ -30,4 +30,6 @@ if __name__ == '__main__': args = parser.parse_args() - args.function(args) + pl = config.create_powerline_logger(args) + + args.function(pl, args) From a3e29a6cd61a55315cdfcb29ba73f4de47612d55 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:12:08 +0400 Subject: [PATCH 1049/1472] =?UTF-8?q?Get=20rid=20of=20lots=20of=20NotImple?= =?UTF-8?q?mentedError=E2=80=99s=20in=20travis=20log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/vim.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index ef37db71..04a340ac 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -179,6 +179,22 @@ def eval(expr): return '0' elif expr.startswith('exists('): return '0' + elif expr.startswith('getwinvar('): + import re + match = re.match(r'^getwinvar\((\d+), "(\w+)"\)$', expr) + if not match: + raise NotImplementedError + winnr = int(match.group(1)) + varname = match.group(2) + return _emul_getwinvar(winnr, varname) + elif expr.startswith('has_key('): + import re + match = re.match(r'^has_key\(getwinvar\((\d+), ""\), "(\w+)"\)$', expr) + if not match: + raise NotImplementedError + winnr = int(match.group(1)) + varname = match.group(2) + return 0 + (varname in windows[winnr - 1].vars) elif expr == 'getbufvar("%", "NERDTreeRoot").path.str()': import os assert os.path.basename(buffers[_buffer()].name).startswith('NERD_tree_') @@ -239,12 +255,12 @@ def _emul_getbufvar(bufnr, varname): @_vim @_str_func def _emul_getwinvar(winnr, varname): - return windows[winnr].vars[varname] + return windows[winnr - 1].vars.get(varname, '') @_vim def _emul_setwinvar(winnr, varname, value): - windows[winnr].vars[varname] = value + windows[winnr - 1].vars[varname] = value @_vim From 0ab80742c4dd49346ee13d6f0908c98f6bc6c26d Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:21:29 +0400 Subject: [PATCH 1050/1472] Add _with('wvars') --- tests/vim.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/vim.py b/tests/vim.py index 04a340ac..6a00b39c 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -729,6 +729,8 @@ def _with(key, *args, **kwargs): return _WithDict(options, **kwargs) elif key == 'globals': return _WithDict(vars, **kwargs) + elif key == 'wvars': + return _WithDict(windows[_window - 1].vars, **kwargs) elif key == 'environ': return _WithDict(_environ, **kwargs) elif key == 'split': From 3f8c8c942686ab3d59e4c0287584ba9fa304730c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:22:24 +0400 Subject: [PATCH 1051/1472] Prefix _Windows.{append,insert,pop} methods with underscore There are no such methods in Vim. --- tests/vim.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index 6a00b39c..4f53baeb 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -103,15 +103,15 @@ class _Windows(object): return not not self.l @_vim - def pop(self, *args, **kwargs): + def _pop(self, *args, **kwargs): return self.l.pop(*args, **kwargs) @_vim - def append(self, *args, **kwargs): + def _append(self, *args, **kwargs): return self.l.append(*args, **kwargs) @_vim - def index(self, *args, **kwargs): + def _index(self, *args, **kwargs): return self.l.index(*args, **kwargs) @@ -367,14 +367,14 @@ class _Window(object): self.buffer = _Buffer(**buffer) else: self.buffer = _Buffer() - windows.append(self) + windows._append(self) _window_id += 1 _window_ids.append(_window_id) self.options = {} self.vars = {} def __repr__(self): - return '' + return '' _buf_lines = {} @@ -559,7 +559,7 @@ def _split(): @_vim def _del_window(winnr): - win = windows.pop(winnr - 1) + win = windows._pop(winnr - 1) _window_ids.pop(winnr) return win From e436bdbbfae4438a8b3c8a665d2cd219276e794b Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:26:14 +0400 Subject: [PATCH 1052/1472] Remove failing _index, use (self.number - 1) instead --- tests/vim.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index 4f53baeb..4e7dadba 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -110,10 +110,6 @@ class _Windows(object): def _append(self, *args, **kwargs): return self.l.append(*args, **kwargs) - @_vim - def _index(self, *args, **kwargs): - return self.l.index(*args, **kwargs) - windows = _Windows() @@ -374,7 +370,7 @@ class _Window(object): self.vars = {} def __repr__(self): - return '' + return '' _buf_lines = {} From f9af425054d1ae239fa248ced0f417aef8e01b16 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:32:07 +0400 Subject: [PATCH 1053/1472] Add missing winnr key to segments_dictionary --- tests/vim.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/vim.py b/tests/vim.py index 4e7dadba..2f8488fa 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -495,6 +495,7 @@ def _get_segment_info(): mode = mode_translations.get(mode, mode) return { 'window': windows[_window - 1], + 'winnr': _window, 'buffer': buffers[_buffer()], 'bufnr': _buffer(), 'window_id': _window_ids[_window], From 3ac75eeca6444367adef11c640a7f07ec4d94ab5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:32:41 +0400 Subject: [PATCH 1054/1472] Add window_title tests --- tests/test_segments.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index 95a38782..441d6951 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -667,6 +667,13 @@ class TestVim(TestCase): self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), [{'divider_highlight_group': 'background:divider', 'contents': 'python'}]) + def test_window_title(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.window_title(pl=pl, segment_info=segment_info), None) + with vim_module._with('wvars', quickfix_title='Abc'): + self.assertEqual(vim.window_title(pl=pl, segment_info=segment_info), 'Abc') + def test_line_percent(self): pl = Pl() segment_info = vim_module._get_segment_info() From a92d72b5d7e3fcd093360814ea5fadcea7c7ad70 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 22:54:27 +0400 Subject: [PATCH 1055/1472] Respect the fact that vim.eval in Vim returns str instances --- tests/vim.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/vim.py b/tests/vim.py index 2f8488fa..c55b0251 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -44,6 +44,24 @@ def _vim(func): return f +def _unicode(func): + from functools import wraps + import sys + + if sys.version_info < (3,): + return func + + @wraps(func) + def f(*args, **kwargs): + from powerline.lib.unicode import u + ret = func(*args, **kwargs) + if isinstance(ret, bytes): + ret = u(ret) + return ret + + return f + + class _Buffers(object): @_vim def __init__(self): @@ -163,6 +181,7 @@ def command(cmd): @_vim +@_unicode def eval(expr): if expr.startswith('g:'): return vars[expr[2:]] From 128921454fdf6a4103a40478886a2827fc2e7895 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 23:08:59 +0400 Subject: [PATCH 1056/1472] Correct _construct_result for Python-3 --- tests/vim.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index c55b0251..89d12b65 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -142,10 +142,13 @@ def _construct_result(r): if sys.version_info < (3,): return r else: - if type(r) is str: + if isinstance(r, str): return r.encode('utf-8') - elif type(r) is dict or type(r) is list: - raise NotImplementedError + elif isinstance(r, list): + return [_construct_result(i) for i in r] + elif isinstance(r, dict): + return dict(((_construct_result(k), _construct_result(v)) + for k, v in r.items())) return r From 93c3b287855f9398ff9047380009c15180a0af98 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 23:18:16 +0400 Subject: [PATCH 1057/1472] Use better type converted for python-3* Converter already existed, just needed to make it used by getbufvar --- powerline/bindings/vim/__init__.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 5ee5225e..40e1f616 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -41,20 +41,28 @@ else: vim_get_func = VimFunc -# It may crash on some old vim versions and I do not remember in which patch -# I fixed this crash. -if hasattr(vim, 'vvars') and vim.vvars['version'] > 703: +if hasattr(vim, 'bindeval'): _vim_to_python_types = { - vim.Dictionary: lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), - vim.List: lambda value: [_vim_to_python(item) for item in value], - vim.Function: lambda _: None, + getattr(vim, 'Dictionary', None) or type(vim.bindeval('{}')): + lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), + getattr(vim, 'List', None) or type(vim.bindeval('[]')): + lambda value: [_vim_to_python(item) for item in value], + getattr(vim, 'Function', None) or type(vim.bindeval('function("mode")')): + lambda _: None, } + if sys.version_info >= (3,): + _vim_to_python_types[bytes] = lambda value: value.decode('utf-8') + _id = lambda value: value def _vim_to_python(value): return _vim_to_python_types.get(type(value), _id)(value) + +# It may crash on some old vim versions and I do not remember in which patch +# I fixed this crash. +if hasattr(vim, 'vvars') and vim.vvars['version'] > 703: def vim_getvar(varname): return _vim_to_python(vim.vars[str(varname)]) @@ -102,10 +110,7 @@ else: _getbufvar = vim_get_func('getbufvar') def getbufvar(*args): - r = _getbufvar(*args) - if type(r) is bytes: - return r.decode('utf-8') - return r + return _vim_to_python(_getbufvar(*args)) class VimEnviron(object): From ff7be9ac4019bdacdce52888f5029e99699bd210 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 10 Jul 2014 23:40:15 +0400 Subject: [PATCH 1058/1472] Set VIRTUAL_ENV_DISABLE_PROMPT to 1 Otherwise powerline prompt is being prefixed by `(virtualenv-name)` in zsh which looks rather ugly. In bash prompt is always overridden by PROMPT_COMMAND, thus there is no actual need to set this variable. --- powerline/bindings/bash/powerline.sh | 1 + powerline/bindings/zsh/powerline.zsh | 1 + 2 files changed, 2 insertions(+) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 7094ae5f..67c1d8bb 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -43,6 +43,7 @@ _powerline_prompt() { } _powerline_setup_prompt() { + VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_COMMAND}" ; then if which powerline-client &>/dev/null ; then export POWERLINE_COMMAND=powerline-client diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 797c216f..aad0df53 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -111,6 +111,7 @@ _powerline_setup_prompt() { fi done precmd_functions+=( _powerline_set_jobnum ) + VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_NO_ZSH_ZPYTHON}" && { zmodload libzpython || zmodload zsh/zpython } &>/dev/null ; then precmd_functions+=( _powerline_update_counter ) zpython 'from powerline.bindings.zsh import setup as _powerline_setup' From 97266b7ffc93cb0e9ce843dacb14b865b8493bf1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Feb 2014 21:37:42 +0400 Subject: [PATCH 1059/1472] Initial support for #770 What was done: - Implemented loading using configuration hierarhy as proposed in the issue - Implemented group aliasing What was not: - Some tests (config_reload) are failing - Other (test_configuration) are spamming console with unexpected messages - No support for powerline-lint - No tests for new functionality - Specifically I have not checked whether group aliasing actually works - Colorschemes were not ported Some other things: I have named this branch `config-ng` because I have other ideas about configuration and it would be good to include them making only one possibly backwards-incompatible merge commit instead of many. Specifically I am going to rebase `merge-config` branch here. --- powerline/__init__.py | 56 ++++++++++++++++++++++++++++++++++++- powerline/colorscheme.py | 43 ++++++++++++++++++---------- tests/test_config_reload.py | 31 +++++++++++--------- 3 files changed, 101 insertions(+), 29 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 2d4fba9c..e982375d 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -9,6 +9,7 @@ from powerline.colorscheme import Colorscheme from powerline.lib.config import ConfigLoader from powerline.lib.unicode import safe_unicode, FailedUnicode from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR +from powerline.lib import mergedicts from threading import Lock, Event @@ -220,6 +221,22 @@ def finish_common_config(common_config): return common_config +if sys.version_info < (3,): + # `raise exception[0], None, exception[1]` is a SyntaxError in python-3* + # Not using ('''…''') because this syntax does not work in python-2.6 + exec(('def reraise(exception):\n' + ' if type(exception) is tuple:\n' + ' raise exception[0], None, exception[1]\n' + ' else:\n' + ' raise exception\n')) +else: + def reraise(exception): + if type(exception) is tuple: + raise exception[0].with_traceback(exception[1]) + else: + raise exception + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -455,7 +472,38 @@ class Powerline(object): :return: dictionary with :ref:`colorscheme configuration `. ''' - return self._load_config(os.path.join('colorschemes', self.ext, name), 'colorscheme') + # TODO Make sure no colorscheme name ends with __ (do it in + # powerline-lint) + levels = ( + os.path.join('colorschemes', name), + os.path.join('colorschemes', self.ext, '__main__'), + os.path.join('colorschemes', self.ext, name), + ) + config = {} + loaded = 0 + exceptions = [] + for cfg_path in levels: + try: + lvl_config = self._load_config(cfg_path, 'colorscheme') + except IOError as e: + if sys.version_info < (3,): + tb = sys.exc_info()[2] + exceptions.append((e, tb)) + else: + exceptions.append(e) + else: + if not cfg_path.endswith('__'): + loaded += 1 + mergedicts(config, lvl_config) + if not loaded: + for exception in exceptions: + if type(exception) is tuple: + e = exception[0] + else: + e = exception + self.exception('Failed to load colorscheme: {0}', e, exception=exception) + raise e + return config def load_colors_config(self): '''Get colorscheme. @@ -562,5 +610,11 @@ class Powerline(object): def exception(self, msg, *args, **kwargs): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' + exception = kwargs.pop('exception', None) pl = getattr(self, 'pl', None) or get_fallback_logger() + if exception: + try: + reraise(exception) + except Exception: + return pl.exception(msg, *args, **kwargs) return pl.exception(msg, *args, **kwargs) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 3b30118f..a49c5c09 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,6 +1,10 @@ # vim:fileencoding=utf-8:noet from copy import copy +try: + from __builtin__ import unicode +except ImportError: + unicode = str DEFAULT_MODE_KEY = None @@ -70,32 +74,41 @@ class Colorscheme(object): else: return self.colors[gradient] - def get_highlighting(self, groups, mode, gradient_level=None): - trans = self.translations.get(mode, {}) - for group in hl_iter(groups): - if 'groups' in trans and group in trans['groups']: + def get_group_props(self, mode, trans, group, translate_colors=True): + if isinstance(group, (str, unicode)): + try: + group_props = trans['groups'][group] + except KeyError: try: - group_props = trans['groups'][group] + group_props = self.groups[group] except KeyError: - continue - break - + return None + else: + return self.get_group_props(mode, trans, group_props, True) else: - try: - group_props = copy(self.groups[group]) - except KeyError: - continue - + return self.get_group_props(mode, trans, group_props, False) + else: + if translate_colors: + group_props = copy(group) try: ctrans = trans['colors'] + except KeyError: + pass + else: for key in ('fg', 'bg'): try: group_props[key] = ctrans[group_props[key]] except KeyError: pass - except KeyError: - pass + return group_props + else: + return group + def get_highlighting(self, groups, mode, gradient_level=None): + trans = self.translations.get(mode, {}) + for group in hl_iter(groups): + group_props = self.get_group_props(mode, trans, group) + if group_props: break else: raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(hl_iter(groups))) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 4d33128e..7f3c184f 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -101,7 +101,7 @@ class TestConfigReload(TestCase): with get_powerline(run_once=True) as p: with replace_item(globals(), 'config', deepcopy(config)): self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['config']['common']['spaces'] = 1 add_watcher_events(p, 'config', wait=False, interval=0.05) # When running once thread should not start @@ -117,7 +117,7 @@ class TestConfigReload(TestCase): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['config']['common']['spaces'] = 1 add_watcher_events(p, 'config') @@ -141,14 +141,19 @@ class TestConfigReload(TestCase): config['config']['ext']['test']['colorscheme'] = 'nonexistent' add_watcher_events(p, 'config') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents('config', 'colorschemes/test/nonexistent') + self.assertAccessEvents('config', 'colorschemes/nonexistent', 'colorschemes/test/__main__', 'colorschemes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load colorscheme: colorschemes/nonexistent', + 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/__main__', + 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/nonexistent', + 'exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent' + ]) config['config']['ext']['test']['colorscheme'] = '2' add_watcher_events(p, 'config') self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>>') - self.assertAccessEvents('config', 'colorschemes/test/2') + self.assertAccessEvents('config', 'colorschemes/2', 'colorschemes/test/__main__', 'colorschemes/test/2') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['theme'] = '2' @@ -170,7 +175,7 @@ class TestConfigReload(TestCase): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' add_watcher_events(p, 'config') @@ -192,7 +197,7 @@ class TestConfigReload(TestCase): }, } while not p._will_create_renderer(): - sleep(0.000001) + sleep(0.1) self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') self.assertAccessEvents('colorschemes/test/nonexistentraise') self.assertEqual(p.logger._pop_msgs(), []) @@ -202,7 +207,7 @@ class TestConfigReload(TestCase): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['colors']['colors']['col1'] = 5 add_watcher_events(p, 'colors') @@ -215,12 +220,12 @@ class TestConfigReload(TestCase): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' add_watcher_events(p, 'colorschemes/test/default') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('colorschemes/test/default') + self.assertAccessEvents('colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default') self.assertEqual(p.logger._pop_msgs(), []) pop_events() @@ -228,7 +233,7 @@ class TestConfigReload(TestCase): with get_powerline(run_once=False) as p: with replace_item(globals(), 'config', deepcopy(config)): self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default') @@ -242,7 +247,7 @@ class TestConfigReload(TestCase): config['config']['common']['interval'] = None with get_powerline(run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default', wait=False) @@ -257,7 +262,7 @@ class TestConfigReload(TestCase): config['config']['common']['interval'] = None with get_powerline(run_once=True) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default', wait=False) From e79eada2a0c2a409ada97bd8ebccce61afb33f5d Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 5 Feb 2014 08:56:26 +0400 Subject: [PATCH 1060/1472] Move some color defintions to colorscheme/*.json --- .../config_files/colorschemes/default.json | 42 +++++++++++++++++++ .../colorschemes/ipython/default.json | 5 +-- .../colorschemes/shell/__main__.json | 6 +++ .../colorschemes/shell/default.json | 23 +++------- .../colorschemes/shell/solarized.json | 23 +++------- .../config_files/colorschemes/solarized.json | 17 ++++++++ .../colorschemes/tmux/default.json | 32 -------------- .../colorschemes/vim/solarized.json | 2 +- .../colorschemes/vim/solarizedlight.json | 2 +- .../config_files/colorschemes/wm/default.json | 26 ------------ 10 files changed, 80 insertions(+), 98 deletions(-) create mode 100644 powerline/config_files/colorschemes/default.json create mode 100644 powerline/config_files/colorschemes/shell/__main__.json create mode 100644 powerline/config_files/colorschemes/solarized.json delete mode 100644 powerline/config_files/colorschemes/tmux/default.json delete mode 100644 powerline/config_files/colorschemes/wm/default.json diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json new file mode 100644 index 00000000..77bec02d --- /dev/null +++ b/powerline/config_files/colorschemes/default.json @@ -0,0 +1,42 @@ +{ + "name": "Default", + "groups": { + "background:divider": { "fg": "gray5", "bg": "gray0" }, + "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, + "date": { "fg": "gray8", "bg": "gray2" }, + "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, + "time:divider": { "fg": "gray5", "bg": "gray2" }, + "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, + "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attr": ["bold"] }, + "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, + "weather": { "fg": "gray8", "bg": "gray0" }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, + "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, + "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, + "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, + "uptime": { "fg": "gray8", "bg": "gray0" }, + "external_ip": { "fg": "gray8", "bg": "gray0" }, + "network_load": { "fg": "gray8", "bg": "gray0" }, + "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, + "system_load": { "fg": "gray8", "bg": "gray0" }, + "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, + "environment": { "fg": "gray8", "bg": "gray0" }, + "cpu_load_percent": { "fg": "gray8", "bg": "gray0" }, + "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, + "battery": { "fg": "gray8", "bg": "gray0" }, + "battery_gradient": { "fg": "white_red", "bg": "gray0" }, + "battery_full": { "fg": "red", "bg": "gray0" }, + "battery_empty": { "fg": "white", "bg": "gray0" }, + "now_playing": { "fg": "gray10", "bg": "black" }, + "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, + "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, + "branch": { "fg": "gray9", "bg": "gray2" }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray2" }, + "branch_clean": { "fg": "gray9", "bg": "gray2" }, + "branch:divider": { "fg": "gray7", "bg": "gray2" }, + "cwd": { "fg": "gray9", "bg": "gray4" }, + "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, + "cwd:divider": { "fg": "gray7", "bg": "gray4" }, + "virtualenv": { "fg": "white", "bg": "darkcyan" } + } +} diff --git a/powerline/config_files/colorschemes/ipython/default.json b/powerline/config_files/colorschemes/ipython/default.json index d7875916..60585c0d 100644 --- a/powerline/config_files/colorschemes/ipython/default.json +++ b/powerline/config_files/colorschemes/ipython/default.json @@ -1,8 +1,7 @@ { "name": "Default color scheme for IPython prompt", "groups": { - "virtualenv": { "fg": "white", "bg": "darkcyan" }, - "prompt": { "fg": "gray9", "bg": "gray4" }, - "prompt_count": { "fg": "white", "bg": "gray4" } + "prompt": { "fg": "gray9", "bg": "gray4" }, + "prompt_count": { "fg": "white", "bg": "gray4" } } } diff --git a/powerline/config_files/colorschemes/shell/__main__.json b/powerline/config_files/colorschemes/shell/__main__.json new file mode 100644 index 00000000..c8c2aaa2 --- /dev/null +++ b/powerline/config_files/colorschemes/shell/__main__.json @@ -0,0 +1,6 @@ +{ + "groups": { + "continuation": "cwd", + "continuation:current": "cwd:current_folder" + } +} diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 2efd3796..46483940 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -1,23 +1,12 @@ { "name": "Default color scheme for shell prompts", "groups": { - "jobnum": { "fg": "brightyellow", "bg": "mediumorange" }, - "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, - "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, - "virtualenv": { "fg": "white", "bg": "darkcyan" }, - "branch": { "fg": "gray9", "bg": "gray2" }, - "branch_dirty": { "fg": "brightyellow", "bg": "gray2" }, - "branch_clean": { "fg": "gray9", "bg": "gray2" }, - "continuation": { "fg": "gray9", "bg": "gray4" }, - "continuation:current": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, - "cwd": { "fg": "gray9", "bg": "gray4" }, - "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, - "cwd:divider": { "fg": "gray7", "bg": "gray4" }, - "hostname": { "fg": "brightyellow", "bg": "mediumorange" }, - "exit_fail": { "fg": "white", "bg": "darkestred" }, - "exit_success": { "fg": "white", "bg": "darkestgreen" }, - "environment": { "fg": "white", "bg": "darkestgreen" }, - "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } + "hostname": { "fg": "brightyellow", "bg": "mediumorange" }, + "jobnum": { "fg": "brightyellow", "bg": "mediumorange" }, + "exit_fail": { "fg": "white", "bg": "darkestred" }, + "exit_success": { "fg": "white", "bg": "darkestgreen" }, + "environment": { "fg": "white", "bg": "darkestgreen" }, + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } }, "mode_translations": { "vicmd": { diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index 612941d0..2916940a 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,23 +1,10 @@ { - "name": "Solarized Dark", + "name": "Solarized dark for shell", "groups": { - "jobnum": { "fg": "oldlace", "bg": "darkgreencopper" }, - "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, - "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, - "virtualenv": { "fg": "oldlace", "bg": "green" }, - "branch": { "fg": "gray61", "bg": "royalblue5" }, - "branch_dirty": { "fg": "yellow", "bg": "royalblue5" }, - "branch_clean": { "fg": "gray61", "bg": "royalblue5" }, - "continuation": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "continuation:current": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, - "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, - "exit_fail": { "fg": "oldlace", "bg": "red" }, - "exit_success": { "fg": "oldlace", "bg": "green" }, - "environment": { "fg": "oldlace", "bg": "green" }, - "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } + "jobnum": { "fg": "oldlace", "bg": "darkgreencopper" }, + "exit_fail": { "fg": "oldlace", "bg": "red" }, + "exit_success": { "fg": "oldlace", "bg": "green" }, + "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } }, "mode_translations": { "vicmd": { diff --git a/powerline/config_files/colorschemes/solarized.json b/powerline/config_files/colorschemes/solarized.json new file mode 100644 index 00000000..3e901e50 --- /dev/null +++ b/powerline/config_files/colorschemes/solarized.json @@ -0,0 +1,17 @@ +{ + "name": "Solarized dark", + "groups": { + "background": { "fg": "oldlace", "bg": "royalblue5" }, + "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, + "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, + "virtualenv": { "fg": "oldlace", "bg": "green" }, + "branch": { "fg": "gray61", "bg": "royalblue5" }, + "branch_dirty": { "fg": "yellow", "bg": "royalblue5" }, + "branch_clean": { "fg": "gray61", "bg": "royalblue5" }, + "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, + "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, + "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, + "environment": { "fg": "oldlace", "bg": "green" } + } +} diff --git a/powerline/config_files/colorschemes/tmux/default.json b/powerline/config_files/colorschemes/tmux/default.json deleted file mode 100644 index 20b122d7..00000000 --- a/powerline/config_files/colorschemes/tmux/default.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "Default color scheme for terminal prompts", - "groups": { - "background:divider": { "fg": "gray5", "bg": "gray0" }, - "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, - "date": { "fg": "gray8", "bg": "gray2" }, - "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, - "time:divider": { "fg": "gray5", "bg": "gray2" }, - "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, - "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attr": ["bold"] }, - "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, - "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, - "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, - "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, - "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, - "uptime": { "fg": "gray8", "bg": "gray0" }, - "external_ip": { "fg": "gray8", "bg": "gray0" }, - "network_load": { "fg": "gray8", "bg": "gray0" }, - "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, - "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, - "cpu_load_percent": { "fg": "gray8", "bg": "gray0" }, - "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, - "environment": { "fg": "gray8", "bg": "gray0" }, - "battery": { "fg": "gray8", "bg": "gray0" }, - "battery_gradient": { "fg": "white_red", "bg": "gray0" }, - "battery_full": { "fg": "red", "bg": "gray0" }, - "battery_empty": { "fg": "white", "bg": "gray0" }, - "now_playing": { "fg": "gray10", "bg": "black" } - } -} diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 4822573e..0ea4fd39 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,5 +1,5 @@ { - "name": "Solarized Dark", + "name": "Solarized dark for vim", "groups": { "background": { "fg": "oldlace", "bg": "royalblue5" }, "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5" }, diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index b8e4c254..2622cb1a 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -1,5 +1,5 @@ { - "name": "Solarized Light", + "name": "Solarized light for vim", "groups": { "background": { "fg": "gray13", "bg": "darkgreencopper" }, "background:divider": { "fg": "azure4", "bg": "darkgreencopper" }, diff --git a/powerline/config_files/colorschemes/wm/default.json b/powerline/config_files/colorschemes/wm/default.json deleted file mode 100644 index 445da4f6..00000000 --- a/powerline/config_files/colorschemes/wm/default.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Default color scheme for window managers", - "groups": { - "background:divider": { "fg": "gray5", "bg": "gray0" }, - "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, - "date": { "fg": "gray8", "bg": "gray2" }, - "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, - "time:divider": { "fg": "gray5", "bg": "gray2" }, - "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, - "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attr": ["bold"] }, - "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, - "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, - "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, - "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, - "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, - "uptime": { "fg": "gray8", "bg": "gray0" }, - "external_ip": { "fg": "gray8", "bg": "gray0" }, - "network_load": { "fg": "gray8", "bg": "gray0" }, - "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_good": { "fg": "lightyellowgreen", "bg": "gray0" }, - "system_load_bad": { "fg": "gold3", "bg": "gray0" }, - "system_load_ugly": { "fg": "orangered", "bg": "gray0" }, - "environment": { "fg": "gray8", "bg": "gray0" } - } -} From d72e4f5081c05991db7904a385814c019e06f486 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 5 Feb 2014 09:12:04 +0400 Subject: [PATCH 1061/1472] Update documentation --- docs/source/configuration.rst | 42 +++++++++++++++++++++++------------ powerline/__init__.py | 2 ++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 9517d7a7..d4f47551 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -16,7 +16,9 @@ Powerline provides default configurations in the following locations: `Main configuration`_ :file:`powerline/config.json` `Colorschemes`_ - :file:`powerline/colorschemes/{extension}/default.json` + :file:`powerline/colorschemes/{name}.json`, + :file:`powerline/colorscheme/__main__.json`, + :file:`powerline/colorschemes/{extension}/{name}.json` `Themes`_ :file:`powerline/themes/{extension}/default.json` @@ -248,7 +250,14 @@ Color definitions Colorschemes ============ -:Location: :file:`powerline/colorschemes/{extension}/{name}.json` +:Location: :file:`powerline/colorschemes/{name}.json`, + :file:`powerline/colorscheme/__main__.json`, + :file:`powerline/colorschemes/{extension}/{name}.json` + +Colorscheme files are processed in order given: definitions from each next file +override those from each previous file. It is required that either +:file:`powerline/colorschemes/{name}.json`, or +:file:`powerline/colorschemes/{extension}/{name}.json` exists. ``name`` Name of the colorscheme. @@ -258,21 +267,26 @@ Colorschemes ``groups`` Segment highlighting groups, consisting of a dict where the key is the name of the highlighting group (usually the function name for function - segments), and the value is a dict that defines the foreground color, - background color and optional attributes: + segments), and the value is either - ``fg`` - Foreground color. Must be defined in :ref:`colors - `. + #) a dict that defines the foreground color, background color and optional + attributes: - ``bg`` - Background color. Must be defined in :ref:`colors - `. + ``fg`` + Foreground color. Must be defined in :ref:`colors + `. - ``attr`` - Optional list of attributes. Valid values are one or more of - ``bold``, ``italic`` and ``underline``. Note that some attributes - may be unavailable in some applications or terminal emulators. + ``bg`` + Background color. Must be defined in :ref:`colors + `. + + ``attr`` + List of attributes. Valid values are one or more of ``bold``, + ``italic`` and ``underline``. Note that some attributes may be + unavailable in some applications or terminal emulators. + + #) a string (an alias): a name of existing group. This group’s definition + will be used when this color is requested. ``mode_translations`` Mode-specific highlighting for extensions that support it (e.g. the vim diff --git a/powerline/__init__.py b/powerline/__init__.py index e982375d..bf3e1212 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -494,6 +494,8 @@ class Powerline(object): else: if not cfg_path.endswith('__'): loaded += 1 + # TODO Either make sure `attr` list is always present or make + # mergedicts not merge group definitions. mergedicts(config, lvl_config) if not loaded: for exception in exceptions: From 7f94583324333451416fb7c54dbdea368c709bc4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 11:58:59 +0400 Subject: [PATCH 1062/1472] Make attr colorscheme group key required --- docs/source/configuration.rst | 5 +- .../config_files/colorschemes/default.json | 58 ++++++------ .../colorschemes/ipython/default.json | 4 +- .../colorschemes/shell/default.json | 10 +- .../colorschemes/shell/solarized.json | 6 +- .../config_files/colorschemes/solarized.json | 18 ++-- .../colorschemes/vim/default.json | 58 ++++++------ .../colorschemes/vim/solarized.json | 90 +++++++++--------- .../colorschemes/vim/solarizedlight.json | 92 +++++++++---------- 9 files changed, 171 insertions(+), 170 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index d4f47551..c3c8c110 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -269,7 +269,7 @@ override those from each previous file. It is required that either name of the highlighting group (usually the function name for function segments), and the value is either - #) a dict that defines the foreground color, background color and optional + #) a dict that defines the foreground color, background color and attributes: ``fg`` @@ -283,7 +283,8 @@ override those from each previous file. It is required that either ``attr`` List of attributes. Valid values are one or more of ``bold``, ``italic`` and ``underline``. Note that some attributes may be - unavailable in some applications or terminal emulators. + unavailable in some applications or terminal emulators. If you do not + need any attributes leave this empty. #) a string (an alias): a name of existing group. This group’s definition will be used when this color is requested. diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json index 77bec02d..a015fe6b 100644 --- a/powerline/config_files/colorschemes/default.json +++ b/powerline/config_files/colorschemes/default.json @@ -1,42 +1,42 @@ { "name": "Default", "groups": { - "background:divider": { "fg": "gray5", "bg": "gray0" }, + "background:divider": { "fg": "gray5", "bg": "gray0", "attr": [] }, "session": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, - "date": { "fg": "gray8", "bg": "gray2" }, + "date": { "fg": "gray8", "bg": "gray2", "attr": [] }, "time": { "fg": "gray10", "bg": "gray2", "attr": ["bold"] }, - "time:divider": { "fg": "gray5", "bg": "gray2" }, + "time:divider": { "fg": "gray5", "bg": "gray2", "attr": [] }, "email_alert": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attr": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attr": ["bold"] }, - "weather": { "fg": "gray8", "bg": "gray0" }, - "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0" }, - "weather_condition_hot": { "fg": "khaki1", "bg": "gray0" }, - "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0" }, - "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0" }, - "uptime": { "fg": "gray8", "bg": "gray0" }, - "external_ip": { "fg": "gray8", "bg": "gray0" }, - "network_load": { "fg": "gray8", "bg": "gray0" }, - "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, - "system_load": { "fg": "gray8", "bg": "gray0" }, - "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, - "environment": { "fg": "gray8", "bg": "gray0" }, - "cpu_load_percent": { "fg": "gray8", "bg": "gray0" }, - "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0" }, - "battery": { "fg": "gray8", "bg": "gray0" }, - "battery_gradient": { "fg": "white_red", "bg": "gray0" }, - "battery_full": { "fg": "red", "bg": "gray0" }, - "battery_empty": { "fg": "white", "bg": "gray0" }, - "now_playing": { "fg": "gray10", "bg": "black" }, + "weather": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0", "attr": [] }, + "weather_condition_hot": { "fg": "khaki1", "bg": "gray0", "attr": [] }, + "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0", "attr": [] }, + "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0", "attr": [] }, + "uptime": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "external_ip": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "network_load": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attr": [] }, + "system_load": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attr": [] }, + "environment": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "cpu_load_percent": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attr": [] }, + "battery": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "battery_gradient": { "fg": "white_red", "bg": "gray0", "attr": [] }, + "battery_full": { "fg": "red", "bg": "gray0", "attr": [] }, + "battery_empty": { "fg": "white", "bg": "gray0", "attr": [] }, + "now_playing": { "fg": "gray10", "bg": "black", "attr": [] }, "user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] }, "superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] }, - "branch": { "fg": "gray9", "bg": "gray2" }, - "branch_dirty": { "fg": "brightyellow", "bg": "gray2" }, - "branch_clean": { "fg": "gray9", "bg": "gray2" }, - "branch:divider": { "fg": "gray7", "bg": "gray2" }, - "cwd": { "fg": "gray9", "bg": "gray4" }, + "branch": { "fg": "gray9", "bg": "gray2", "attr": [] }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray2", "attr": [] }, + "branch_clean": { "fg": "gray9", "bg": "gray2", "attr": [] }, + "branch:divider": { "fg": "gray7", "bg": "gray2", "attr": [] }, + "cwd": { "fg": "gray9", "bg": "gray4", "attr": [] }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, - "cwd:divider": { "fg": "gray7", "bg": "gray4" }, - "virtualenv": { "fg": "white", "bg": "darkcyan" } + "cwd:divider": { "fg": "gray7", "bg": "gray4", "attr": [] }, + "virtualenv": { "fg": "white", "bg": "darkcyan", "attr": [] } } } diff --git a/powerline/config_files/colorschemes/ipython/default.json b/powerline/config_files/colorschemes/ipython/default.json index 60585c0d..54987c48 100644 --- a/powerline/config_files/colorschemes/ipython/default.json +++ b/powerline/config_files/colorschemes/ipython/default.json @@ -1,7 +1,7 @@ { "name": "Default color scheme for IPython prompt", "groups": { - "prompt": { "fg": "gray9", "bg": "gray4" }, - "prompt_count": { "fg": "white", "bg": "gray4" } + "prompt": { "fg": "gray9", "bg": "gray4", "attr": [] }, + "prompt_count": { "fg": "white", "bg": "gray4", "attr": [] } } } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 46483940..754addda 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -1,11 +1,11 @@ { "name": "Default color scheme for shell prompts", "groups": { - "hostname": { "fg": "brightyellow", "bg": "mediumorange" }, - "jobnum": { "fg": "brightyellow", "bg": "mediumorange" }, - "exit_fail": { "fg": "white", "bg": "darkestred" }, - "exit_success": { "fg": "white", "bg": "darkestgreen" }, - "environment": { "fg": "white", "bg": "darkestgreen" }, + "hostname": { "fg": "brightyellow", "bg": "mediumorange", "attr": [] }, + "jobnum": { "fg": "brightyellow", "bg": "mediumorange", "attr": [] }, + "exit_fail": { "fg": "white", "bg": "darkestred", "attr": [] }, + "exit_success": { "fg": "white", "bg": "darkestgreen", "attr": [] }, + "environment": { "fg": "white", "bg": "darkestgreen", "attr": [] }, "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } }, "mode_translations": { diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index 2916940a..e7f79569 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,9 +1,9 @@ { "name": "Solarized dark for shell", "groups": { - "jobnum": { "fg": "oldlace", "bg": "darkgreencopper" }, - "exit_fail": { "fg": "oldlace", "bg": "red" }, - "exit_success": { "fg": "oldlace", "bg": "green" }, + "jobnum": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, + "exit_fail": { "fg": "oldlace", "bg": "red", "attr": [] }, + "exit_success": { "fg": "oldlace", "bg": "green", "attr": [] }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } }, "mode_translations": { diff --git a/powerline/config_files/colorschemes/solarized.json b/powerline/config_files/colorschemes/solarized.json index 3e901e50..a5ed3f64 100644 --- a/powerline/config_files/colorschemes/solarized.json +++ b/powerline/config_files/colorschemes/solarized.json @@ -1,17 +1,17 @@ { "name": "Solarized dark", "groups": { - "background": { "fg": "oldlace", "bg": "royalblue5" }, + "background": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, - "virtualenv": { "fg": "oldlace", "bg": "green" }, - "branch": { "fg": "gray61", "bg": "royalblue5" }, - "branch_dirty": { "fg": "yellow", "bg": "royalblue5" }, - "branch_clean": { "fg": "gray61", "bg": "royalblue5" }, - "cwd": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "virtualenv": { "fg": "oldlace", "bg": "green", "attr": [] }, + "branch": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, + "branch_dirty": { "fg": "yellow", "bg": "royalblue5", "attr": [] }, + "branch_clean": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, + "cwd": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper" }, - "hostname": { "fg": "oldlace", "bg": "darkgreencopper" }, - "environment": { "fg": "oldlace", "bg": "green" } + "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, + "hostname": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, + "environment": { "fg": "oldlace", "bg": "green", "attr": [] } } } diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 4d2fb4a5..fae208de 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -1,42 +1,42 @@ { "name": "Default color scheme", "groups": { - "background": { "fg": "white", "bg": "gray2" }, - "background:divider": { "fg": "gray6", "bg": "gray2" }, + "background": { "fg": "white", "bg": "gray2", "attr": [] }, + "background:divider": { "fg": "gray6", "bg": "gray2", "attr": [] }, "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, "visual_range": { "fg": "brightestorange", "bg": "darkorange", "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" }, - "branch_dirty": { "fg": "brightyellow", "bg": "gray4" }, - "branch_clean": { "fg": "gray9", "bg": "gray4" }, - "branch:divider": { "fg": "gray7", "bg": "gray4" }, - "file_directory": { "fg": "gray9", "bg": "gray4" }, + "readonly_indicator": { "fg": "brightestred", "bg": "gray4", "attr": [] }, + "branch": { "fg": "gray9", "bg": "gray4", "attr": [] }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray4", "attr": [] }, + "branch_clean": { "fg": "gray9", "bg": "gray4", "attr": [] }, + "branch:divider": { "fg": "gray7", "bg": "gray4", "attr": [] }, + "file_directory": { "fg": "gray9", "bg": "gray4", "attr": [] }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, - "window_title": { "fg": "white", "bg": "gray4" }, - "file_size": { "fg": "gray8", "bg": "gray2" }, + "window_title": { "fg": "white", "bg": "gray4", "attr": [] }, + "file_size": { "fg": "gray8", "bg": "gray2", "attr": [] }, "file_name_no_file": { "fg": "gray9", "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" }, - "file_vcs_status": { "fg": "brightestred", "bg": "gray4" }, - "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4" }, - "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4" }, - "line_percent": { "fg": "gray9", "bg": "gray4" }, - "line_percent_gradient": { "fg": "dark_green_gray", "bg": "gray4" }, - "position": { "fg": "gray9", "bg": "gray4" }, - "position_gradient": { "fg": "green_yellow_red", "bg": "gray4" }, + "file_name_empty": { "fg": "gray9", "bg": "gray4", "attr": [] }, + "file_format": { "fg": "gray8", "bg": "gray2", "attr": [] }, + "file_encoding": { "fg": "gray8", "bg": "gray2", "attr": [] }, + "file_type": { "fg": "gray8", "bg": "gray2", "attr": [] }, + "file_vcs_status": { "fg": "brightestred", "bg": "gray4", "attr": [] }, + "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4", "attr": [] }, + "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4", "attr": [] }, + "line_percent": { "fg": "gray9", "bg": "gray4", "attr": [] }, + "line_percent_gradient": { "fg": "dark_green_gray", "bg": "gray4", "attr": [] }, + "position": { "fg": "gray9", "bg": "gray4", "attr": [] }, + "position_gradient": { "fg": "green_yellow_red", "bg": "gray4", "attr": [] }, "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, - "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10" }, - "col_current": { "fg": "gray6", "bg": "gray10" }, - "modified_buffers": { "fg": "brightyellow", "bg": "gray2" }, - "environment": { "fg": "gray8", "bg": "gray2" }, + "line_current_symbol": { "fg": "gray1", "bg": "gray10", "attr": [] }, + "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10", "attr": [] }, + "col_current": { "fg": "gray6", "bg": "gray10", "attr": [] }, + "modified_buffers": { "fg": "brightyellow", "bg": "gray2", "attr": [] }, + "environment": { "fg": "gray8", "bg": "gray2", "attr": [] }, "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] }, - "current_tag": { "fg": "gray9", "bg": "gray2" } + "current_tag": { "fg": "gray9", "bg": "gray2", "attr": [] } }, "mode_translations": { "nc": { @@ -76,8 +76,8 @@ }, "groups": { "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }, - "background:divider": { "fg": "darkcyan", "bg": "darkestblue" }, - "branch:divider": { "fg": "darkcyan", "bg": "darkblue" } + "background:divider": { "fg": "darkcyan", "bg": "darkestblue", "attr": [] }, + "branch:divider": { "fg": "darkcyan", "bg": "darkblue", "attr": [] } } }, "v": { diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 0ea4fd39..95b0b4b3 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,38 +1,38 @@ { "name": "Solarized dark for vim", "groups": { - "background": { "fg": "oldlace", "bg": "royalblue5" }, - "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5" }, + "background": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, + "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5", "attr": [] }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, "visual_range": { "fg": "green", "bg": "oldlace", "attr": ["bold"] }, "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "darkgreencopper" }, - "branch": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper" }, - "branch_clean": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "branch:divider": { "fg": "gray61", "bg": "darkgreencopper" }, - "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "readonly_indicator": { "fg": "red", "bg": "darkgreencopper", "attr": [] }, + "branch": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, + "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper", "attr": [] }, + "branch_clean": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, + "branch:divider": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, + "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "window_title": { "fg": "oldlace", "bg": "darkgreencopper" }, - "file_size": { "fg": "oldlace", "bg": "darkgreencopper" }, + "window_title": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, + "file_size": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper" }, - "file_format": { "fg": "gray61", "bg": "royalblue5" }, - "file_encoding": { "fg": "gray61", "bg": "royalblue5" }, - "file_type": { "fg": "gray61", "bg": "royalblue5" }, - "file_vcs_status": { "fg": "red", "bg": "darkgreencopper" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper" }, - "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper" }, - "line_percent": { "fg": "oldlace", "bg": "lightskyblue4" }, - "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, - "position": { "fg": "oldlace", "bg": "lightskyblue4" }, - "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4" }, + "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, + "file_format": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, + "file_encoding": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, + "file_type": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, + "file_vcs_status": { "fg": "red", "bg": "darkgreencopper", "attr": [] }, + "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper", "attr": [] }, + "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper", "attr": [] }, + "line_percent": { "fg": "oldlace", "bg": "lightskyblue4", "attr": [] }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4", "attr": [] }, + "position": { "fg": "oldlace", "bg": "lightskyblue4", "attr": [] }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4", "attr": [] }, "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray13", "bg": "lightyellow" }, - "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "lightyellow" }, - "col_current": { "fg": "azure4", "bg": "lightyellow" }, - "environment": { "fg": "gray61", "bg": "royalblue5" }, + "line_current_symbol": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, + "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "lightyellow", "attr": [] }, + "col_current": { "fg": "azure4", "bg": "lightyellow", "attr": [] }, + "environment": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, "current_tag": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] } @@ -50,32 +50,32 @@ }, "i": { "groups": { - "background": { "fg": "oldlace", "bg": "darkgreencopper" }, - "background:divider": { "fg": "lightyellow", "bg": "darkgreencopper" }, + "background": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, + "background:divider": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "lightyellow" }, - "branch": { "fg": "darkgreencopper", "bg": "lightyellow" }, - "branch:divider": { "fg": "lightskyblue4", "bg": "lightyellow" }, - "file_directory": { "fg": "darkgreencopper", "bg": "lightyellow" }, + "readonly_indicator": { "fg": "red", "bg": "lightyellow", "attr": [] }, + "branch": { "fg": "darkgreencopper", "bg": "lightyellow", "attr": [] }, + "branch:divider": { "fg": "lightskyblue4", "bg": "lightyellow", "attr": [] }, + "file_directory": { "fg": "darkgreencopper", "bg": "lightyellow", "attr": [] }, "file_name": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, - "file_size": { "fg": "royalblue5", "bg": "lightyellow" }, + "file_size": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "file_name_no_file": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, - "file_name_empty": { "fg": "royalblue5", "bg": "lightyellow" }, - "file_format": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "file_encoding": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "file_type": { "fg": "lightyellow", "bg": "darkgreencopper" }, - "file_vcs_status": { "fg": "red", "bg": "lightyellow" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow" }, - "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, - "line_percent": { "fg": "oldlace", "bg": "gray61" }, - "line_percent_gradient": { "fg": "oldlace", "bg": "gray61" }, - "position": { "fg": "oldlace", "bg": "gray61" }, - "position_gradient": { "fg": "oldlace", "bg": "gray61" }, + "file_name_empty": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "file_format": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, + "file_encoding": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, + "file_type": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, + "file_vcs_status": { "fg": "red", "bg": "lightyellow", "attr": [] }, + "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, + "file_vcs_status_A": { "fg": "green", "bg": "lightyellow", "attr": [] }, + "line_percent": { "fg": "oldlace", "bg": "gray61", "attr": [] }, + "line_percent_gradient": { "fg": "oldlace", "bg": "gray61", "attr": [] }, + "position": { "fg": "oldlace", "bg": "gray61", "attr": [] }, + "position_gradient": { "fg": "oldlace", "bg": "gray61", "attr": [] }, "line_current": { "fg": "gray13", "bg": "oldlace", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray13", "bg": "oldlace" }, - "col_current": { "fg": "azure4", "bg": "oldlace" } + "line_current_symbol": { "fg": "gray13", "bg": "oldlace", "attr": [] }, + "col_current": { "fg": "azure4", "bg": "oldlace", "attr": [] } } }, "v": { diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index 2622cb1a..69940e0d 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -1,38 +1,38 @@ { "name": "Solarized light for vim", "groups": { - "background": { "fg": "gray13", "bg": "darkgreencopper" }, - "background:divider": { "fg": "azure4", "bg": "darkgreencopper" }, + "background": { "fg": "gray13", "bg": "darkgreencopper", "attr": [] }, + "background:divider": { "fg": "azure4", "bg": "darkgreencopper", "attr": [] }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, "visual_range": { "fg": "green", "bg": "oldlace", "attr": ["bold"] }, "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, "paste_indicator": { "fg": "red", "bg": "lightyellow", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "lightyellow" }, - "branch": { "fg": "royalblue5", "bg": "lightyellow" }, - "branch_dirty": { "fg": "yellow", "bg": "lightyellow" }, - "branch_clean": { "fg": "royalblue5", "bg": "lightyellow" }, - "branch:divider": { "fg": "gray61", "bg": "lightyellow" }, - "file_directory": { "fg": "royalblue5", "bg": "lightyellow" }, + "readonly_indicator": { "fg": "red", "bg": "lightyellow", "attr": [] }, + "branch": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "branch_dirty": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, + "branch_clean": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "branch:divider": { "fg": "gray61", "bg": "lightyellow", "attr": [] }, + "file_directory": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "file_name": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "window_title": { "fg": "gray13", "bg": "lightyellow" }, - "file_size": { "fg": "gray13", "bg": "lightyellow" }, + "window_title": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, + "file_size": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, "file_name_no_file": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "file_name_empty": { "fg": "gray13", "bg": "lightyellow" }, - "file_format": { "fg": "gray61", "bg": "darkgreencopper" }, - "file_encoding": { "fg": "gray61", "bg": "darkgreencopper" }, - "file_type": { "fg": "gray61", "bg": "darkgreencopper" }, - "file_vcs_status": { "fg": "red", "bg": "lightyellow" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow" }, - "file_vcs_status_A": { "fg": "green", "bg": "lightyellow" }, - "line_percent": { "fg": "gray13", "bg": "lightyellow" }, - "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow" }, - "position": { "fg": "gray13", "bg": "lightyellow" }, - "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow" }, + "file_name_empty": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, + "file_format": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, + "file_encoding": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, + "file_type": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, + "file_vcs_status": { "fg": "red", "bg": "lightyellow", "attr": [] }, + "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, + "file_vcs_status_A": { "fg": "green", "bg": "lightyellow", "attr": [] }, + "line_percent": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow", "attr": [] }, + "position": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow", "attr": [] }, "line_current": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] }, - "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5" }, - "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5" }, - "col_current": { "fg": "lightskyblue4", "bg": "royalblue5" }, - "environment": { "fg": "gray61", "bg": "darkgreencopper" }, + "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5", "attr": [] }, + "col_current": { "fg": "lightskyblue4", "bg": "royalblue5", "attr": [] }, + "environment": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, "error": { "fg": "gray13", "bg": "red", "attr": ["bold"] }, "warning": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "current_tag": { "fg": "gray13", "bg": "darkgreencopper", "attr": ["bold"] } @@ -50,33 +50,33 @@ }, "i": { "groups": { - "background": { "fg": "gray13", "bg": "lightyellow" }, - "background:divider": { "fg": "royalblue5", "bg": "lightyellow" }, + "background": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, + "background:divider": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, "modified_indicator": { "fg": "yellow", "bg": "royalblue5", "attr": ["bold"] }, "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "royalblue5" }, - "branch": { "fg": "lightyellow", "bg": "royalblue5" }, - "branch:divider": { "fg": "azure4", "bg": "royalblue5" }, - "file_directory": { "fg": "lightyellow", "bg": "royalblue5" }, + "readonly_indicator": { "fg": "red", "bg": "royalblue5", "attr": [] }, + "branch": { "fg": "lightyellow", "bg": "royalblue5", "attr": [] }, + "branch:divider": { "fg": "azure4", "bg": "royalblue5", "attr": [] }, + "file_directory": { "fg": "lightyellow", "bg": "royalblue5", "attr": [] }, "file_name": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, - "file_size": { "fg": "darkgreencopper", "bg": "royalblue5" }, + "file_size": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": [] }, "file_name_no_file": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, - "file_name_empty": { "fg": "darkgreencopper", "bg": "royalblue5" }, - "file_format": { "fg": "royalblue5", "bg": "lightyellow" }, - "file_encoding": { "fg": "royalblue5", "bg": "lightyellow" }, - "file_type": { "fg": "royalblue5", "bg": "lightyellow" }, - "file_vcs_status": { "fg": "red", "bg": "royalblue5" }, - "file_vcs_status_M": { "fg": "yellow", "bg": "royalblue5" }, - "file_vcs_status_A": { "fg": "green", "bg": "royalblue5" }, - "line_percent": { "fg": "gray13", "bg": "gray61" }, - "line_percent_gradient": { "fg": "gray13", "bg": "gray61" }, - "position": { "fg": "gray13", "bg": "gray61" }, - "position_gradient": { "fg": "gray13", "bg": "gray61" }, + "file_name_empty": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": [] }, + "file_format": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "file_encoding": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "file_type": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "file_vcs_status": { "fg": "red", "bg": "royalblue5", "attr": [] }, + "file_vcs_status_M": { "fg": "yellow", "bg": "royalblue5", "attr": [] }, + "file_vcs_status_A": { "fg": "green", "bg": "royalblue5", "attr": [] }, + "line_percent": { "fg": "gray13", "bg": "gray61", "attr": [] }, + "line_percent_gradient": { "fg": "gray13", "bg": "gray61", "attr": [] }, + "position": { "fg": "gray13", "bg": "gray61", "attr": [] }, + "position_gradient": { "fg": "gray13", "bg": "gray61", "attr": [] }, "line_current": { "fg": "oldlace", "bg": "gray13", "attr": ["bold"] }, - "line_current_symbol": { "fg": "oldlace", "bg": "gray13" }, - "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "gray13" }, - "col_current": { "fg": "lightskyblue4", "bg": "gray13" } + "line_current_symbol": { "fg": "oldlace", "bg": "gray13", "attr": [] }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "gray13", "attr": [] }, + "col_current": { "fg": "lightskyblue4", "bg": "gray13", "attr": [] } } }, "v": { From ee71eac7b2594e8bbba5b6a95a98ad889e4d0646 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 12:02:14 +0400 Subject: [PATCH 1063/1472] Check for dictionaries using isinstance(), not is --- powerline/lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index b68b4830..b9839afc 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -18,7 +18,7 @@ def mergedicts(d1, d2): '''Recursively merge two dictionaries. First dictionary is modified in-place. ''' for k in d2: - if k in d1 and type(d1[k]) is dict and type(d2[k]) is dict: + if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): mergedicts(d1[k], d2[k]) elif d2[k] is REMOVE_THIS_KEY: d1.pop(k, None) From 4d9157d7f1816a72b0f38f81e512e651d090f363 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 12:01:32 +0400 Subject: [PATCH 1064/1472] Add MarkedDict class, add special code for .copy() in Marked --- powerline/lint/markedjson/markedvalue.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 6a304b9f..397be680 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -47,6 +47,16 @@ class MarkedFloat(float): __new__ = gen_new(float) +class MarkedDict(dict): + __new__ = gen_new(dict) + + def __init__(self, value, mark): + super(MarkedDict, self).__init__(value) + + def copy(self): + return MarkedDict(super(MarkedDict, self).copy(), self.mark) + + class MarkedValue: def __init__(self, value, mark): self.mark = mark @@ -57,6 +67,7 @@ specialclasses = { unicode: MarkedUnicode, int: MarkedInt, float: MarkedFloat, + dict: MarkedDict, } classcache = {} @@ -70,7 +81,10 @@ def gen_marked_value(value, mark, use_special_classes=True): else: class Marked(MarkedValue): for func in value.__class__.__dict__: - if func not in set(('__init__', '__new__', '__getattribute__')): + if func == 'copy': + def copy(self): + return self.__class__(self.value.copy(), self.mark) + elif func not in set(('__init__', '__new__', '__getattribute__')): if func in set(('__eq__',)): # HACK to make marked dictionaries always work exec (('def {0}(self, *args):\n' From 8ded14c3d8d8e1cc0468086b88d16d2b6caf4804 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 12:03:08 +0400 Subject: [PATCH 1065/1472] Add mergedicts_copy: non-modifying mergedicts --- powerline/lib/__init__.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index b9839afc..252b1e2e 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -26,6 +26,21 @@ def mergedicts(d1, d2): d1[k] = d2[k] +def mergedicts_copy(d1, d2): + '''Recursively merge two dictionaries. + + Dictionaries are not modified. Copying happens only if necessary. Assumes + that first dictionary support .copy() method. + ''' + ret = d1.copy() + for k in d2: + if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): + ret[k] = mergedicts_copy(d1[k], d2[k]) + else: + ret[k] = d2[k] + return ret + + def add_divider_highlight_group(highlight_group): def dec(func): @wraps_saveargs(func) From 2d27f821227368747406080e85a282c860aa6d1a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 12:03:26 +0400 Subject: [PATCH 1066/1472] Make lint checker work --- powerline/lint/__init__.py | 286 ++++++++++++++++++++++++++++--------- 1 file changed, 215 insertions(+), 71 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 611f0606..074e00e8 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -7,6 +7,7 @@ from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes from powerline.lint.inspect import getconfigargspec from powerline.lib.threaded import ThreadedSegment +from powerline.lib import mergedicts_copy import itertools import sys import os @@ -414,14 +415,20 @@ def check_matcher_func(ext, match_name, data, context, echoerr): def check_ext(ext, data, context, echoerr): hadsomedirs = False hadproblem = False - for subdir in ('themes', 'colorschemes'): - if ext not in data['configs'][subdir]: - hadproblem = True - echoerr(context='Error while loading {0} extension configuration'.format(ext), - context_mark=ext.mark, - problem='{0} configuration does not exist'.format(subdir)) - else: - hadsomedirs = True + if ext not in data['lists']['exts']: + hadproblem = True + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='extension configuration does not exist') + else: + for typ in ('themes', 'colorschemes'): + if ext not in data['configs'][typ] and not data['configs']['top_' + typ]: + hadproblem = True + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='{0} configuration does not exist'.format(typ)) + else: + hadsomedirs = True return hadsomedirs, hadproblem @@ -431,7 +438,13 @@ def check_config(d, theme, data, context, echoerr): else: # local_themes ext = context[-3][0] - if ext not in data['configs'][d] or theme not in data['configs'][d][ext]: + if ext not in data['lists']['exts']: + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='extension configuration does not exist') + return True, False, True + if ((ext not in data['configs'][d] or theme not in data['configs'][d][ext]) + and theme not in data['configs']['top_' + d]): echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext), problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme), problem_mark=theme.mark) @@ -531,22 +544,84 @@ def check_color(color, data, context, echoerr): def check_translated_group_name(group, data, context, echoerr): - if group not in context[0][1].get('groups', {}): - echoerr(context='Error while checking translated group in colorscheme (key {key})'.format(key=context_key(context)), - problem='translated group {0} is not in main groups dictionary'.format(group), - problem_mark=group.mark) - return True, False, True - return True, False, False + return check_group(group, data, context, echoerr) + + +def check_group(group, data, context, echoerr): + if not isinstance(group, unicode): + return True, False, False + colorscheme = data['colorscheme'] + ext = data['ext'] + configs = [] + if ext: + if colorscheme == '__main__': + configs.append([config for config in data['ext_colorscheme_configs'][ext].items()]) + configs.append([config for config in data['top_colorscheme_configs'].items()]) + else: + try: + configs.append([data['ext_colorscheme_configs'][ext][colorscheme]]) + except KeyError: + pass + try: + configs.append([data['ext_colorscheme_configs'][ext]['__main__']]) + except KeyError: + pass + try: + configs.append([data['top_colorscheme_configs'][colorscheme]]) + except KeyError: + pass + else: + try: + configs.append([data['top_colorscheme_configs'][colorscheme]]) + except KeyError: + pass + new_echoerr = DelayedEchoErr(echoerr) + hadproblem = False + for config_lst in configs: + tofind = len(config_lst) + not_found = [] + for config in config_lst: + if isinstance(config, tuple): + new_colorscheme, config = config + new_data = data.copy() + new_data['colorscheme'] = new_colorscheme + else: + new_data = data + try: + group_data = config['groups'][group] + except KeyError: + not_found.append(config.mark.name) + else: + proceed, echo, chadproblem = check_group( + group_data, + new_data, + context, + echoerr, + ) + if chadproblem: + hadproblem = True + else: + tofind -= 1 + if not tofind: + return proceed, echo, hadproblem + if not proceed: + break + if not_found: + new_echoerr(context='Error while checking group definition in colorscheme (key {key})'.format(key=context_key(context)), + problem='name {0} is not present in {1} {2} colorschemes: {3}'.format(group, tofind, ext, ', '.join(not_found)), + problem_mark=group.mark) + new_echoerr.echo_all() + return True, False, hadproblem color_spec = Spec().type(unicode).func(check_color).copy name_spec = Spec().type(unicode).len('gt', 0).optional().copy -group_spec = Spec( +group_name_spec = Spec().re('^\w+(?::\w+)?$').copy +group_spec = Spec().either(Spec( fg=color_spec(), bg=color_spec(), - attr=Spec().list(Spec().type(unicode).oneof(set(('bold', 'italic', 'underline')))).optional(), -).copy -group_name_spec = Spec().re('^\w+(?::\w+)?$').copy + attr=Spec().list(Spec().type(unicode).oneof(set(('bold', 'italic', 'underline')))), +), group_name_spec().func(check_group)).copy groups_spec = Spec().unknown_spec( group_name_spec(), group_spec(), @@ -555,23 +630,32 @@ colorscheme_spec = (Spec( name=name_spec(), groups=groups_spec(), ).context_message('Error while loading coloscheme')) +mode_translations_value_spec = Spec( + colors=Spec().unknown_spec( + color_spec(), + color_spec(), + ).optional(), + groups=Spec().unknown_spec( + group_name_spec().func(check_translated_group_name), + group_spec(), + ).optional(), +).copy +top_colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec(), + mode_translations=Spec().unknown_spec( + Spec().type(unicode), + mode_translations_value_spec(), + ).optional().context_message('Error while loading mode translations (key {key})').optional(), +).context_message('Error while loading top-level coloscheme')) vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc'])).copy vim_colorscheme_spec = (Spec( name=name_spec(), groups=groups_spec(), mode_translations=Spec().unknown_spec( vim_mode_spec(), - Spec( - colors=Spec().unknown_spec( - color_spec(), - color_spec(), - ).optional(), - groups=Spec().unknown_spec( - group_name_spec().func(check_translated_group_name), - group_spec(), - ).optional(), - ), - ).context_message('Error while loading mode translations (key {key})'), + mode_translations_value_spec(), + ).optional().context_message('Error while loading mode translations (key {key})'), ).context_message('Error while loading vim colorscheme')) shell_mode_spec = Spec().re('^(?:[\w\-]+|\.safe)$').copy shell_colorscheme_spec = (Spec( @@ -579,17 +663,8 @@ shell_colorscheme_spec = (Spec( groups=groups_spec(), mode_translations=Spec().unknown_spec( shell_mode_spec(), - Spec( - colors=Spec().unknown_spec( - color_spec(), - color_spec(), - ).optional(), - groups=Spec().unknown_spec( - group_name_spec().func(check_translated_group_name), - group_spec(), - ).optional(), - ), - ).context_message('Error while loading mode translations (key {key})'), + mode_translations_value_spec(), + ).optional().context_message('Error while loading mode translations (key {key})'), ).context_message('Error while loading shell colorscheme')) @@ -1044,44 +1119,60 @@ def check(path=None, debug=False): ee = EchoErr(echoerr, logger) - dirs = { + paths = { 'themes': defaultdict(lambda: []), - 'colorschemes': defaultdict(lambda: []) + 'colorschemes': defaultdict(lambda: []), + 'top_colorschemes': [], + 'top_themes': [], + } + lists = { + 'colorschemes': set(), + 'themes': set(), + 'exts': set(), } for path in reversed(search_paths): - for subdir in ('themes', 'colorschemes'): - d = os.path.join(path, subdir) + for typ in ('themes', 'colorschemes'): + d = os.path.join(path, typ) if os.path.isdir(d): - for ext in os.listdir(d): - extd = os.path.join(d, ext) - if os.path.isdir(extd): - dirs[subdir][ext].append(extd) - elif os.path.exists(d): + for subp in os.listdir(d): + extpath = os.path.join(d, subp) + if os.path.isdir(extpath): + lists['exts'].add(subp) + paths[typ][subp].append(extpath) + elif extpath.endswith('.json'): + name = subp[:-5] + if name != '__main__': + lists[typ].add(name) + paths['top_' + typ].append(extpath) + else: hadproblem = True sys.stderr.write('Path {0} is supposed to be a directory, but it is not\n'.format(d)) - configs = { - 'themes': defaultdict(lambda: {}), - 'colorschemes': defaultdict(lambda: {}) - } - for subdir in ('themes', 'colorschemes'): - for ext in dirs[subdir]: - for d in dirs[subdir][ext]: - for config in os.listdir(d): - if os.path.isdir(os.path.join(d, config)): - dirs[subdir][ext].append(os.path.join(d, config)) - elif config.endswith('.json'): - configs[subdir][ext][config[:-5]] = os.path.join(d, config) + configs = defaultdict(lambda: defaultdict(lambda: {})) + for typ in ('themes', 'colorschemes'): + for ext in paths[typ]: + for d in paths[typ][ext]: + for subp in os.listdir(d): + if subp.endswith('.json'): + name = subp[:-5] + if name != '__main__': + lists[typ].add(name) + configs[typ][ext][name] = os.path.join(d, subp) + for path in paths['top_' + typ]: + name = os.path.basename(path)[:-5] + configs['top_' + typ][name] = path diff = set(configs['themes']) ^ set(configs['colorschemes']) if diff: hadproblem = True for ext in diff: - sys.stderr.write('{0} extension {1} present only in {2}\n'.format( - ext, - 'configuration' if (ext in dirs['themes'] and ext in dirs['colorschemes']) else 'directory', - 'themes' if ext in configs['themes'] else 'colorschemes', - )) + typ = 'colorschemes' if ext in configs['themes'] else 'themes' + if not configs['top_' + typ] or typ == 'themes': + sys.stderr.write('{0} extension {1} not present in {2}\n'.format( + ext, + 'configuration' if (ext in paths['themes'] and ext in paths['colorschemes']) else 'directory', + typ, + )) lhadproblem = [False] @@ -1103,7 +1194,7 @@ def check(path=None, debug=False): sys.stderr.write(str(e) + '\n') hadproblem = True else: - if main_spec.match(main_config, data={'configs': configs}, context=(('', main_config),), echoerr=ee)[1]: + if main_spec.match(main_config, data={'configs': configs, 'lists': lists}, context=(('', main_config),), echoerr=ee)[1]: hadproblem = True import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] @@ -1125,9 +1216,30 @@ def check(path=None, debug=False): if lhadproblem[0]: hadproblem = True - colorscheme_configs = defaultdict(lambda: {}) + top_colorscheme_configs = {} + data = { + 'ext': None, + 'top_colorscheme_configs': top_colorscheme_configs, + 'ext_colorscheme_configs': {}, + 'colors_config': colors_config + } + for colorscheme, cfile in configs['top_colorschemes'].items(): + with open_file(cfile) as config_file_fp: + try: + config, lhadproblem = load(config_file_fp) + except MarkedError as e: + sys.stderr.write(str(e) + '\n') + hadproblem = True + continue + if lhadproblem: + hadproblem = True + top_colorscheme_configs[colorscheme] = config + data['colorscheme'] = colorscheme + if top_colorscheme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + hadproblem = True + + ext_colorscheme_configs = defaultdict(lambda: {}) for ext in configs['colorschemes']: - data = {'ext': ext, 'colors_config': colors_config} for colorscheme, cfile in configs['colorschemes'][ext].items(): with open_file(cfile) as config_file_fp: try: @@ -1138,7 +1250,17 @@ def check(path=None, debug=False): continue if lhadproblem: hadproblem = True - colorscheme_configs[ext][colorscheme] = config + ext_colorscheme_configs[ext][colorscheme] = config + + for ext, econfigs in ext_colorscheme_configs.items(): + data = { + 'ext': ext, + 'top_colorscheme_configs': top_colorscheme_configs, + 'ext_colorscheme_configs': ext_colorscheme_configs, + 'colors_config': colors_config, + } + for colorscheme, config in econfigs.items(): + data['colorscheme'] = colorscheme if ext == 'vim': spec = vim_colorscheme_spec elif ext == 'shell': @@ -1148,6 +1270,27 @@ def check(path=None, debug=False): if spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: hadproblem = True + colorscheme_configs = {} + for ext in lists['exts']: + colorscheme_configs[ext] = {} + for colorscheme in lists['colorschemes']: + econfigs = ext_colorscheme_configs[ext] + ecconfigs = econfigs.get(colorscheme) + mconfigs = ( + top_colorscheme_configs.get(colorscheme), + econfigs.get('__main__'), + ecconfigs, + ) + config = None + for mconfig in mconfigs: + if not mconfig: + continue + if config: + config = mergedicts_copy(config, mconfig) + else: + config = mconfig + colorscheme_configs[colorscheme] = config + theme_configs = defaultdict(lambda: {}) for ext in configs['themes']: for theme, sfile in configs['themes'][ext].items(): @@ -1161,6 +1304,7 @@ def check(path=None, debug=False): if lhadproblem: hadproblem = True theme_configs[ext][theme] = config + for ext, configs in theme_configs.items(): data = {'ext': ext, 'colorscheme_configs': colorscheme_configs, 'import_paths': import_paths, 'main_config': main_config, 'ext_theme_configs': configs, 'colors_config': colors_config} From ba63965a25f92b20e5ebe2052853788d48eacaeb Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 20 Feb 2014 21:33:49 +0400 Subject: [PATCH 1067/1472] Refactor some vim colorschemes to use group aliases --- .../config_files/colorschemes/vim/__main__.json | 15 +++++++++++++++ .../config_files/colorschemes/vim/default.json | 11 ++--------- .../config_files/colorschemes/vim/solarized.json | 11 ++--------- .../colorschemes/vim/solarizedlight.json | 11 ++--------- 4 files changed, 21 insertions(+), 27 deletions(-) create mode 100644 powerline/config_files/colorschemes/vim/__main__.json diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json new file mode 100644 index 00000000..6bfc59dc --- /dev/null +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -0,0 +1,15 @@ +{ + "groups": { + "branch_clean": "branch", + "environment": "information:unimportant", + "file_size": "information:unimportant", + "file_format": "information:unimportant", + "file_encoding": "file_format", + "file_type": "file_format", + "branch": "information:additional", + "file_directory": "information:additional", + "file_name_empty": "file_directory", + "line_percent": "information:additional", + "position": "information:additional" + } +} diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index fae208de..b4f1b483 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -1,6 +1,8 @@ { "name": "Default color scheme", "groups": { + "information:unimportant": { "fg": "gray8", "bg": "gray2", "attr": [] }, + "information:additional": { "fg": "gray9", "bg": "gray4", "attr": [] }, "background": { "fg": "white", "bg": "gray2", "attr": [] }, "background:divider": { "fg": "gray6", "bg": "gray2", "attr": [] }, "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, @@ -8,19 +10,11 @@ "modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attr": ["bold"] }, "paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] }, "readonly_indicator": { "fg": "brightestred", "bg": "gray4", "attr": [] }, - "branch": { "fg": "gray9", "bg": "gray4", "attr": [] }, "branch_dirty": { "fg": "brightyellow", "bg": "gray4", "attr": [] }, - "branch_clean": { "fg": "gray9", "bg": "gray4", "attr": [] }, "branch:divider": { "fg": "gray7", "bg": "gray4", "attr": [] }, - "file_directory": { "fg": "gray9", "bg": "gray4", "attr": [] }, "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, "window_title": { "fg": "white", "bg": "gray4", "attr": [] }, - "file_size": { "fg": "gray8", "bg": "gray2", "attr": [] }, "file_name_no_file": { "fg": "gray9", "bg": "gray4", "attr": ["bold"] }, - "file_name_empty": { "fg": "gray9", "bg": "gray4", "attr": [] }, - "file_format": { "fg": "gray8", "bg": "gray2", "attr": [] }, - "file_encoding": { "fg": "gray8", "bg": "gray2", "attr": [] }, - "file_type": { "fg": "gray8", "bg": "gray2", "attr": [] }, "file_vcs_status": { "fg": "brightestred", "bg": "gray4", "attr": [] }, "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4", "attr": [] }, "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4", "attr": [] }, @@ -33,7 +27,6 @@ "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10", "attr": [] }, "col_current": { "fg": "gray6", "bg": "gray10", "attr": [] }, "modified_buffers": { "fg": "brightyellow", "bg": "gray2", "attr": [] }, - "environment": { "fg": "gray8", "bg": "gray2", "attr": [] }, "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] }, "current_tag": { "fg": "gray9", "bg": "gray2", "attr": [] } diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 95b0b4b3..a3703d74 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,6 +1,8 @@ { "name": "Solarized dark for vim", "groups": { + "information:additional": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, + "information:unimportant": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, "background": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5", "attr": [] }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, @@ -8,19 +10,12 @@ "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, "readonly_indicator": { "fg": "red", "bg": "darkgreencopper", "attr": [] }, - "branch": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper", "attr": [] }, - "branch_clean": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "branch:divider": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, - "file_directory": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, "window_title": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "file_size": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "file_name_empty": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, "file_format": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "file_encoding": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "file_type": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, "file_vcs_status": { "fg": "red", "bg": "darkgreencopper", "attr": [] }, "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper", "attr": [] }, "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper", "attr": [] }, @@ -64,8 +59,6 @@ "file_name_no_file": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, "file_name_empty": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "file_format": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, - "file_encoding": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, - "file_type": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, "file_vcs_status": { "fg": "red", "bg": "lightyellow", "attr": [] }, "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, "file_vcs_status_A": { "fg": "green", "bg": "lightyellow", "attr": [] }, diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index 69940e0d..cd86a7a8 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -1,6 +1,8 @@ { "name": "Solarized light for vim", "groups": { + "information:additional": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, + "information:unimportant": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, "background": { "fg": "gray13", "bg": "darkgreencopper", "attr": [] }, "background:divider": { "fg": "azure4", "bg": "darkgreencopper", "attr": [] }, "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, @@ -8,19 +10,13 @@ "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, "paste_indicator": { "fg": "red", "bg": "lightyellow", "attr": ["bold"] }, "readonly_indicator": { "fg": "red", "bg": "lightyellow", "attr": [] }, - "branch": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "branch_dirty": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, - "branch_clean": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "branch:divider": { "fg": "gray61", "bg": "lightyellow", "attr": [] }, - "file_directory": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "file_name": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "window_title": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, "file_size": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, "file_name_no_file": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "file_name_empty": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "file_format": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, - "file_encoding": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, - "file_type": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, "file_vcs_status": { "fg": "red", "bg": "lightyellow", "attr": [] }, "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, "file_vcs_status_A": { "fg": "green", "bg": "lightyellow", "attr": [] }, @@ -32,7 +28,6 @@ "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5", "attr": [] }, "col_current": { "fg": "lightskyblue4", "bg": "royalblue5", "attr": [] }, - "environment": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, "error": { "fg": "gray13", "bg": "red", "attr": ["bold"] }, "warning": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, "current_tag": { "fg": "gray13", "bg": "darkgreencopper", "attr": ["bold"] } @@ -64,8 +59,6 @@ "file_name_no_file": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, "file_name_empty": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": [] }, "file_format": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "file_encoding": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "file_type": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, "file_vcs_status": { "fg": "red", "bg": "royalblue5", "attr": [] }, "file_vcs_status_M": { "fg": "yellow", "bg": "royalblue5", "attr": [] }, "file_vcs_status_A": { "fg": "green", "bg": "royalblue5", "attr": [] }, From 621eb8412d21976fde78cbbd259e4edfd2d20a5b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Mar 2014 19:54:29 +0400 Subject: [PATCH 1068/1472] Fix tests --- tests/test_config_reload.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 7f3c184f..cc6f331d 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -187,9 +187,14 @@ class TestConfigReload(TestCase): # fcf:colorschemes/test/nonexistentraise”). # sleep(0.1) self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config') + # For colorschemes/{test/,}*raise find_config_file raises + # IOError, but it does not do so for colorschemes/test/__main__, + # so powerline is trying to load this, but not other + # colorschemes/* + self.assertAccessEvents('config', 'colorschemes/test/__main__') self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) + config['colorschemes/nonexistentraise'] = {} config['colorschemes/test/nonexistentraise'] = { 'groups': { "str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]}, @@ -199,7 +204,8 @@ class TestConfigReload(TestCase): while not p._will_create_renderer(): sleep(0.1) self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') - self.assertAccessEvents('colorschemes/test/nonexistentraise') + # Same as above + self.assertAccessEvents('colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'colorschemes/test/__main__') self.assertEqual(p.logger._pop_msgs(), []) pop_events() From 9af7834463124253cc8aac2ae74b545799ffd2f4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 13:46:28 +0400 Subject: [PATCH 1069/1472] Disable shell tests in PyPy Do not let PyPy SEGVs result in failed build. --- tests/test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test.sh b/tests/test.sh index 8d03f548..5ea9d0ea 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -22,6 +22,8 @@ for script in tests/*.vim ; do done if ! sh tests/test_shells/test.sh ; then echo "Failed shells" - FAILED=1 + if ${PYTHON} -c 'import platform, sys; sys.exit(1 * (platform.python_implementation() == "PyPy"))' ; then + FAILED=1 + fi fi exit $FAILED From 576971c5f1b3e37bcd7fabdc5e3adca1711b3bba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 13:48:04 +0400 Subject: [PATCH 1070/1472] Fix test class name in test_configuration --- tests/test_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 343f90ba..9db784ff 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -111,7 +111,7 @@ def add_p_arg(func): return f -class TestSingleLine(TestCase): +class TestLines(TestCase): def assertRenderEqual(self, p, output, **kwargs): self.assertEqual(p.render(**kwargs).replace(' ', ' '), output) From 3ebf9c1a819cce427e268ac54cc4a1f4b04e6f5a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 14:08:05 +0400 Subject: [PATCH 1071/1472] Use highlighted_string function to define segments --- tests/test_configuration.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 9db784ff..641acfc3 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -12,6 +12,16 @@ import sys import os +def highlighted_string(s, group, **kwargs): + ret = { + 'type': 'string', + 'contents': s, + 'highlight_group': [group], + } + ret.update(kwargs) + return ret + + config = { 'config': { 'common': { @@ -64,26 +74,11 @@ config = { 'themes/test/default': { 'segments': { 'left': [ - { - 'type': 'string', - 'contents': 's', - 'width': 'auto', - 'highlight_group': ['str1'], - }, - { - 'type': 'string', - 'contents': 'g', - 'highlight_group': ['str2'], - }, + highlighted_string('s', 'str1', width='auto'), + highlighted_string('g', 'str2'), ], 'right': [ - { - 'type': 'string', - 'contents': 'f', - 'width': 'auto', - 'align': 'right', - 'highlight_group': ['str2'], - }, + highlighted_string('f', 'str2', width='auto', align='right'), ], }, }, From f5735148e803235f60b39bf6f4b29bd3921ee7fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 14:22:23 +0400 Subject: [PATCH 1072/1472] Add group aliasing and colorscheme hierarchy tests --- tests/test_configuration.py | 138 +++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 641acfc3..d62ce173 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -56,14 +56,39 @@ config = { 'col2': 2, 'col3': 3, 'col4': 4, + 'col5': 5, + 'col6': 6, + 'col7': 7, + 'col8': 8, + 'col9': 9, + 'col10': 10, + 'col11': 11, + 'col12': 12, }, 'gradients': { }, }, + 'colorschemes/test/__main__': { + 'groups': { + 'm1': 'g1', + 'm2': 'g3', + 'm3': {'fg': 'col11', 'bg': 'col12', 'attr': []}, + } + }, + 'colorschemes/default': { + 'groups': { + 'g1': {'fg': 'col5', 'bg': 'col6', 'attr': []}, + 'g2': {'fg': 'col7', 'bg': 'col8', 'attr': []}, + 'g3': {'fg': 'col9', 'bg': 'col10', 'attr': []}, + } + }, 'colorschemes/test/default': { 'groups': { 'str1': {'fg': 'col1', 'bg': 'col2', 'attr': ['bold']}, 'str2': {'fg': 'col3', 'bg': 'col4', 'attr': ['underline']}, + 'd1': 'g2', + 'd2': 'm2', + 'd3': 'm3', }, }, 'colorschemes/vim/default': { @@ -106,13 +131,15 @@ def add_p_arg(func): return f -class TestLines(TestCase): +class TestRender(TestCase): def assertRenderEqual(self, p, output, **kwargs): self.assertEqual(p.render(**kwargs).replace(' ', ' '), output) def assertRenderLinesEqual(self, p, output, **kwargs): self.assertEqual([l.replace(' ', ' ') for l in p.render_above_lines(**kwargs)], output) + +class TestLines(TestRender): @add_p_arg def test_without_above(self, p): self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}') @@ -144,6 +171,115 @@ class TestLines(TestCase): ], width=10) +class TestColorschemesHierarchy(TestRender): + @add_p_arg + def test_group_redirects(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('a', 'd1', draw_hard_divider=False), + highlighted_string('b', 'd2', draw_hard_divider=False), + highlighted_string('c', 'd3', draw_hard_divider=False), + highlighted_string('A', 'm1', draw_hard_divider=False), + highlighted_string('B', 'm2', draw_hard_divider=False), + highlighted_string('C', 'm3', draw_hard_divider=False), + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}') + + @add_p_arg + def test_group_redirects_no_main(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + del config['colorschemes/test/__main__'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('a', 'd1', draw_hard_divider=False), + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}') + + @add_p_arg + def test_group_redirects_no_top_default(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + del config['colorschemes/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('c', 'd3', draw_soft_divider=False), + highlighted_string('C', 'm3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{1112} c{1112}C{--}') + + @add_p_arg + def test_group_redirects_no_test_default(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + del config['colorschemes/test/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('A', 'm1', draw_hard_divider=False), + highlighted_string('B', 'm2', draw_hard_divider=False), + highlighted_string('C', 'm3', draw_hard_divider=False), + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}') + + @add_p_arg + def test_group_redirects_only_main(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + del config['colorschemes/default'] + del config['colorschemes/test/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('C', 'm3', draw_hard_divider=False), + ], + 'right': [], + } + # Powerline is not able to work without default colorscheme + # somewhere, thus it will output error string + self.assertRenderEqual(p, 'colorschemes/test/default') + + @add_p_arg + def test_group_redirects_only_top_default(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + del config['colorschemes/test/__main__'] + del config['colorschemes/test/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}') + + @add_p_arg + def test_group_redirects_only_test_default(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + del config['colorschemes/default'] + del config['colorschemes/test/__main__'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s', 'str1', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{121} s{--}') + + class TestVim(TestCase): def test_environ_update(self): # Regression test: test that segment obtains environment from vim, not From f92e6eb54c693865fba162abdf8edc95a73a3e8d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jul 2014 19:56:31 +0400 Subject: [PATCH 1073/1472] Use `stty size` or zero as a fallback for $COLUMNS Fixes #924 --- powerline/bindings/bash/powerline.sh | 16 +++++++++++-- powerline/bindings/fish/powerline-setup.fish | 25 +++++++++++++++++--- powerline/bindings/zsh/powerline.zsh | 16 +++++++++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 67c1d8bb..f14a4743 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -1,3 +1,15 @@ +_powerline_columns_fallback() { + if which stty &>/dev/null ; then + local cols="$(stty size 2>/dev/null)" + if ! test -z "$cols" ; then + echo "${cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + _powerline_init_tmux_support() { if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one @@ -16,7 +28,7 @@ _powerline_init_tmux_support() { } _powerline_tmux_set_columns() { - _powerline_tmux_setenv COLUMNS "$COLUMNS" + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" } trap "_powerline_tmux_set_columns" SIGWINCH @@ -32,7 +44,7 @@ _powerline_init_tmux_support() { _run_powerline() { # Arguments: side, last_exit_code, jobnum - $POWERLINE_COMMAND shell $1 -w $COLUMNS -r bash_prompt --last_exit_code=$2 --jobnum=$3 + $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" -r bash_prompt --last_exit_code=$2 --jobnum=$3 } _powerline_prompt() { diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 6cb807fa..35ec5763 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -1,4 +1,23 @@ function powerline-setup + function _powerline_columns_fallback + if which stty ^/dev/null + if stty size ^/dev/null + stty size | cut -d' ' -f2 + return 0 + end + end + echo 0 + return 0 + end + + function _powerline_columns + if test -z "$COLUMNS" + _powerline_columns_fallback + else + echo "$COLUMNS" + end + end + if test -z "$POWERLINE_NO_FISH_PROMPT$POWERLINE_NO_SHELL_PROMPT" if test -z "$POWERLINE_COMMAND" if which powerline-client >/dev/null @@ -20,11 +39,11 @@ function powerline-setup if test -z "$POWERLINE_NO_FISH_ABOVE$POWERLINE_NO_SHELL_ABOVE" set promptside aboveleft set rpromptpast 'echo -n " "' - set columnsexpr '(math $COLUMNS - 1)' + set columnsexpr '(math (_powerline_columns) - 1)' else set promptside left set rpromptpast - set columnsexpr '$COLUMNS' + set columnsexpr '(_powerline_columns)' end eval " function fish_prompt @@ -53,7 +72,7 @@ function powerline-setup _powerline_tmux_setenv PWD "$PWD" end function --on-signal WINCH _powerline_tmux_set_columns - _powerline_tmux_setenv COLUMNS "$COLUMNS" + _powerline_tmux_setenv COLUMNS (_powerline_columns) end _powerline_tmux_set_columns _powerline_tmux_set_pwd diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index aad0df53..0eadb16a 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -8,6 +8,18 @@ if test -z "${POWERLINE_COMMAND}" ; then fi fi +_powerline_columns_fallback() { + if which stty &>/dev/null ; then + local cols="$(stty size 2>/dev/null)" + if ! test -z "$cols" ; then + echo "${cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + integer _POWERLINE_JOBNUM _powerline_init_tmux_support() { @@ -28,7 +40,7 @@ _powerline_init_tmux_support() { } function -g _powerline_tmux_set_columns() { - _powerline_tmux_setenv COLUMNS "$COLUMNS" + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" } chpwd_functions+=( _powerline_tmux_set_pwd ) @@ -126,7 +138,7 @@ _powerline_setup_prompt() { new_args_2+=' -R local_theme=continuation' local add_args_3=$add_args' -R local_theme=select' local add_args_2=$add_args$new_args_2 - add_args+=' --width=$(( COLUMNS - 1 ))' + add_args+=' --width=$(( ${COLUMNS:-$(_powerline_columns_fallback)} - 1 ))' local add_args_r2=$add_args$new_args_2 PS1='$($POWERLINE_COMMAND shell aboveleft -r zsh_prompt '$add_args')' RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' From face17174c37f902fb693c134001dba12e15b87a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jul 2014 19:59:36 +0400 Subject: [PATCH 1074/1472] Do not define POWERLINE_COMMAND when not using prompt To be consistent with fish and bash --- powerline/bindings/tcsh/powerline.tcsh | 21 ++++++++++++--------- powerline/bindings/zsh/powerline.zsh | 23 +++++++++++++---------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 91b08993..931b9ab0 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -5,27 +5,30 @@ # Guess this relies on `$_` being set as to last argument to previous command # which must be `.` or `source` in this case set POWERLINE_SOURCED=($_) -if ! $?POWERLINE_COMMAND then - if ( { which powerline-client > /dev/null } ) then - setenv POWERLINE_COMMAND powerline-client - else if ( { which powerline > /dev/null } ) then - setenv POWERLINE_COMMAND powerline - else - setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline - endif -endif if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) then alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" endif if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then + if ! $?POWERLINE_COMMAND then + if ( { which powerline-client > /dev/null } ) then + setenv POWERLINE_COMMAND powerline-client + else if ( { which powerline > /dev/null } ) then + setenv POWERLINE_COMMAND powerline + else + setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline + endif + endif + if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then alias _powerline_above true else alias _powerline_above '$POWERLINE_COMMAND shell above --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' endif + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' + alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' endif diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 0eadb16a..096d0ff4 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,13 +1,3 @@ -if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-client &>/dev/null ; then - export POWERLINE_COMMAND=powerline-client - elif which powerline &>/dev/null ; then - export POWERLINE_COMMAND=powerline - else - export POWERLINE_COMMAND="$0:A:h:h:h:h/scripts/powerline" - fi -fi - _powerline_columns_fallback() { if which stty &>/dev/null ; then local cols="$(stty size 2>/dev/null)" @@ -117,19 +107,32 @@ _powerline_update_counter() { _powerline_setup_prompt() { emulate -L zsh + for f in "${precmd_functions[@]}"; do if [[ "$f" = "_powerline_set_jobnum" ]]; then return fi done precmd_functions+=( _powerline_set_jobnum ) + VIRTUAL_ENV_DISABLE_PROMPT=1 + if test -z "${POWERLINE_NO_ZSH_ZPYTHON}" && { zmodload libzpython || zmodload zsh/zpython } &>/dev/null ; then precmd_functions+=( _powerline_update_counter ) zpython 'from powerline.bindings.zsh import setup as _powerline_setup' zpython '_powerline = _powerline_setup()' zpython 'del _powerline_setup' else + if test -z "${POWERLINE_COMMAND}" ; then + if which powerline-client &>/dev/null ; then + export POWERLINE_COMMAND=powerline-client + elif which powerline &>/dev/null ; then + export POWERLINE_COMMAND=powerline + else + export POWERLINE_COMMAND="$0:A:h:h:h:h/scripts/powerline" + fi + fi + local add_args='--last_exit_code=$?' add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' From d86064436d16734b2c402535ba9a647108394d65 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jul 2014 20:34:39 +0400 Subject: [PATCH 1075/1472] Rename some bash powerline functions Renamed `_powerline_prompt` to `_powerline_set_prompt` and `_run_powerline` to `_powerline_prompt.` Reason: name `_run_powerline` is not consistent with other names used in shell bindings. --- powerline/bindings/bash/powerline.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index f14a4743..d813fc6f 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -42,15 +42,15 @@ _powerline_init_tmux_support() { export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n_powerline_tmux_set_pwd' } -_run_powerline() { +_powerline_prompt() { # Arguments: side, last_exit_code, jobnum $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" -r bash_prompt --last_exit_code=$2 --jobnum=$3 } -_powerline_prompt() { +_powerline_set_prompt() { local last_exit_code=$? local jobnum="$(jobs -p|wc -l)" - PS1="$(_run_powerline aboveleft $last_exit_code $jobnum)" + PS1="$(_powerline_prompt aboveleft $last_exit_code $jobnum)" return $last_exit_code } @@ -66,8 +66,8 @@ _powerline_setup_prompt() { export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" fi fi - test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_prompt*}" || - export PROMPT_COMMAND=$'_powerline_prompt\n'"${PROMPT_COMMAND}" + test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || + export PROMPT_COMMAND=$'_powerline_set_prompt\n'"${PROMPT_COMMAND}" } if test -z "$POWERLINE_NO_BASH_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then From 3d12662f8b5f766e7e06ccab92a29f76bffd5858 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 19 Jul 2014 21:38:49 +0400 Subject: [PATCH 1076/1472] Refactor _powerline_init_tmux_support - Move _powerline_tmux* functions definitions out of _powerline_init_tmux_support. - Do not touch PROMPT_COMMAND unless tmux support is to be added. --- powerline/bindings/bash/powerline.sh | 43 +++++++++++++--------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index d813fc6f..dabacfa9 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -10,36 +10,33 @@ _powerline_columns_fallback() { return 0 } +_powerline_tmux_setenv() { + TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" + TMUX="$_POWERLINE_TMUX" tmux refresh -S +} + +_powerline_tmux_set_pwd() { + if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" +} + _powerline_init_tmux_support() { if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then # TMUX variable may be unset to create new tmux session inside this one _POWERLINE_TMUX="$TMUX" - _powerline_tmux_setenv() { - TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" - TMUX="$_POWERLINE_TMUX" tmux refresh -S - } - - _powerline_tmux_set_pwd() { - if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then - _POWERLINE_SAVED_PWD="$PWD" - _powerline_tmux_setenv PWD "$PWD" - fi - } - - _powerline_tmux_set_columns() { - _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" - } - - trap "_powerline_tmux_set_columns" SIGWINCH + trap "_powerline_tmux_set_columns" WINCH _powerline_tmux_set_columns - else - _powerline_tmux_set_pwd() { - return 0 - } + + test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND/_powerline_tmux_set_pwd}" || + export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n_powerline_tmux_set_pwd' fi - test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND/_powerline_tmux_set_pwd}" || - export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n_powerline_tmux_set_pwd' } _powerline_prompt() { From a8da67e2aabf367687d548b7d15e718012f43c9f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 00:24:12 +0400 Subject: [PATCH 1077/1472] Add various other shell implementations support Tested in busybox, mksh, ksh and dash. Ksh has issues described in troubleshooting. --- .../installation/troubleshooting-common.rst | 7 + docs/source/overview.rst | 32 ++++ powerline/bindings/shell/powerline.sh | 181 ++++++++++++++++++ powerline/renderers/ksh_prompt.py | 20 ++ 4 files changed, 240 insertions(+) create mode 100644 powerline/bindings/shell/powerline.sh create mode 100644 powerline/renderers/ksh_prompt.py diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 98abdd54..bb688d7e 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -132,3 +132,10 @@ To get rid of these lags there currently are two options: the future. * Compile and install ``libzpython`` module that lives in https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. + +Prompt is spoiled after completing files in ksh +----------------------------------------------- + +This is exactly why powerline has official mksh support, but not official ksh +support. If you know the solution feel free to share it in `powerline bug +tracker `_. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 23b16942..3b44268d 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -166,6 +166,38 @@ is the absolute path to your Powerline installation directory: .. _tmux-statusline: +Busybox (ash), mksh and dash prompt +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +After launching busybox run the following command: + +.. code-block:: bash + + . {repository_root}/powerline/bindings/shell/powerline.sh + +Mksh users may put this line into ``~/.mkshrc`` file. Dash users may use the +following in ``~/.profile``: + +.. code-block:: bash + + if test "x$0" != "x${0#dash}" ; then + export ENV={repository_root}/powerline/bindings/shell/powerline.sh + fi + +.. note:: + Dash users that already have ``$ENV`` defined should either put the ``. + …/shell/powerline.sh`` line in the ``$ENV`` file or create a new file which + will source (using ``.`` command) both former ``$ENV`` file and + :file:`powerline.sh` files and set ``$ENV`` to the path of this new file. + +.. warning:: + Job count is using some weird hack that uses signals and temporary files for + interprocess communication. It may be wrong sometimes. Not the case in mksh. + +.. warning:: + Busybox has two shells: ``ash`` and ``hush``. Second is known to segfault in + busybox 1.22.1 when using :file:`powerline.sh` script. + Tmux statusline --------------- diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh new file mode 100644 index 00000000..6f45ffde --- /dev/null +++ b/powerline/bindings/shell/powerline.sh @@ -0,0 +1,181 @@ +_powerline_columns_fallback() { + if which stty >/dev/null ; then + # Ksh does not have “local” built-in + _powerline_cols="$(stty size 2>/dev/null)" + if ! test -z "$_powerline_cols" ; then + echo "${_powerline_cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + +_powerline_has_jobs_in_subshell() { + if test -n "$_POWERLINE_HAS_JOBS_IN_SUBSHELL" ; then + return $_POWERLINE_HAS_JOBS_IN_SUBSHELL + elif test -z "$1" ; then + sleep 1 & + # Check whether shell outputs anything in a subshell when using jobs + # built-in. Shells like dash will not output anything meaning that + # I have to bother with temporary files. + test "$(jobs -p|wc -l)" -gt 0 + else + case "$1" in + dash|bb|ash) return 1 ;; + mksh|ksh|bash) return 0 ;; + *) _powerline_has_jobs_in_subshell ;; + esac + fi + _POWERLINE_HAS_JOBS_IN_SUBSHELL=$? + return $_POWERLINE_HAS_JOBS_IN_SUBSHELL +} + +_powerline_set_append_trap() { + if _powerline_has_jobs_in_subshell "$@" ; then + _powerline_append_trap() { + # Arguments: command, signal + # Ksh does not have “local” built-in + _powerline_traps="$(trap)" + if echo "$_powerline_traps" | grep -cm1 $2'$' >/dev/null ; then + _powerline_traps="$(echo "$_powerline_traps" | sed "s/ $2/'\\n$1' $2/")" + eval "$_powerline_traps" + fi + } + else + _powerline_append_trap() { + # Arguments: command, signal + _powerline_create_temp + trap > $_POWERLINE_TEMP + if grep -cm1 $2'$' $_POWERLINE_TEMP >/dev/null ; then + sed -i -e "s/ $2/'\\n$1' $2/" + . $_POWERLINE_TEMP + else + trap "$1" $2 + fi + echo -n > $_POWERLINE_TEMP + } + fi + _powerline_set_append_trap() { + return 0 + } +} + +_powerline_create_temp() { + if test -z "$_POWERLINE_TEMP" || ! test -e "$_POWERLINE_TEMP" ; then + _POWERLINE_TEMP="$(mktemp)" + _powerline_append_trap 'rm $_POWERLINE_TEMP' EXIT + fi +} + +_powerline_set_set_jobs() { + if _powerline_has_jobs_in_subshell "$@" ; then + _powerline_set_jobs() { + _POWERLINE_JOBS="$(jobs -p|wc -l)" + } + else + _powerline_set_append_trap "$@" + _POWERLINE_PID=$$ + _powerline_append_trap '_powerline_do_set_jobs' USR1 + _powerline_do_set_jobs() { + _powerline_create_temp + jobs -p > $_POWERLINE_TEMP + } + # This command will always be launched from a subshell, thus a hack is + # needed to run `jobs -p` outside of the subshell. + _powerline_set_jobs() { + kill -USR1 $_POWERLINE_PID + # Note: most likely this will read data from the previous run. Tests + # show that it is OK for some reasons. + _POWERLINE_JOBS="$(wc -l < $_POWERLINE_TEMP)" + } + fi + _powerline_set_set_jobs() { + return 0 + } +} + +_powerline_set_command() { + if test -z "${POWERLINE_COMMAND}" ; then + if which powerline-client >/dev/null ; then + export POWERLINE_COMMAND=powerline-client + elif which powerline >/dev/null ; then + export POWERLINE_COMMAND=powerline + else + # `$0` is set to `-bash` when using SSH so that won't work + export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" + fi + fi +} + +_powerline_tmux_setenv() { + TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" + TMUX="$_POWERLINE_TMUX" tmux refresh -S +} + +_powerline_tmux_set_pwd() { + if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" +} + +_powerline_set_renderer_arg() { + case "$1" in + bb|ash) _POWERLINE_RENDERER_ARG="-rbash_prompt" ;; + mksh|ksh) _POWERLINE_RENDERER_ARG="-rksh_prompt" ;; + bash|dash) _POWERLINE_RENDERER_ARG= ;; + esac +} + +_powerline_set_jobs() { + _powerline_set_set_jobs + _powerline_set_jobs +} + +_powerline_prompt() { + # Arguments: side, exit_code + _powerline_set_jobs + $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" $_POWERLINE_RENDERER_ARG --last_exit_code=$2 --jobnum=$_POWERLINE_JOBS +} + +_powerline_setup_prompt() { + VIRTUAL_ENV_DISABLE_PROMPT=1 + _powerline_set_append_trap "$@" + _powerline_set_set_jobs "$@" + _powerline_set_command "$@" + _powerline_set_renderer_arg "$@" + PS1='$(_powerline_prompt aboveleft $?)' +} + +_powerline_init_tmux_support() { + # Dash does not have &>/dev/null + if test -n "$TMUX" && tmux refresh -S >/dev/null 2>/dev/null ; then + # TMUX variable may be unset to create new tmux session inside this one + _POWERLINE_TMUX="$TMUX" + + _powerline_set_append_trap "$@" + + # If _powerline_tmux_set_pwd is used before _powerline_prompt it sets $? + # to zero in ksh. + PS1="$PS1"'$(_powerline_tmux_set_pwd)' + _powerline_append_trap '_powerline_tmux_set_columns' WINCH + _powerline_tmux_set_columns + fi +} + +# Strips the leading `-`: it may be present when shell is a login shell +_POWERLINE_USED_SHELL=${0#-} +_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/usr} +_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/bin/} + +if test -z "$POWERLINE_NO_BB_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then + _powerline_setup_prompt $_POWERLINE_USED_SHELL +fi +if test -z "$POWERLINE_NO_BB_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then + _powerline_init_tmux_support $_POWERLINE_USED_SHELL +fi diff --git a/powerline/renderers/ksh_prompt.py b/powerline/renderers/ksh_prompt.py new file mode 100644 index 00000000..3b4387a4 --- /dev/null +++ b/powerline/renderers/ksh_prompt.py @@ -0,0 +1,20 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import, unicode_literals + +from powerline.renderers.shell import ShellRenderer + + +ESCAPE_CHAR = '\001' + + +class KshPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + escape_hl_start = '\001' + escape_hl_end = '\001' + + def render(self, *args, **kwargs): + return '\001\r' + super(KshPromptRenderer, self).render(*args, **kwargs) + + +renderer = KshPromptRenderer From 82e433906c4fb6e91ca4ec927a17ccefb2a5a343 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 00:32:16 +0400 Subject: [PATCH 1078/1472] Fix shell tests: VIRTUAL_ENV is not accessible by powerline by default --- tests/test_shells/input.bash | 2 +- tests/test_shells/input.fish | 6 +++--- tests/test_shells/input.zsh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 3a95d3a1..748e78fa 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -1,7 +1,7 @@ POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" -VIRTUAL_ENV= +export VIRTUAL_ENV= source powerline/bindings/bash/powerline.sh ; cd tests/shell/3rd cd .git cd .. diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index afb3df3b..3e69ad5e 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -1,13 +1,13 @@ set POWERLINE_COMMAND "$PWD/scripts/powerline -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" -set VIRTUAL_ENV +setenv VIRTUAL_ENV set fish_function_path $fish_function_path "$PWD/powerline/bindings/fish" powerline-setup ; cd tests/shell/3rd cd .git cd .. -set VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" -set VIRTUAL_ENV +setenv VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" +setenv VIRTUAL_ENV bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false kill (cat pid) ; sleep 1s diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 5beb8517..ca888d51 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -2,7 +2,7 @@ unsetopt promptsp transientrprompt POWERLINE_COMMAND=( $PWD/scripts/powerline -p $PWD/powerline/config_files ) POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) -VIRTUAL_ENV= +export VIRTUAL_ENV= source powerline/bindings/zsh/powerline.zsh ; cd tests/shell/3rd cd .git cd .. From b2406d983de140f0f4b33fe9846ee03fd508fe19 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 00:43:19 +0400 Subject: [PATCH 1079/1472] Disable libzpython support in zsh --- tests/test_shells/input.zsh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index ca888d51..b40c3283 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -2,6 +2,11 @@ unsetopt promptsp transientrprompt POWERLINE_COMMAND=( $PWD/scripts/powerline -p $PWD/powerline/config_files ) POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) +setopt interactivecomments +# POWERLINE_CONFIG_PATH=$PWD/powerline/config_files +# POWERLINE_THEME_CONFIG=( default_leftonly.segment_data.hostname.args.only_if_ssh=false ) +# POWERLINE_CONFIG=( ext.shell.theme=default_leftonly ) +POWERLINE_NO_ZSH_ZPYTHON=1 # TODO: make tests work with zsh/zpython export VIRTUAL_ENV= source powerline/bindings/zsh/powerline.zsh ; cd tests/shell/3rd cd .git From 5425c0647640f92b8472d02a84bdd8e70511300a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 01:49:30 +0400 Subject: [PATCH 1080/1472] Add tests for new functionality --- tests/install.sh | 2 +- tests/test.sh | 2 +- tests/test_shells/bb.ok | 17 ++++++++++ tests/test_shells/dash.ok | 17 ++++++++++ tests/test_shells/input.bb | 23 ++++++++++++++ tests/test_shells/input.dash | 23 ++++++++++++++ tests/test_shells/input.mksh | 23 ++++++++++++++ tests/test_shells/mksh.ok | 19 ++++++++++++ tests/test_shells/test.sh | 60 ++++++++++++++++++++++++++++++++---- 9 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 tests/test_shells/bb.ok create mode 100644 tests/test_shells/dash.ok create mode 100644 tests/test_shells/input.bb create mode 100644 tests/test_shells/input.dash create mode 100644 tests/test_shells/input.mksh create mode 100644 tests/test_shells/mksh.ok diff --git a/tests/install.sh b/tests/install.sh index 05d2e3d2..1f1a3729 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -12,7 +12,7 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -sudo apt-get install -qq screen zsh tcsh +sudo apt-get install -qq screen zsh tcsh mksh busybox # Travis has too outdated fish. It cannot be used for tests. # sudo apt-get install fish true diff --git a/tests/test.sh b/tests/test.sh index 5ea9d0ea..f25d63a6 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -20,7 +20,7 @@ for script in tests/*.vim ; do FAILED=1 fi done -if ! sh tests/test_shells/test.sh ; then +if ! bash tests/test_shells/test.sh ; then echo "Failed shells" if ${PYTHON} -c 'import platform, sys; sys.exit(1 * (platform.python_implementation() == "PyPy"))' ; then FAILED=1 diff --git a/tests/test_shells/bb.ok b/tests/test_shells/bb.ok new file mode 100644 index 00000000..8a439c05 --- /dev/null +++ b/tests/test_shells/bb.ok @@ -0,0 +1,17 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  cd "$DIR1" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/dash.ok b/tests/test_shells/dash.ok new file mode 100644 index 00000000..2a42cd64 --- /dev/null +++ b/tests/test_shells/dash.ok @@ -0,0 +1,17 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1] + Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +cd "$DIR1" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1    HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/input.bb b/tests/test_shells/input.bb new file mode 100644 index 00000000..d9ae1342 --- /dev/null +++ b/tests/test_shells/input.bb @@ -0,0 +1,23 @@ +POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +export VIRTUAL_ENV= +. powerline/bindings/shell/powerline.sh ; cd tests/shell/3rd +cd .git +cd .. +VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +VIRTUAL_ENV= +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +false +kill `cat pid` ; sleep 1s +cd "$DIR1" +cd ../"$DIR2" +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' +false +true is the last line +exit diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash new file mode 100644 index 00000000..d9ae1342 --- /dev/null +++ b/tests/test_shells/input.dash @@ -0,0 +1,23 @@ +POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +export VIRTUAL_ENV= +. powerline/bindings/shell/powerline.sh ; cd tests/shell/3rd +cd .git +cd .. +VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +VIRTUAL_ENV= +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +false +kill `cat pid` ; sleep 1s +cd "$DIR1" +cd ../"$DIR2" +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' +false +true is the last line +exit diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh new file mode 100644 index 00000000..a4a5928a --- /dev/null +++ b/tests/test_shells/input.mksh @@ -0,0 +1,23 @@ +POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" +export VIRTUAL_ENV= +. powerline/bindings/shell/powerline.sh ; cd tests/shell/3rd +cd .git +cd .. +VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +VIRTUAL_ENV= +bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +false +kill `cat pid` ; sleep 1 +cd "$DIR1" +cd ../"$DIR2" +cd ../'\[\]' +cd ../'%%' +cd ../'#[bold]' +cd ../'(echo)' +cd ../'$(echo)' +cd ../'`echo`' +false +true is the last line +exit diff --git a/tests/test_shells/mksh.ok b/tests/test_shells/mksh.ok new file mode 100644 index 00000000..f03a4c29 --- /dev/null +++ b/tests/test_shells/mksh.ok @@ -0,0 +1,19 @@ + +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 +[1] + Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 9e9c370b..69a34fc9 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash FAILED=0 ONLY_SHELL="$1" @@ -16,20 +16,38 @@ check_screen_log() { run_test() { SH="$1" SESNAME="powerline-shell-test-${SH}-$$" + ARGS=( "$@" ) test "x$ONLY_SHELL" = "x" || test "x$ONLY_SHELL" = "x$SH" || return 0 - which "${SH}" || return 0 + if ! which "${SH}" ; then + if test "x${SH}" = "xbb" ; then + if ! which busybox ; then + return 0 + fi + shift + ARGS=( busybox ash "$@" ) + else + return 0 + fi + fi export SH screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ - env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" + env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH # Wait for screen to initialize - sleep 1s + sleep 1 screen -S "$SESNAME" -p 0 -X width 300 1 - screen -S "$SESNAME" -p 0 -X paste a + if test "x${SH}" = "xdash" ; then + while read -r line ; do + screen -S "$SESNAME" -p 0 -X stuff "$line"$'\n' + sleep 1 + done < tests/test_shells/input.$SH + else + screen -S "$SESNAME" -p 0 -X paste a + fi # Wait for screen to exit (sending command to non-existing screen session # fails; when launched instance exits corresponding session is deleted) while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do @@ -51,7 +69,23 @@ run_test() { echo '============================================================' cat -v tests/shell/${SH}.full.log echo '____________________________________________________________' - ${SH} --version + case ${SH} in + *ksh) + ${SH} -c 'echo ${KSH_VERSION}' + ;; + dash) + # ? + ;; + bb) + bb --help + ;; + *) + ${SH} --version + ;; + esac + if which dpkg >/dev/null ; then + dpkg -s ${SH} + fi return 1 fi return 0 @@ -90,5 +124,19 @@ if ! run_test tcsh -f -i ; then FAILED=1 fi +if ! run_test bb -i ; then + FAILED=1 +fi + +unset ENV + +if ! run_test mksh -i ; then + FAILED=1 +fi + +if ! run_test dash -i ; then + FAILED=1 +fi + test "x$ONLY_SHELL" = "x" && rm -r tests/shell exit $FAILED From 855e33274ee3ac881fdf66a68fbea6069ce2ce09 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 02:00:56 +0400 Subject: [PATCH 1081/1472] =?UTF-8?q?Fix=20travis=20build:=20on=20travis?= =?UTF-8?q?=20=E2=80=9CTerminated=E2=80=A6=E2=80=9D=20message=20is=20trunc?= =?UTF-8?q?ated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_shells/mksh.ok | 2 +- tests/test_shells/postproc.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/mksh.ok b/tests/test_shells/mksh.ok index f03a4c29..09307716 100644 --- a/tests/test_shells/mksh.ok +++ b/tests/test_shells/mksh.ok @@ -7,7 +7,7 @@ [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 -[1] + Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +[1] + Terminated bash -c ...   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 8d85917c..ce68c9d5 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -48,4 +48,9 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: line = line[start:end] + '\033[0m\n' except ValueError: line = '' + elif shell == 'mksh': + # Output is different in travis: on my machine I see full + # command, in travis it is truncated just after `true`. + if line.startswith('[1] + Terminated'): + line = '[1] + Terminated bash -c ...\n' W.write(line) From 97e4ee6799bbcf0c4558e5228ceeb6122773d663 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 02:04:44 +0400 Subject: [PATCH 1082/1472] Document why `stuff` and waiting are used for dash --- tests/test_shells/test.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 69a34fc9..82f17f71 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -41,6 +41,12 @@ run_test() { sleep 1 screen -S "$SESNAME" -p 0 -X width 300 1 if test "x${SH}" = "xdash" ; then + # If I do not use this hack for dash then output will look like + # + # command1 + # command2 + # … + # prompt1> prompt2> … while read -r line ; do screen -S "$SESNAME" -p 0 -X stuff "$line"$'\n' sleep 1 From 1447be1ddcb5484fae2688ad84b0a5b3dd2d7961 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 23 Jul 2014 01:47:46 +0400 Subject: [PATCH 1083/1472] Add missing imports in powerline-config They matter when script is used without properly installing powerline, so that importing `powerline.bindings.config` does not work. --- scripts/powerline-config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/powerline-config b/scripts/powerline-config index 028ab783..bcece7e6 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -7,6 +7,8 @@ import argparse try: import powerline.bindings.config as config except ImportError: + import sys + import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) import powerline.bindings.config as config # NOQA From cd5c6fe719752513e97d51ab059fd3e7e972db57 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 26 Jul 2014 01:09:56 +0400 Subject: [PATCH 1084/1472] Make sure arguments in `args` are only strings Required for python-2.6 Fixes #933 --- powerline/segment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segment.py b/powerline/segment.py index cf495c87..d0ab2746 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -94,7 +94,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) - args['create_watcher'] = create_watcher + args[str('create_watcher')] = create_watcher if hasattr(_contents_func, 'powerline_requires_segment_info'): contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) From a279ea236c286804d4bc8e794a98417dc542347a Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 25 Jul 2014 16:25:50 +0400 Subject: [PATCH 1085/1472] Also check error messages --- tests/test_configuration.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d62ce173..b532db81 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -190,6 +190,7 @@ class TestColorschemesHierarchy(TestRender): 'right': [], } self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) @add_p_arg def test_group_redirects_no_main(self, p): @@ -205,6 +206,7 @@ class TestColorschemesHierarchy(TestRender): 'right': [], } self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) @add_p_arg def test_group_redirects_no_top_default(self, p): @@ -218,6 +220,7 @@ class TestColorschemesHierarchy(TestRender): 'right': [], } self.assertRenderEqual(p, '{1112} c{1112}C{--}') + self.assertEqual(p.logger._pop_msgs(), []) @add_p_arg def test_group_redirects_no_test_default(self, p): @@ -235,6 +238,7 @@ class TestColorschemesHierarchy(TestRender): 'right': [], } self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) @add_p_arg def test_group_redirects_only_main(self, p): @@ -250,6 +254,12 @@ class TestColorschemesHierarchy(TestRender): # Powerline is not able to work without default colorscheme # somewhere, thus it will output error string self.assertRenderEqual(p, 'colorschemes/test/default') + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load colorscheme: colorschemes/default', + 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default', + 'exception:test:powerline:Failed to create renderer: colorschemes/test/default', + 'exception:test:powerline:Failed to render: colorschemes/test/default', + ]) @add_p_arg def test_group_redirects_only_top_default(self, p): @@ -265,6 +275,7 @@ class TestColorschemesHierarchy(TestRender): 'right': [], } self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) @add_p_arg def test_group_redirects_only_test_default(self, p): @@ -278,6 +289,7 @@ class TestColorschemesHierarchy(TestRender): 'right': [], } self.assertRenderEqual(p, '{121} s{--}') + self.assertEqual(p.logger._pop_msgs(), []) class TestVim(TestCase): From d0c4d4e266fe7f3c05d3cf031702f5fd7dbf3e90 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 26 Jul 2014 01:25:32 +0400 Subject: [PATCH 1086/1472] =?UTF-8?q?Ignore=20IOError=E2=80=99s=20in=20Con?= =?UTF-8?q?figLoader.update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #932 --- powerline/lib/config.py | 5 ++- tests/lib/fsconfig.py | 80 ++++++++++++++++++++++++++++++++++++++++ tests/test_lib_config.py | 51 +++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/lib/fsconfig.py create mode 100644 tests/test_lib_config.py diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 8dd50afe..eaf4c502 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -109,7 +109,8 @@ class ConfigLoader(MultiRunnedThread): :param function condition_function: Function which will be called each ``interval`` seconds. All - exceptions from it will be ignored. + exceptions from it will be logged and ignored. IOError exception + will be ignored without logging. :param function function: Function which will be called if condition_function returns something that is true. Accepts result of condition_function as an @@ -179,6 +180,8 @@ class ConfigLoader(MultiRunnedThread): for condition_function, function in list(functions): try: path = condition_function(key) + except IOError: + pass except Exception as e: self.exception('Error while running condition function for key {0}: {1}', key, str(e)) else: diff --git a/tests/lib/fsconfig.py b/tests/lib/fsconfig.py new file mode 100644 index 00000000..db80488b --- /dev/null +++ b/tests/lib/fsconfig.py @@ -0,0 +1,80 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals, absolute_import, division + +import os +import json + +from subprocess import check_call +from operator import add +from shutil import rmtree + +from powerline import Powerline + + +CONFIG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config') + + +class TestPowerline(Powerline): + def __init__(self, _paths, *args, **kwargs): + super(TestPowerline, self).__init__(*args, **kwargs) + self._paths = _paths + + def get_config_paths(self): + return self._paths + + +def mkdir_recursive(directory): + if os.path.isdir(directory): + return + mkdir_recursive(os.path.dirname(directory)) + os.mkdir(directory) + + +class FSTree(object): + __slots__ = ('tree', 'p', 'p_kwargs', 'create_p', 'get_config_paths', 'root') + + def __init__( + self, + tree, + p_kwargs={'run_once': True}, + root=CONFIG_DIR, + get_config_paths=lambda p: (p,), + create_p=False + ): + self.tree = tree + self.root = root + self.get_config_paths = get_config_paths + self.create_p = create_p + self.p = None + self.p_kwargs = p_kwargs + + def __enter__(self, *args): + os.mkdir(self.root) + for k, v in self.tree.items(): + fname = os.path.join(self.root, k) + '.json' + mkdir_recursive(os.path.dirname(fname)) + with open(fname, 'w') as F: + json.dump(v, F) + if self.create_p: + self.p = TestPowerline( + _paths=self.get_config_paths(self.root), + ext='test', + renderer_module='tests.lib.config_mock', + **self.p_kwargs + ) + if os.environ.get('POWERLINE_RUN_LINT_DURING_TESTS'): + try: + check_call(['scripts/powerline-lint'] + reduce(add, ( + ['-p', d] for d in self.p.get_config_paths() + ))) + except: + self.__exit__() + raise + return self.p and self.p.__enter__(*args) + + def __exit__(self, *args): + try: + rmtree(self.root) + finally: + if self.p: + self.p.__exit__(*args) diff --git a/tests/test_lib_config.py b/tests/test_lib_config.py new file mode 100644 index 00000000..5c941d72 --- /dev/null +++ b/tests/test_lib_config.py @@ -0,0 +1,51 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals, absolute_import, division + +import os + +from powerline.lib.config import ConfigLoader +from tests import TestCase +from tests.lib.fsconfig import FSTree + + +FILE_ROOT = os.path.join(os.path.dirname(__file__), 'cfglib') + + +class LoadedList(list): + def pop_all(self): + try: + return self[:] + finally: + self[:] = () + + +loaded = LoadedList() + + +def on_load(key): + loaded.append(key) + + +def check_file(path): + if os.path.exists(path): + return path + else: + raise IOError + + +class TestLoaderCondition(TestCase): + def test_update_missing(self): + loader = ConfigLoader(run_once=True) + fpath = os.path.join(FILE_ROOT, 'file.json') + self.assertRaises(IOError, loader.load, fpath) + loader.register_missing(check_file, on_load, fpath) + loader.update() # This line must not raise IOError + with FSTree({'file': {'test': 1}}, root=FILE_ROOT): + loader.update() + self.assertEqual(loader.load(fpath), {'test': 1}) + self.assertEqual(loaded.pop_all(), [fpath]) + + +if __name__ == '__main__': + from tests import main + main() From d1b62eba4578115746249036cd41b8524aabe6a6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 28 Jul 2014 03:30:51 +0400 Subject: [PATCH 1087/1472] Disable dash tests Temporary fix for #931 --- tests/test_shells/test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 82f17f71..a9bdfc31 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -141,7 +141,8 @@ if ! run_test mksh -i ; then fi if ! run_test dash -i ; then - FAILED=1 + # dash tests are not stable, see #931 + # FAILED=1 fi test "x$ONLY_SHELL" = "x" && rm -r tests/shell From 6a081685e53747896b7b242edf878e1c163c04d5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 28 Jul 2014 03:36:15 +0400 Subject: [PATCH 1088/1472] =?UTF-8?q?Fix=20=E2=80=9Csyntax=20error=20near?= =?UTF-8?q?=20unexpected=20token=20`fi'=E2=80=9D=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_shells/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index a9bdfc31..b0f30fed 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -143,6 +143,7 @@ fi if ! run_test dash -i ; then # dash tests are not stable, see #931 # FAILED=1 + true fi test "x$ONLY_SHELL" = "x" && rm -r tests/shell From c91a0fa769767b5ed355dce9aab216a01ce612d0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 29 Jul 2014 14:31:17 +0400 Subject: [PATCH 1089/1472] Remove note about Gentoo ebuild It is not hosted in this repo any longer. --- docs/source/overview.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 3b44268d..5aedaf74 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -127,9 +127,6 @@ hand: ``powerline`` is installed and run just like any other plugin using call vam#ActivateAddons(['powerline']) -Note: when using Gentoo ebuild you need to specify ``USE=vim`` to enable -powerline. - Shell prompts ------------- From 191c71cf536ef2ffb16da4d1f1f1b4450d0c43ba Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 29 Jul 2014 14:48:26 +0400 Subject: [PATCH 1090/1472] Do not let powerline trigger loading wrong python Used python version is controlled by `g:powerline_pycmd`. User configuration now has top priority: if `g:powerline_pyeval` is set powerline will not try to use `pyeval()` emulation in old Vim versions. Closes #937 as WONTFIX --- docs/source/overview.rst | 19 +++++++++ powerline/bindings/vim/plugin/powerline.vim | 45 ++++++++++++++++++--- powerline/vim.py | 5 ++- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 5aedaf74..9ddc798a 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -127,6 +127,25 @@ hand: ``powerline`` is installed and run just like any other plugin using call vam#ActivateAddons(['powerline']) +.. note:: + If you use supplied :file:`powerline.vim` file to load powerline there are + additional configuration variables available: ``g:powerline_pycmd`` and + ``g:powerline_pyeval``. First sets command used to load powerline: expected + values are ``"py"`` and ``"py3"``. Second sets function used in statusline, + expected values are ``"pyeval"`` and ``"py3eval"``. + + If ``g:powerline_pycmd`` is set to the one of the expected values then + ``g:powerline_pyeval`` will be set accordingly. If it is set to some other + value then you must also set ``g:powerline_pyeval``. Powerline will not + check that Vim is compiled with Python support if you set + ``g:powerline_pycmd`` to an unexpected value. + + These values are to be used to specify the only Python that is to be loaded + if you have both versions: Vim may disable loading one python version if + other was already loaded. They should also be used if you have two python + versions able to load simultaneously, but with powerline installed only for + python-3 version. + Shell prompts ------------- diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 3aa97853..15d37d05 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -3,7 +3,41 @@ if exists('g:powerline_loaded') endif let g:powerline_loaded = 1 -if !has('python') && !has('python3') +if exists('g:powerline_pycmd') + let s:pycmd = substitute(g:powerline_pycmd, '\v\C^(py)%[thon](3?)$', '\1\2', '') + if s:pycmd is# 'py' + let s:has_python = has('python') + let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval') + elseif s:pycmd is# 'py3' + let s:has_python = has('python3') + let s:pyeval = 'py3eval' + let s:pyeval = get(g:, 'powerline_pyeval', 'py3eval') + else + if !exists('g:powerline_pyeval') + echohl ErrorMsg + echomsg 'g:powerline_pycmd was set to an unknown values, but g:powerline_pyeval' + echomsg 'was not set. You should either set g:powerline_pycmd to "py3" or "py",' + echomsg 'specify g:powerline_pyeval explicitly or unset both and let powerline' + echomsg 'figure them out.' + echohl None + finish + endif + let s:pyeval = g:powerline_pyeval + let s:has_python = 1 + endif +elseif has('python') + let s:has_python = 1 + let s:pycmd = 'py' + let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval') +elseif has('python3') + let s:has_python = 1 + let s:pycmd = 'py3' + let s:pyeval = get(g:, 'powerline_pyeval', 'py3eval') +else + let s:has_python = 0 +endif + +if !s:has_python if !exists('g:powerline_no_python_error') echohl ErrorMsg echomsg 'You need vim compiled with Python 2.6, 2.7 or 3.2 and later support' @@ -13,9 +47,7 @@ if !has('python') && !has('python3') endif finish endif - -let s:pycmd = substitute(get(g:, 'powerline_pycmd', has('python') ? 'py' : 'py3'), '\v^(py)%[thon](3?)$', '\1\2', '') -let s:pyeval = get(g:, 'powerline_pyeval', s:pycmd.'eval') +unlet s:has_python let s:import_cmd = 'from powerline.vim import setup as powerline_setup' try @@ -81,9 +113,12 @@ finally endif endtry +let s:can_replace_pyeval = !exists('g:powerline_pyeval') + execute s:pycmd 'import vim' -execute s:pycmd 'powerline_setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"))' +execute s:pycmd 'powerline_setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"), can_replace_pyeval=int(vim.eval("s:can_replace_pyeval")))' execute s:pycmd 'del powerline_setup' +unlet s:can_replace_pyeval unlet s:pycmd unlet s:pyeval diff --git a/powerline/vim.py b/powerline/vim.py index a26f5e40..829961e9 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -155,16 +155,17 @@ class VimPowerline(Powerline): __main__.__dict__))) -def setup(pyeval=None, pycmd=None): +def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): import sys import __main__ if not pyeval: pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' + can_replace_pyeval = True if not pycmd: pycmd = 'python' if sys.version_info < (3,) else 'python3' # pyeval() and vim.bindeval were both introduced in one patch - if not hasattr(vim, 'bindeval'): + if not hasattr(vim, 'bindeval') and can_replace_pyeval: vim.command((''' function! PowerlinePyeval(e) {pycmd} powerline.pyeval() From 8cb478f859327fa6e2a15226b12dddf6fe08a570 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 29 Jul 2014 16:24:38 +0400 Subject: [PATCH 1091/1472] Add information about g:powerline_pycmd to error message --- powerline/bindings/vim/plugin/powerline.vim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 15d37d05..cdf92726 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -70,6 +70,10 @@ finally echomsg 'Python 2.6, 2.7 or 3.2 and later to work). Please consult' echomsg 'the troubleshooting section in the documentation for' echomsg 'possible solutions.' + if s:pycmd is# 'py' && has('python3') + echomsg 'If powerline on your system is installed for python 3 only you' + echomsg 'should set g:powerline_pycmd to "py3" to make it load correctly.' + endif echohl None let s:pystr = "def powerline_troubleshoot():\n" let s:pystr .= " import sys\n" From 16c01e8d644d2f5c733f1aa5e0606b4511d95052 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 23:39:45 +0400 Subject: [PATCH 1092/1472] Add support for display option --- docs/source/configuration.rst | 15 +++++++++++---- powerline/lint/__init__.py | 28 +++++++++++++++++++--------- powerline/segment.py | 3 +++ powerline/theme.py | 17 +++++++++-------- tests/test_configuration.py | 10 ++++++++++ 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index c3c8c110..23a3022e 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -325,13 +325,14 @@ Themes A dict where keys are segment names or strings ``{module}.{name}``. Used to specify default values for various keys: :ref:`after `, + :ref:`args ` (only for function segments), :ref:`before `, :ref:`contents ` (only for string segments if :ref:`name ` is defined), - :ref:`args ` (only for function segments). When - using :ref:`local themes ` values of these keys are - first searched in the segment description, then in ``segment_data`` key of - a local theme, then in ``segment_data`` key of a :ref:`default theme + :ref:`display ` values of these + keys are first searched in the segment description, then in ``segment_data`` + key of a local theme, then in ``segment_data`` key of a :ref:`default theme `. For the :ref:`default theme ` itself step 2 is obviously avoided. @@ -455,6 +456,12 @@ Themes A list of modes where this segment will be included: The segment is *not* included in any modes, *except* for the modes in this list. + ``display`` + .. _config-themes-seg-display: + + Boolean. If false disables displaying of the segment. + Defaults to ``True``. + Segments ======== diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 074e00e8..1976e876 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -668,17 +668,25 @@ shell_colorscheme_spec = (Spec( ).context_message('Error while loading shell colorscheme')) -generic_keys = set(('exclude_modes', 'include_modes', 'width', 'align', 'name', 'draw_soft_divider', 'draw_hard_divider', 'priority', 'after', 'before')) +generic_keys = set(( + 'exclude_modes', 'include_modes', + 'width', 'align', + 'name', + 'draw_soft_divider', 'draw_hard_divider', + 'priority', + 'after', 'before', + 'display' +)) type_keys = { - 'function': set(('args', 'module', 'draw_inner_divider')), - 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), - 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), - } + 'function': set(('args', 'module', 'draw_inner_divider')), + 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), + 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), +} required_keys = { - 'function': set(), - 'string': set(('contents',)), - 'filler': set(), - } + 'function': set(), + 'string': set(('contents',)), + 'filler': set(), +} function_keys = set(('args', 'module')) highlight_keys = set(('highlight_group', 'name')) @@ -1071,6 +1079,7 @@ segments_spec = Spec().optional().list( draw_hard_divider=Spec().type(bool).optional(), draw_soft_divider=Spec().type(bool).optional(), draw_inner_divider=Spec().type(bool).optional(), + display=Spec().type(bool).optional(), module=segment_module_spec(), priority=Spec().type(int, float, type(None)).optional(), after=Spec().type(unicode).optional(), @@ -1101,6 +1110,7 @@ theme_spec = (Spec( Spec( after=Spec().type(unicode).optional(), before=Spec().type(unicode).optional(), + display=Spec().type(bool).optional(), args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_segments, *args, **kwargs)), contents=Spec().type(unicode).optional(), ), diff --git a/powerline/segment.py b/powerline/segment.py index d0ab2746..836a5739 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -82,6 +82,9 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') return None + if not get_key(segment, module, 'display', True): + return None + if segment_type == 'function': highlight_group = [module + '.' + segment['name'], segment['name']] else: diff --git a/powerline/theme.py b/powerline/theme.py index 6450fab3..9de033a9 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -49,14 +49,15 @@ class Theme(object): for side in ['left', 'right']: for segment in segdict.get(side, []): segment = get_segment(segment, side) - if not run_once: - if segment['startup']: - try: - segment['startup'](pl, shutdown_event) - except Exception as e: - pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) - continue - self.segments[-1][side].append(segment) + if segment: + if not run_once: + if segment['startup']: + try: + segment['startup'](pl, shutdown_event) + except Exception as e: + pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) + continue + self.segments[-1][side].append(segment) def shutdown(self): for line in self.segments: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index b532db81..faf355b5 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -171,6 +171,16 @@ class TestLines(TestRender): ], width=10) +class TestSegments(TestRender): + @add_p_arg + def test_display(self, p): + with replace_item(globals(), 'config', deepcopy(config)): + config['themes/test/default']['segments']['left'][0]['display'] = False + config['themes/test/default']['segments']['left'][1]['display'] = True + config['themes/test/default']['segments']['right'][0]['display'] = False + self.assertRenderEqual(p, '{344} g{4-}>>{--}') + + class TestColorschemesHierarchy(TestRender): @add_p_arg def test_group_redirects(self, p): From 9e8c115eea6080b0a9e7c41444f203a91ea36505 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 18:22:45 +0400 Subject: [PATCH 1093/1472] Add trailing whitespace segment Note: by default this segment is disabled. Until #923 it may only be enabled by copying the whole file and changing "enabled" to "true". After #923 it may be enabled by having `~/.config/powerline/themes/vim/default.json` with the following contents: { "segment_data": { "trailing_whitespace": { "display": true } } } Fixes #388 --- powerline/bindings/vim/__init__.py | 27 ++++++++ .../config_files/themes/vim/default.json | 6 ++ powerline/segments/vim.py | 61 ++++++++++++++++++- powerline/vim.py | 18 +++++- tests/test_segments.py | 20 ++++++ tests/vim.py | 50 ++++++++++++++- 6 files changed, 177 insertions(+), 5 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 40e1f616..6830365c 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -159,4 +159,31 @@ def powerline_vim_strtrans_error(e): codecs.register_error('powerline_vim_strtrans_error', powerline_vim_strtrans_error) +did_autocmd = False +buffer_caches = [] + + +def register_buffer_cache(cachedict): + global did_autocmd + global buffer_caches + from powerline.vim import get_default_pycmd, pycmd + if not did_autocmd: + import __main__ + __main__.powerline_on_bwipe = on_bwipe + vim.command('augroup Powerline') + vim.command(' autocmd! BufWipeout * :{pycmd} powerline_on_bwipe()'.format( + pycmd=(pycmd or get_default_pycmd()))) + vim.command('augroup END') + did_autocmd = True + buffer_caches.append(cachedict) + return cachedict + + +def on_bwipe(): + global buffer_caches + bufnr = int(vim.eval('expand("")')) + for cachedict in buffer_caches: + cachedict.pop(bufnr, None) + + environ = VimEnviron() diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 5f10912d..05204943 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -58,6 +58,12 @@ "name": "modified_indicator", "before": " " }, + { + "exclude_modes": ["i", "R", "Rv"], + "name": "trailing_whitespace", + "display": false, + "priority": 60 + }, { "exclude_modes": ["nc"], "module": "powerline.segments.plugin.syntastic", diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index f1647dee..62f4fe25 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -8,8 +8,14 @@ try: except ImportError: vim = {} # NOQA +try: + from __builtin__ import xrange as range +except ImportError: + pass + from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, - buffer_name, vim_getwinvar) + buffer_name, vim_getwinvar, + register_buffer_cache) from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -17,6 +23,7 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib import wraps_saveargs as wraps from collections import defaultdict + vim_funcs = { 'virtcol': vim_get_func('virtcol', rettype=int), 'getpos': vim_get_func('getpos'), @@ -420,3 +427,55 @@ def file_vcs_status(pl, segment_info, create_watcher): 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], }) return ret + + +trailing_whitespace_cache = None + + +@requires_segment_info +def trailing_whitespace(pl, segment_info): + '''Return the line number for trailing whitespaces + + It is advised not to use this segment in insert mode: in Insert mode it will + iterate over all lines in buffer each time you happen to type a character + which may cause lags. It will also show you whitespace warning each time you + happen to type space. + + Highlight groups used: ``trailing_whitespace`` or ``warning``. + ''' + global trailing_whitespace_cache + if trailing_whitespace_cache is None: + trailing_whitespace_cache = register_buffer_cache(defaultdict(lambda: (0, None))) + bufnr = segment_info['bufnr'] + changedtick = getbufvar(bufnr, 'changedtick') + if trailing_whitespace_cache[bufnr][0] == changedtick: + return trailing_whitespace_cache[bufnr][1] + else: + buf = segment_info['buffer'] + bws = b' \t' + sws = str(bws) + for i in range(len(buf)): + try: + line = buf[i] + except UnicodeDecodeError: # May happen in Python 3 + if hasattr(vim, 'bindeval'): + line = vim.bindeval('getbufline({0}, {1})'.format( + bufnr, i + 1)) + has_trailing_ws = (line[-1] in bws) + else: + line = vim.eval('strtrans(getbufline({0}, {1}))'.format( + bufnr, i + 1)) + has_trailing_ws = (line[-1] in bws) + else: + has_trailing_ws = (line and line[-1] in sws) + if has_trailing_ws: + break + if has_trailing_ws: + ret = [{ + 'contents': str(i + 1), + 'highlight_group': ['trailing_whitespace', 'warning'], + }] + else: + ret = None + trailing_whitespace_cache[bufnr] = (changedtick, ret) + return ret diff --git a/powerline/vim.py b/powerline/vim.py index 829961e9..05d42de4 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -2,6 +2,7 @@ from __future__ import absolute_import +import sys from powerline.bindings.vim import vim_get_func, vim_getvar from powerline import Powerline from powerline.lib import mergedicts @@ -155,14 +156,27 @@ class VimPowerline(Powerline): __main__.__dict__))) +pycmd = None + + +def set_pycmd(new_pycmd): + global pycmd + pycmd = new_pycmd + + +def get_default_pycmd(): + return 'python' if sys.version_info < (3,) else 'python3' + + def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): - import sys import __main__ if not pyeval: pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' can_replace_pyeval = True if not pycmd: - pycmd = 'python' if sys.version_info < (3,) else 'python3' + pycmd = get_default_pycmd() + + set_pycmd(pycmd) # pyeval() and vim.bindeval were both introduced in one patch if not hasattr(vim, 'bindeval') and can_replace_pyeval: diff --git a/tests/test_segments.py b/tests/test_segments.py index 441d6951..de467728 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -757,6 +757,26 @@ class TestVim(TestCase): with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')): self.assertEqual(file_vcs_status(segment_info=segment_info), None) + def test_trailing_whitespace(self): + pl = Pl() + with vim_module._with('buffer', 'tws') as segment_info: + trailing_whitespace = partial(vim.trailing_whitespace, pl=pl, segment_info=segment_info) + self.assertEqual(trailing_whitespace(), None) + self.assertEqual(trailing_whitespace(), None) + vim_module.current.buffer[0] = ' ' + self.assertEqual(trailing_whitespace(), [{ + 'highlight_group': ['trailing_whitespace', 'warning'], + 'contents': '1', + }]) + self.assertEqual(trailing_whitespace(), [{ + 'highlight_group': ['trailing_whitespace', 'warning'], + 'contents': '1', + }]) + vim_module.current.buffer[0] = '' + self.assertEqual(trailing_whitespace(), None) + self.assertEqual(trailing_whitespace(), None) + + old_cwd = None diff --git a/tests/vim.py b/tests/vim.py index 89d12b65..0c0cefaa 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -167,8 +167,14 @@ def _log_print(): sys.stdout.write(repr(entry) + '\n') +_current_group = None +_on_wipeout = [] + + @_vim def command(cmd): + global _current_group + cmd = cmd.lstrip() if cmd.startswith('let g:'): import re varname, value = re.compile(r'^let g:(\w+)\s*=\s*(.*)').match(cmd).groups() @@ -179,6 +185,26 @@ def command(cmd): elif cmd.startswith('function! Powerline_plugin_ctrlp'): # Ignore CtrlP updating functions pass + elif cmd.startswith('augroup'): + augroup = cmd.partition(' ')[2] + if augroup.upper() == 'END': + _current_group = None + else: + _current_group = augroup + elif cmd.startswith('autocmd'): + rest = cmd.partition(' ')[2] + auevent, rest = rest.partition(' ')[::2] + pattern, aucmd = rest.partition(' ')[::2] + if auevent != 'BufWipeout' or pattern != '*': + raise NotImplementedError + import sys + if sys.version_info < (3,): + if not aucmd.startswith(':python '): + raise NotImplementedError + else: + if not aucmd.startswith(':python3 '): + raise NotImplementedError + _on_wipeout.append(aucmd.partition(' ')[2]) else: raise NotImplementedError @@ -314,8 +340,9 @@ def _emul_fnamemodify(path, modstring): @_vim @_str_func def _emul_expand(expr): + global _abuf if expr == '': - return _buffer() + return _abuf or _buffer() raise NotImplementedError @@ -398,6 +425,7 @@ class _Window(object): _buf_lines = {} _undostate = {} _undo_written = {} +_abuf = None class _Buffer(object): @@ -408,7 +436,7 @@ class _Buffer(object): self.number = bufnr # FIXME Use unicode() for python-3 self.name = name - self.vars = {} + self.vars = {'changedtick': 1} self.options = { 'modified': 0, 'readonly': 0, @@ -447,12 +475,14 @@ class _Buffer(object): def __setitem__(self, line, value): self.options['modified'] = 1 + self.vars['changedtick'] += 1 _buf_lines[self.number][line] = value from copy import copy _undostate[self.number].append(copy(_buf_lines[self.number])) def __setslice__(self, *args): self.options['modified'] = 1 + self.vars['changedtick'] += 1 _buf_lines[self.number].__setslice__(*args) from copy import copy _undostate[self.number].append(copy(_buf_lines[self.number])) @@ -467,7 +497,23 @@ class _Buffer(object): return '' def __del__(self): + global _abuf bufnr = self.number + try: + import __main__ + except ImportError: + pass + except RuntimeError: + # Module may have already been garbage-collected + pass + else: + if _on_wipeout: + _abuf = bufnr + try: + for event in _on_wipeout: + exec(event, __main__.__dict__) + finally: + _abuf = None if _buf_lines: _buf_lines.pop(bufnr) if _undostate: From a122e73b9b64a16a929330bd21cf1bcf9e206546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 31 Jul 2013 22:15:35 +0200 Subject: [PATCH 1094/1472] Add files from @kovidgoyal's powerline-daemon repo Minor changes have been applied: - Removed copyright info and GPL 3 license since Powerline is MIT (needs confirmation from kovidgoyal before merge) - The `powerline-client` script is renamed to `powerline`, and calls the daemon or `powerline-render` (the previous `powerline`) to render a statusline - Minor coding style corrections to match the rest of the project - Python 3 support is removed for now due to setuptools failing with binary scripts Todo: - Automatically attempt to launch powerline-daemon the first time powerline is run if the daemon isn't already running? - pip install -e fails with binary files (it appears that pip recodes the powerline binary to ASCII, the compiled powerline script must be copied manually to ~/.local/bin after pip install -e has been run) --- client/powerline | Bin 0 -> 10732 bytes client/powerline.c | 115 +++++++ client/powerline.py | 77 +++++ scripts/.gitignore | 1 + scripts/powerline-daemon | 395 ++++++++++++++++++++++++ scripts/{powerline => powerline-render} | 0 setup.py | 36 ++- 7 files changed, 620 insertions(+), 4 deletions(-) create mode 100755 client/powerline create mode 100644 client/powerline.c create mode 100755 client/powerline.py create mode 100644 scripts/.gitignore create mode 100755 scripts/powerline-daemon rename scripts/{powerline => powerline-render} (100%) diff --git a/client/powerline b/client/powerline new file mode 100755 index 0000000000000000000000000000000000000000..fd6de02466fadefde0a29997f5b142205dd85172 GIT binary patch literal 10732 zcmcIq4RBM}mA;Z}aDYgHLgEGz)KpN&j1?OK?f0Igne5K&_4u88zI)EO=l-4h-qn4fIl9K_a4?yi>`xeR_3j{tj3;9~_i$EV zjjV0 zgW!k{5@BKfoTR)jfNi*#Pf-4d*AO`34f2enmGqv19{KrYll1f9RC3$OM;=Zk>Qc#c zcTZhUv~SnG zQn&ch>$U$vWh5i}zaIG!7UYC}-z^3i!+l z_z!@8jeV6h&TB`GA&wa7halzfL%?U3k!P5cd(AEMp9aOS27E;s=B>1lNRzS zTN{lutyY%>8XlR*E(@$c>zoz-XkFRMXdOBES`y)befO`whh^_#zknsey6;QgMK(_$ zPpNBeJEs&I?&dk;A^V?Vsa%ItGAQJyBQHW5!R(L;7a@(~LnfTox=cSe;S?uiI&8wt zaeUZ>ldm$3m~iq*rngPFoI_MLX2Q`C6X~1@pD}?lcHV^3oRz6y!e>bk^qL7b_vg3? zcbnvynCIlX$As6Ia5^0%DP4cxS+`JBbCj2WVd8O~88s$Lsz$_nA`bYBBh5tgK z()VV~s7VOf(u(IGQd;ypo}Pt4p6cP@ib&bALr)~e>d@7&d(s8hN#Fm?JYHM@DNN97Yt-0Z9 zR4;^D+cxR9l@~v%y_d1R+m7f!-tW_&_s>!WUh|JZI;!9LRC(3&dbJz<(xHd_UTAfM z_36sM3BQ-HW6TK&MgQa%ZiE{d(a?aOwDRIHR9>W0_&8VWjYVpbcl=AlD54 zR>Hr&@sd93+2@63bl`b^JE}NF-PCZlMZXc%OR#&4G&=OCzx@mi5VBjULI>Jdr0;aq zz+aZ`+_`h>?AOK?^&;gz)PEfrc)=CbZ=SmBeWkY-(>)BN%^CHCFSC(I-}|lzreDia z*uGr&P7p?@T#zqNM@ zIvv&w_BAp^ukw5LEk={6ZN|Jg zLDR;lU3u}eh9RZqxeGgG{X5O|_X9Px>q(>jP~R8Tdw*%_#Wci>UVI8Q(R#1ge=b>% z%UsX>)_OjK%m2&%d-b;HzgM94ZuH9`u@4}hkxV)uiSc98F;Exs6!b_3iC)A%g%co04dD{ z<>Nb3`u95&C<90MdOJ>gE2Q>HH_!3ua||MV<81G}Qcj7F84K*jXQk4WQ{5+_vkE$J z=fyW^wg36n&@-W}p=U$tD2f)M=s?szYxoSW>o01e`lT2DNQ`IwseuipkbVY>f0lCU zf~#-bF-$>G@vI*!d;@xs_!+e9WGmXNoY}|2hRDB3A%qgQUwjoqc5*+&3LD_mFnX31 z)0ls#|HlAM=BVf*<*2A|n{r{o-krccL=NI!YRT1z{_e91)>Gf_uWe|JoEhq#mqofqqNm^dka zYTN~RMgO&O>hcqcV@!E>yc^2L4dt^?R`jp7DBiF9*^Fk){y>r z+a}sw`_Jxe?idW~HzM)(!%MG59k-SE)hn}Q{23kR^SpbdqF+|@{|xIN7S03Wll%fj zp!>zb`4#QTrg>qkf07^iBZB0XkX{OHj_5Z-Z4v$7Laot(MPp7Mx@e$|=%(n>iw(b3 zPJQ7bp9?f|Do(a-e0 z$=`FAEcJE7k|{00x-z@9EWQ@Cx@1?pZc#$KfR_`>?p)s2mCfu*CNy6x?@MVh0GTwH?Dp79s&6o@F|3gcR)V^O#y!f!+HdizDX%Lp4-SAJzmHBS<`C<9n-ypFTj5- z^dBMtk;=<6=0`cn58o`6g0z6#-Zk#pb)K2KYkJue_kHuB<^BiB1nGzIKL&sABOv6a zcMKNAKlynG_4R_XCb#!xXS2KZC0DcCH&E5=UUINH?5_XLw2-^8Z+h4rbhgZJH-_Bx zA@`Cdx39@v3ymgs&06E_mF&HS|NW?EtV-HjNA@5+P^FMPXbXE0_gKL7v8$}b5LqOj zKgWL${G)H!vi>P#Pa*$W+`&{i?~L}Ne}-U(Lasesa|byzCa5f@E~O;tEQ4j|rU!YB z-dHHjMHHg+RsN)No_3y_FL;Wblz88R4vKObJ4&+r#+_1z_;zOy&&ji<;VOftjfT>o zGf0HZ;7`Wh7StF8%pbwe6I2qsaPZV4z87R)ye%3i=NZjyO4u`a@=gnKD)IBx1~2`t z=cSCvZys4craf=ZVIhy{&w1IO*96}y%7vN#cOuWv>=oTp6XeihL01a8M$pZIb_lvl z(C-NPJwbme=utu667)SmF9~{6&}rDWDBUCIVnJ65$_q`8wX0XJ@-4xs*HYgjfyO|+ zZ&`iA^7<9^%X~|^vRYQ7_@ymnHn9fA0=Ye%`PeqleAb{lWDchYTDFS?(wV##2sK6O z^0DoL+n(+YZ0kj?kHrA=BI}*^9oS~t{vf0=kLqq1j6^9tC$2ya7 zRGooAJOaEH!{WAFjs@bGP73Cxp8UUMX{%Z7x$?0=uwe|K_sM@^_E06cp56|52O%xX>RL^&hnLpS}JO=)3VR_hb3KAb%gS%iH;Ppi75FQeVDL z49)?FWS6(|=O9BaN`3kQp>*g=>c0!UdIhLiU%t=u2>ruCPZFso>2;g_5`6YiS|VPUq@NtbL+VNXGl9wW(`O^4 z#xJQ~f-L!8PfDgF^&3qXWl5Hjq_dHib@Kx8=o0nw^D9$<)RPeLa_&ov==l8xw?s6U zSn^S}U&e1*pXKWJEjIMuu@&0uH>AoxUDQfva7rQl9wGNhbD;~K$o9f3e~a{U?m{?zfJcuDr< zcQ9Cc3gvcKsjxj0q@+V`z z;<#W; z=G0hs5AJYucBQnumI$m|zT#0>E+eGUnT2rzfqNJE>>gagpfig^cW37wsF*mF8e$Pu z)ioPKEzN54`Y@a|JePTkaDu7fKU*JaiL8c{sf1qzVd~oGlTD$h`sA85o0?nI)=*Ql znauIqBJo@|_mN)?2?pgQ5W4wczlUNi6*pI`pc-jyQ73z~wPiK+X=`j-N@H}!Vq7+v z7$1J^#cF|%n-x+uemrBiY21;q3K(}{teCv?VnvPnGFFUVp0Pr7;l_$gxm#l|Hnq-n zuSQ(AnWB)(s5@fm1bW7}+A}GD8#rp@N%U1BnO3`V_(j7sSTwv0a|xgrUKQt7BF}IA z7*~uY&GE5Zw@ck9Cid!7ESJMXH|`MGaO$E7@EE9px LQ1P3LRsVkg?!G}> literal 0 HcmV?d00001 diff --git a/client/powerline.c b/client/powerline.c new file mode 100644 index 00000000..cef562ec --- /dev/null +++ b/client/powerline.c @@ -0,0 +1,115 @@ +/* vim:fileencoding=utf-8:noet */ + +#include +#include +#include +#include +#include +#include +#include + +#define handle_error(msg) \ + do { perror(msg); exit(EXIT_FAILURE); } while (0) + +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) \ + ( \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + +extern char **environ; + +void do_write(int sd, const char *raw, int len) { + int written = 0, n = -1; + + while (written < len) { + n = TEMP_FAILURE_RETRY(write(sd, raw+written, len-written)); + if (n == -1) { + close(sd); + handle_error("write() failed"); + } + written += n; + } +} + +int main(int argc, char *argv[]) { + int sd = -1, i; + struct sockaddr_un server; + char address[50] = {}; + const char eof[2] = "\0\0"; + char buf[4096] = {}; + char *newargv[200] = {}; + char *wd = NULL; + char **envp; + + if (argc < 2) { printf("Must provide at least one argument.\n"); return EXIT_FAILURE; } + +#ifdef __APPLE__ + snprintf(address, 50, "/tmp/powerline-ipc-%d", getuid()); +#else + snprintf(address, 50, "powerline-ipc-%d", getuid()); +#endif + + sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sd == -1) handle_error("socket() failed"); + + memset(&server, 0, sizeof(struct sockaddr_un)); // Clear + server.sun_family = AF_UNIX; +#ifdef __APPLE__ + strncpy(server.sun_path, address, strlen(address)); +#else + strncpy(server.sun_path+1, address, strlen(address)); +#endif + +#ifdef __APPLE__ + if (connect(sd, (struct sockaddr *) &server, sizeof(server.sun_family) + strlen(address)) < 0) { +#else + if (connect(sd, (struct sockaddr *) &server, sizeof(server.sun_family) + strlen(address)+1) < 0) { +#endif + close(sd); + // We failed to connect to the daemon, execute powerline instead + argc = (argc < 199) ? argc : 199; + for (i=1; i < argc; i++) newargv[i] = argv[i]; + newargv[0] = "powerline-render"; + newargv[argc] = NULL; + execvp("powerline-render", newargv); + } + + for (i = 1; i < argc; i++) { + do_write(sd, argv[i], strlen(argv[i])); + do_write(sd, eof, 1); + } + + for(envp=environ; *envp; envp++) { + do_write(sd, "--env=", 6); + do_write(sd, *envp, strlen(*envp)); + do_write(sd, eof, 1); + } + + wd = getcwd(NULL, 0); + if (wd != NULL) { + do_write(sd, "--cwd=", 6); + do_write(sd, wd, strlen(wd)); + free(wd); wd = NULL; + } + + do_write(sd, eof, 2); + + i = -1; + while (i != 0) { + i = TEMP_FAILURE_RETRY(read(sd, buf, 4096)); + if (i == -1) { + close(sd); + handle_error("read() failed"); + } + if (i > 0) + write(STDOUT_FILENO, buf, i) || 0; + } + + close(sd); + + return 0; +} diff --git a/client/powerline.py b/client/powerline.py new file mode 100755 index 00000000..bbcfffbe --- /dev/null +++ b/client/powerline.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import socket +import os +import errno + +if len(sys.argv) < 2: + print('Must provide at least one argument.', file=sys.stderr) + raise SystemExit(1) + +platform = sys.platform.lower() +use_filesystem = 'darwin' in platform +# use_filesystem = True +del platform + +address = ('/tmp/powerline-ipc-%d' if use_filesystem else '\0powerline-ipc-%d')%os.getuid() + +sock = socket.socket(family=socket.AF_UNIX) + + +def eintr_retry_call(func, *args, **kwargs): + while True: + try: + return func(*args, **kwargs) + except EnvironmentError as e: + if getattr(e, 'errno', None) == errno.EINTR: + continue + raise + +try: + eintr_retry_call(sock.connect, address) +except Exception: + # Run the powerline renderer + args = ['powerline-render'] + sys.argv[1:] + os.execvp('powerline-render', args) + +fenc = sys.getfilesystemencoding() or 'utf-8' +if fenc == 'ascii': + fenc = 'utf-8' + +args = [x.encode(fenc) if isinstance(x, type('')) else x for x in sys.argv[1:]] + +try: + cwd = os.getcwd() +except EnvironmentError: + pass +else: + if isinstance(cwd, type('')): + cwd = cwd.encode(fenc) + args.append(b'--cwd=' + cwd) + + +env = (k + '=' + v for k, v in os.environ.items()) +env = (x if isinstance(x, bytes) else x.encode(fenc, 'replace') for x in env) +args.extend((b'--env=' + x for x in env)) + +EOF = b'\0\0' + +for a in args: + eintr_retry_call(sock.sendall, a + EOF[0]) + +eintr_retry_call(sock.sendall, EOF) + +received = [] +while True: + r = sock.recv(4096) + if not r: + break + received.append(r) + +sock.close() + +print (b''.join(received)) diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 00000000..f2ffc12a --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +powerline diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon new file mode 100755 index 00000000..b7627cc4 --- /dev/null +++ b/scripts/powerline-daemon @@ -0,0 +1,395 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import socket +import os +import errno +import sys +from argparse import ArgumentParser +from select import select +from signal import signal, SIGTERM +from time import sleep +from functools import partial +from locale import getpreferredencoding + +from powerline.shell import get_argparser, finish_args, ShellPowerline +from powerline.lib.monotonic import monotonic + +is_daemon = False +platform = sys.platform.lower() +use_filesystem = 'darwin' in platform +# use_filesystem = True +del platform + +if use_filesystem: + address = '/tmp/powerline-ipc-%d' + pidfile = address + '.pid' +else: + # Use the abstract namespace for sockets rather than the filesystem + # (Available only in linux) + address = '\0powerline-ipc-%d' +address = address % os.getuid() + + +class NonInteractiveArgParser(ArgumentParser): + def print_usage(self, file=None): + raise Exception(self.format_usage()) + + def print_help(self, file=None): + raise Exception(self.format_help()) + + def exit(self, status=0, message=None): + pass + + def error(self, message): + raise Exception(self.format_usage()) + + +parser = get_argparser(parser=NonInteractiveArgParser, description='powerline daemon') +parser.add_argument('--cwd', metavar='PATH') +parser.add_argument('--env', action='append') + +EOF = b'EOF\0\0' + +powerlines = {} +logger = None +config_loader = None +home = os.path.expanduser('~') + + +class PowerlineDaemon(ShellPowerline): + def get_log_handler(self): + if not is_daemon: + import logging + return logging.StreamHandler() + return super(PowerlineDaemon, self).get_log_handler() + + +def render(args): + global logger + global config_loader + environ = dict(((k, v) for k, v in (x.partition('=')[0::2] for x in args.env))) + cwd = environ.get('PWD', args.cwd or '/') + segment_info = { + 'getcwd': lambda: cwd, + 'home': environ.get('HOME', home), + 'environ': environ, + 'args': args, + } + key = (args.ext[0], args.renderer_module, + tuple(args.config) if args.config else None, + tuple(args.theme_option) if args.theme_option else None,) + if args.renderer_arg: + segment_info.update(args.renderer_arg) + finish_args(args) + pl = None + try: + pl = powerlines[key] + except KeyError: + try: + pl = powerlines[key] = PowerlineDaemon( + args, + logger=logger, + config_loader=config_loader + ) + except SystemExit: + # Somebody thought raising system exit was a good idea, + return '' + except Exception as e: + if pl: + pl.pl.exception('Failed to render {0}: {1}', str(key), str(e)) + else: + return 'Failed to render {0}: {1}'.format(str(key), str(e)) + if logger is None: + logger = pl.logger + if config_loader is None: + config_loader = pl.config_loader + return pl.render(width=args.width, side=args.side, segment_info=segment_info) + + +def eintr_retry_call(func, *args, **kwargs): + while True: + try: + return func(*args, **kwargs) + except EnvironmentError as e: + if getattr(e, 'errno', None) == errno.EINTR: + continue + raise + + +def do_read(conn, timeout=2.0): + ''' Read data from the client. If the client fails to send data within + timeout seconds, abort. ''' + read = [] + end_time = monotonic() + timeout + while not read or not read[-1].endswith(b'\0\0'): + r, w, e = select((conn,), (), (conn,), timeout) + if e: + return + if monotonic() > end_time: + return + if not r: + continue + x = eintr_retry_call(conn.recv, 4096) + if x: + read.append(x) + else: + break + return b''.join(read) + + +def do_write(conn, result): + try: + eintr_retry_call(conn.sendall, result + b'\0') + except Exception: + pass + + +encoding = getpreferredencoding() or 'UTF-8' +if encoding.lower() == 'ascii': + encoding = 'UTF-8' + + +def safe_bytes(o, encoding=encoding): + '''Return bytes instance without ever throwing an exception.''' + try: + try: + # We are assuming that o is a unicode object + return o.encode(encoding, 'replace') + except Exception: + # Object may have defined __bytes__ (python 3) or __str__ method + # (python 2) + # This also catches problem with non_ascii_bytes.encode('utf-8') + # that first tries to decode to UTF-8 using ascii codec (and fails + # in this case) and then encode to given encoding: errors= argument + # is not used in the first stage. + return bytes(o) + except Exception as e: + return safe_bytes(str(e), encoding) + + +def do_render(req): + try: + args = [x.decode(encoding) for x in req.split(b'\0') if x] + args = parser.parse_args(args) + return safe_bytes(render(args)) + except Exception as e: + return safe_bytes(str(e)) + + +def do_one(sock, read_sockets, write_sockets, result_map): + r, w, e = select( + tuple(read_sockets) + (sock,), + tuple(write_sockets), + tuple(read_sockets) + tuple(write_sockets) + (sock,), + 60.0 + ) + + if sock in e: + # We cannot accept any more connections, so we exit + raise SystemExit(1) + + for s in e: + # Discard all broken connections to clients + s.close() + read_sockets.discard(s) + write_sockets.discard(s) + + for s in r: + if s == sock: + # A client wants to connect + conn, _ = eintr_retry_call(sock.accept) + read_sockets.add(conn) + else: + # A client has sent some data + read_sockets.discard(s) + req = do_read(s) + if req == EOF: + raise SystemExit(0) + elif req: + ans = do_render(req) + result_map[s] = ans + write_sockets.add(s) + else: + s.close() + + for s in w: + # A client is ready to receive the result + write_sockets.discard(s) + result = result_map.pop(s) + try: + do_write(s, result) + finally: + s.close() + + +def main_loop(sock): + sock.listen(1) + sock.setblocking(0) + + read_sockets, write_sockets = set(), set() + result_map = {} + try: + while True: + do_one(sock, read_sockets, write_sockets, result_map) + except KeyboardInterrupt: + raise SystemExit(0) + return 0 + + +def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull): + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError as e: + print ("fork #1 failed: %d (%s)" % (e.errno, e.strerror), file=sys.stderr) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError as e: + print ("fork #2 failed: %d (%s)" % (e.errno, e.strerror), file=sys.stderr) + sys.exit(1) + + # Redirect standard file descriptors. + si = file(stdin, 'r') + so = file(stdout, 'a+') + se = file(stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + global is_daemon + is_daemon = True + + +def check_existing(): + if use_filesystem: + # We cannot bind if the socket file already exists so remove it, we + # already have a lock on pidfile, so this should be safe. + try: + os.unlink(address) + except EnvironmentError: + pass + + sock = socket.socket(family=socket.AF_UNIX) + try: + sock.bind(address) + except socket.error as e: + if getattr(e, 'errno', None) == errno.EADDRINUSE: + return None + raise + return sock + + +def test_connect(): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + try: + eintr_retry_call(sock.connect, address) + except socket.error: + return False + else: + eintr_retry_call(sock.sendall, EOF) + finally: + sock.close() + return True + + +def cleanup_lockfile(fd, *args): + try: + # Remove the directory entry for the lock file + os.unlink(pidfile) + # Close the file descriptor + os.close(fd) + except EnvironmentError: + pass + if args: + # Called in signal handler + raise SystemExit(1) + + +def lockpidfile(): + import fcntl + import atexit + import stat + fd = os.open(pidfile, os.O_WRONLY|os.O_CREAT, + stat.S_IRUSR|stat.S_IWUSR|stat.S_IRGRP|stat.S_IROTH) + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except EnvironmentError: + os.close(fd) + return None + os.lseek(fd, 0, os.SEEK_SET) + os.ftruncate(fd, 0) + os.write(fd, ('%d' % os.getpid()).encode('ascii')) + os.fsync(fd) + cleanup = partial(cleanup_lockfile, fd) + signal(SIGTERM, cleanup) + atexit.register(cleanup) + return fd + + +def main(): + p = ArgumentParser(description= + 'Daemon to improve the performance of powerline') + a = p.add_mutually_exclusive_group().add_argument + a('--kill', '-k', action='store_true', help='Kill an already running instance') + a('--foreground', '-f', action='store_true', help='Run in the foreground (dont daemonize)') + a('--replace', '-r', action='store_true', help='Replace an already running instance') + args = p.parse_args() + + if args.kill: + if test_connect(): + print ('Kill command sent to daemon, if it does not die in a couple of seconds use kill to kill it') + else: + print ('No running daemon found') + return + + if args.replace: + while test_connect(): + print ('Kill command sent to daemon, waiting for daemon to exit, press Ctrl-C to terminate wait and exit') + sleep(2) + + if use_filesystem and not args.foreground: + # We must daemonize before creating the locked pidfile, unfortunately, + # this means further print statements are discarded + daemonize() + + if use_filesystem: + # Create a locked pid file containing the daemon's PID + if lockpidfile() is None: + print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), + file=sys.stderr) + raise SystemExit(1) + + # Bind to address or bail if we cannot bind + sock = check_existing() + if sock is None: + print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), + file=sys.stderr) + raise SystemExit(1) + + if args.foreground: + return main_loop(sock) + + if not use_filesystem: + # We daemonize on linux + daemonize() + + main_loop(sock) + + +if __name__ == '__main__': + main() diff --git a/scripts/powerline b/scripts/powerline-render similarity index 100% rename from scripts/powerline rename to scripts/powerline-render diff --git a/setup.py b/setup.py index e341ceec..770a927e 100755 --- a/setup.py +++ b/setup.py @@ -3,16 +3,42 @@ from __future__ import unicode_literals import os import sys +import subprocess from setuptools import setup, find_packages -here = os.path.abspath(os.path.dirname(__file__)) +CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) try: - README = open(os.path.join(here, 'README.rst'), 'rb').read().decode('utf-8') + README = open(os.path.join(CURRENT_DIR, 'README.rst'), 'rb').read().decode('utf-8') except IOError: README = '' -old_python = sys.version_info < (2, 7) +OLD_PYTHON = sys.version_info < (2, 7) + + +def compile_client(): + '''Compile the C powerline-client script.''' + + if hasattr(sys, 'getwindowsversion'): + raise NotImplementedError() + if sys.version_info >= (3, 0): + # FIXME Python 3 doesn't allow compiled C files to be included in the + # scripts list below. This is because Python 3 distutils tries to + # decode the file to ASCII, and fails when powerline-client is + # a binary. + raise NotImplementedError() + else: + from distutils.ccompiler import new_compiler + compiler = new_compiler().compiler + subprocess.check_call(compiler + ['-O3', 'client/powerline.c', '-o', 'scripts/powerline']) + +try: + compile_client() +except Exception: + # FIXME Catch more specific exceptions + import shutil + print('Compiling C version of powerline-client failed, using Python version instead') + shutil.copyfile('client/powerline.py', 'scripts/powerline') setup( name='Powerline', @@ -26,6 +52,8 @@ setup( scripts=[ 'scripts/powerline', 'scripts/powerline-lint', + 'scripts/powerline-daemon', + 'scripts/powerline-render', 'scripts/powerline-config', ], keywords='', @@ -38,5 +66,5 @@ setup( 'Sphinx', ], }, - test_suite='tests' if not old_python else None, + test_suite='tests' if not OLD_PYTHON else None, ) From c1d290b5708537faaff8239cb7d51a19eeb21f83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Nov 2013 23:30:35 +0400 Subject: [PATCH 1095/1472] Use data_files on python-3 --- setup.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 770a927e..6dd474bd 100755 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ except IOError: README = '' OLD_PYTHON = sys.version_info < (2, 7) +PYTHON_3 = sys.version_info > (2,) def compile_client(): @@ -21,12 +22,6 @@ def compile_client(): if hasattr(sys, 'getwindowsversion'): raise NotImplementedError() - if sys.version_info >= (3, 0): - # FIXME Python 3 doesn't allow compiled C files to be included in the - # scripts list below. This is because Python 3 distutils tries to - # decode the file to ASCII, and fails when powerline-client is - # a binary. - raise NotImplementedError() else: from distutils.ccompiler import new_compiler compiler = new_compiler().compiler @@ -49,13 +44,16 @@ setup( author='Kim Silkebaekken', author_email='kim.silkebaekken+vim@gmail.com', url='https://github.com/Lokaltog/powerline', + # FIXME Python 3 doesn't allow compiled C files to be included in the + # scripts list below. This is because Python 3 distutils tries to decode the + # file to ASCII, and fails when powerline-client is a binary. scripts=[ - 'scripts/powerline', 'scripts/powerline-lint', 'scripts/powerline-daemon', 'scripts/powerline-render', 'scripts/powerline-config', - ], + ] + ([] if PYTHON_3 else ['scripts/powerline']), + data_files=([('bin', ['scripts/powerline'])] if PYTHON_3 else None), keywords='', packages=find_packages(exclude=('tests', 'tests.*')), include_package_data=True, From 75536825ba9077df12c21f2adb50d412dc984761 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Nov 2013 23:34:15 +0400 Subject: [PATCH 1096/1472] Use powerline/powerline-render pair in place of powerline-client/powerline --- powerline/bindings/bash/powerline.sh | 9 +++++---- powerline/bindings/fish/powerline-setup.fish | 6 +++--- powerline/bindings/tcsh/powerline.tcsh | 6 +++--- powerline/bindings/tmux/powerline.conf | 2 +- powerline/bindings/zsh/powerline.zsh | 6 +++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index dabacfa9..8e3782d6 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -54,13 +54,14 @@ _powerline_set_prompt() { _powerline_setup_prompt() { VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-client &>/dev/null ; then - export POWERLINE_COMMAND=powerline-client - elif which powerline &>/dev/null ; then + if which powerline &>/dev/null ; then export POWERLINE_COMMAND=powerline + elif which powerline-render &>/dev/null ; then + export POWERLINE_COMMAND=powerline-render else # `$0` is set to `-bash` when using SSH so that won't work - export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" + local powerline_dir="$(dirname "$BASH_SOURCE")/../../.." + export POWERLINE_COMMAND="$powerline_dir/scripts/powerline" fi fi test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 35ec5763..32f50131 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -20,10 +20,10 @@ function powerline-setup if test -z "$POWERLINE_NO_FISH_PROMPT$POWERLINE_NO_SHELL_PROMPT" if test -z "$POWERLINE_COMMAND" - if which powerline-client >/dev/null - set -g -x POWERLINE_COMMAND powerline-client - else if which powerline >/dev/null + if which powerline >/dev/null set -g -x POWERLINE_COMMAND powerline + else if which powerline-render >/dev/null + set -g -x POWERLINE_COMMAND powerline-render else set -g -x POWERLINE_COMMAND (dirname (status -f))/../../../scripts/powerline end diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 931b9ab0..1ff61cb2 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -11,10 +11,10 @@ if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) t endif if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then if ! $?POWERLINE_COMMAND then - if ( { which powerline-client > /dev/null } ) then - setenv POWERLINE_COMMAND powerline-client - else if ( { which powerline > /dev/null } ) then + if ( { which powerline > /dev/null } ) then setenv POWERLINE_COMMAND powerline + else if ( { which powerline-render > /dev/null } ) then + setenv POWERLINE_COMMAND powerline-render else setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline endif diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index a55b4856..31f7be40 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,4 +1,4 @@ -if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline-client" "set-environment -g POWERLINE_COMMAND powerline-client" "set-environment -g POWERLINE_COMMAND powerline"' +if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline" "set-environment -g POWERLINE_COMMAND powerline" "set-environment -g POWERLINE_COMMAND powerline-render"' if-shell 'test -z "$POWERLINE_CONFIG_COMMAND"' 'set-environment -g POWERLINE_CONFIG_COMMAND powerline-config' # Don't version-check for this core functionality -- anything too old to diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 096d0ff4..a9717749 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -124,10 +124,10 @@ _powerline_setup_prompt() { zpython 'del _powerline_setup' else if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-client &>/dev/null ; then - export POWERLINE_COMMAND=powerline-client - elif which powerline &>/dev/null ; then + if which powerline &>/dev/null ; then export POWERLINE_COMMAND=powerline + elif which powerline-render &>/dev/null ; then + export POWERLINE_COMMAND=powerline-render else export POWERLINE_COMMAND="$0:A:h:h:h:h/scripts/powerline" fi From 21ee641a0288eaaffbf18db86e0153f668e3f85f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Nov 2013 20:23:56 +0400 Subject: [PATCH 1097/1472] Do not use scripts=[] to install powerline at all. --- setup.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 6dd474bd..9e7013ae 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,6 @@ except IOError: README = '' OLD_PYTHON = sys.version_info < (2, 7) -PYTHON_3 = sys.version_info > (2,) def compile_client(): @@ -44,16 +43,31 @@ setup( author='Kim Silkebaekken', author_email='kim.silkebaekken+vim@gmail.com', url='https://github.com/Lokaltog/powerline', - # FIXME Python 3 doesn't allow compiled C files to be included in the - # scripts list below. This is because Python 3 distutils tries to decode the - # file to ASCII, and fails when powerline-client is a binary. + # XXX Python 3 doesn't allow compiled C files to be included in the scripts + # list below. This is because Python 3 distutils tries to decode the file to + # ASCII, and fails when powerline-client is a binary. + # + # XXX Python 2 fucks up script contents*. Not using it to install scripts + # any longer. + # * Consider the following input: + # % alias hex1=$'hexdump -e \'"" 1/1 "%02X\n"\'' + # % diff <(hex1 ./scripts/powerline) <(hex1 ~/.local/bin/powerline) + # This will show output like + # 375c375 + # < 0D + # --- + # > 0A + # (repeated, with diff segment header numbers growing up). + # + # FIXME Current solution does not work with `pip install -e`. Still better + # then solution that is not working at all. scripts=[ 'scripts/powerline-lint', 'scripts/powerline-daemon', 'scripts/powerline-render', 'scripts/powerline-config', - ] + ([] if PYTHON_3 else ['scripts/powerline']), - data_files=([('bin', ['scripts/powerline'])] if PYTHON_3 else None), + ], + data_files=[('bin', ['scripts/powerline'])], keywords='', packages=find_packages(exclude=('tests', 'tests.*')), include_package_data=True, From a27f90df62beb25289cbd2b989b7734eb6fc304e Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Nov 2013 20:31:04 +0400 Subject: [PATCH 1098/1472] Add a note about not fully working --editable to linux.rst --- docs/source/installation/linux.rst | 5 +++++ docs/source/installation/osx.rst | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index 74e6f74f..e8ff6d72 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -30,6 +30,11 @@ Plugin installation project is currently unavailable on the PyPI due to a naming conflict with an unrelated project. +.. note:: If you are powerline developer you should be aware that ``pip install + --editable`` does not currently fully work. If you + install powerline this way you will be missing ``powerline`` executable and + need to symlink it. It will be located in ``scripts/powerline``. + Font installation ================= diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index aa029f11..c991eac7 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -26,6 +26,11 @@ Python package project is currently unavailable on the PyPI due to a naming conflict with an unrelated project. +.. note:: If you are powerline developer you should be aware that ``pip install + --editable`` does not currently fully work. If you + install powerline this way you will be missing ``powerline`` executable and + need to symlink it. It will be located in ``scripts/powerline``. + Vim installation ---------------- From 7db428667cb0fdc890c5f9c62ad2e29b97284d3c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Nov 2013 13:20:05 +0400 Subject: [PATCH 1099/1472] Get rid of modeline E518 error --- client/powerline.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/powerline.c b/client/powerline.c index cef562ec..a60743a7 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -1,4 +1,5 @@ -/* vim:fileencoding=utf-8:noet */ +/* vim:fileencoding=utf-8:noet + */ #include #include From e613beb8df449f18d7d066ff98164674b39c8485 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Nov 2013 13:54:23 +0400 Subject: [PATCH 1100/1472] Add shell version of powerline-client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: this shell script is still faster then client/powerline.py. `time` reports 0.00, 0.01, 0.02÷0.03 for powerline, powerline.sh and powerline.py respectively (run with `./client/powerline* tmux right`). Note: does not work in busybox as env there does not support -0 argument. Requires sed, id (for dash as $UID is not available there), env, printf and socat. Is not currently handled by setup.py. --- client/powerline.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 client/powerline.sh diff --git a/client/powerline.sh b/client/powerline.sh new file mode 100755 index 00000000..d1657738 --- /dev/null +++ b/client/powerline.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +ADDRESS="powerline-ipc-${UID:-`id -u`}" + +# Warning: env -0 does not work in busybox. Consider switching to parsing +# `set` output in this case +( + for argv in "$@" ; do + printf '%s\0' "$argv" + done + env -0 | sed 's/\(\x00\)\([^\x00]\)\|^/\1--env=\2/g' + printf -- '--cwd=%s\0' "$PWD" +) | socat -t 10 - abstract-client:"$ADDRESS" From e37b13cc4411b5fe63404b029676df79814cff1c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 18:56:44 +0400 Subject: [PATCH 1101/1472] Remove compiled binary from repository --- .gitignore | 2 ++ client/powerline | Bin 10732 -> 0 bytes 2 files changed, 2 insertions(+) delete mode 100755 client/powerline diff --git a/.gitignore b/.gitignore index 7468a1a5..dbae1ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ dist build message.fail + +client/powerline diff --git a/client/powerline b/client/powerline deleted file mode 100755 index fd6de02466fadefde0a29997f5b142205dd85172..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10732 zcmcIq4RBM}mA;Z}aDYgHLgEGz)KpN&j1?OK?f0Igne5K&_4u88zI)EO=l-4h-qn4fIl9K_a4?yi>`xeR_3j{tj3;9~_i$EV zjjV0 zgW!k{5@BKfoTR)jfNi*#Pf-4d*AO`34f2enmGqv19{KrYll1f9RC3$OM;=Zk>Qc#c zcTZhUv~SnG zQn&ch>$U$vWh5i}zaIG!7UYC}-z^3i!+l z_z!@8jeV6h&TB`GA&wa7halzfL%?U3k!P5cd(AEMp9aOS27E;s=B>1lNRzS zTN{lutyY%>8XlR*E(@$c>zoz-XkFRMXdOBES`y)befO`whh^_#zknsey6;QgMK(_$ zPpNBeJEs&I?&dk;A^V?Vsa%ItGAQJyBQHW5!R(L;7a@(~LnfTox=cSe;S?uiI&8wt zaeUZ>ldm$3m~iq*rngPFoI_MLX2Q`C6X~1@pD}?lcHV^3oRz6y!e>bk^qL7b_vg3? zcbnvynCIlX$As6Ia5^0%DP4cxS+`JBbCj2WVd8O~88s$Lsz$_nA`bYBBh5tgK z()VV~s7VOf(u(IGQd;ypo}Pt4p6cP@ib&bALr)~e>d@7&d(s8hN#Fm?JYHM@DNN97Yt-0Z9 zR4;^D+cxR9l@~v%y_d1R+m7f!-tW_&_s>!WUh|JZI;!9LRC(3&dbJz<(xHd_UTAfM z_36sM3BQ-HW6TK&MgQa%ZiE{d(a?aOwDRIHR9>W0_&8VWjYVpbcl=AlD54 zR>Hr&@sd93+2@63bl`b^JE}NF-PCZlMZXc%OR#&4G&=OCzx@mi5VBjULI>Jdr0;aq zz+aZ`+_`h>?AOK?^&;gz)PEfrc)=CbZ=SmBeWkY-(>)BN%^CHCFSC(I-}|lzreDia z*uGr&P7p?@T#zqNM@ zIvv&w_BAp^ukw5LEk={6ZN|Jg zLDR;lU3u}eh9RZqxeGgG{X5O|_X9Px>q(>jP~R8Tdw*%_#Wci>UVI8Q(R#1ge=b>% z%UsX>)_OjK%m2&%d-b;HzgM94ZuH9`u@4}hkxV)uiSc98F;Exs6!b_3iC)A%g%co04dD{ z<>Nb3`u95&C<90MdOJ>gE2Q>HH_!3ua||MV<81G}Qcj7F84K*jXQk4WQ{5+_vkE$J z=fyW^wg36n&@-W}p=U$tD2f)M=s?szYxoSW>o01e`lT2DNQ`IwseuipkbVY>f0lCU zf~#-bF-$>G@vI*!d;@xs_!+e9WGmXNoY}|2hRDB3A%qgQUwjoqc5*+&3LD_mFnX31 z)0ls#|HlAM=BVf*<*2A|n{r{o-krccL=NI!YRT1z{_e91)>Gf_uWe|JoEhq#mqofqqNm^dka zYTN~RMgO&O>hcqcV@!E>yc^2L4dt^?R`jp7DBiF9*^Fk){y>r z+a}sw`_Jxe?idW~HzM)(!%MG59k-SE)hn}Q{23kR^SpbdqF+|@{|xIN7S03Wll%fj zp!>zb`4#QTrg>qkf07^iBZB0XkX{OHj_5Z-Z4v$7Laot(MPp7Mx@e$|=%(n>iw(b3 zPJQ7bp9?f|Do(a-e0 z$=`FAEcJE7k|{00x-z@9EWQ@Cx@1?pZc#$KfR_`>?p)s2mCfu*CNy6x?@MVh0GTwH?Dp79s&6o@F|3gcR)V^O#y!f!+HdizDX%Lp4-SAJzmHBS<`C<9n-ypFTj5- z^dBMtk;=<6=0`cn58o`6g0z6#-Zk#pb)K2KYkJue_kHuB<^BiB1nGzIKL&sABOv6a zcMKNAKlynG_4R_XCb#!xXS2KZC0DcCH&E5=UUINH?5_XLw2-^8Z+h4rbhgZJH-_Bx zA@`Cdx39@v3ymgs&06E_mF&HS|NW?EtV-HjNA@5+P^FMPXbXE0_gKL7v8$}b5LqOj zKgWL${G)H!vi>P#Pa*$W+`&{i?~L}Ne}-U(Lasesa|byzCa5f@E~O;tEQ4j|rU!YB z-dHHjMHHg+RsN)No_3y_FL;Wblz88R4vKObJ4&+r#+_1z_;zOy&&ji<;VOftjfT>o zGf0HZ;7`Wh7StF8%pbwe6I2qsaPZV4z87R)ye%3i=NZjyO4u`a@=gnKD)IBx1~2`t z=cSCvZys4craf=ZVIhy{&w1IO*96}y%7vN#cOuWv>=oTp6XeihL01a8M$pZIb_lvl z(C-NPJwbme=utu667)SmF9~{6&}rDWDBUCIVnJ65$_q`8wX0XJ@-4xs*HYgjfyO|+ zZ&`iA^7<9^%X~|^vRYQ7_@ymnHn9fA0=Ye%`PeqleAb{lWDchYTDFS?(wV##2sK6O z^0DoL+n(+YZ0kj?kHrA=BI}*^9oS~t{vf0=kLqq1j6^9tC$2ya7 zRGooAJOaEH!{WAFjs@bGP73Cxp8UUMX{%Z7x$?0=uwe|K_sM@^_E06cp56|52O%xX>RL^&hnLpS}JO=)3VR_hb3KAb%gS%iH;Ppi75FQeVDL z49)?FWS6(|=O9BaN`3kQp>*g=>c0!UdIhLiU%t=u2>ruCPZFso>2;g_5`6YiS|VPUq@NtbL+VNXGl9wW(`O^4 z#xJQ~f-L!8PfDgF^&3qXWl5Hjq_dHib@Kx8=o0nw^D9$<)RPeLa_&ov==l8xw?s6U zSn^S}U&e1*pXKWJEjIMuu@&0uH>AoxUDQfva7rQl9wGNhbD;~K$o9f3e~a{U?m{?zfJcuDr< zcQ9Cc3gvcKsjxj0q@+V`z z;<#W; z=G0hs5AJYucBQnumI$m|zT#0>E+eGUnT2rzfqNJE>>gagpfig^cW37wsF*mF8e$Pu z)ioPKEzN54`Y@a|JePTkaDu7fKU*JaiL8c{sf1qzVd~oGlTD$h`sA85o0?nI)=*Ql znauIqBJo@|_mN)?2?pgQ5W4wczlUNi6*pI`pc-jyQ73z~wPiK+X=`j-N@H}!Vq7+v z7$1J^#cF|%n-x+uemrBiY21;q3K(}{teCv?VnvPnGFFUVp0Pr7;l_$gxm#l|Hnq-n zuSQ(AnWB)(s5@fm1bW7}+A}GD8#rp@N%U1BnO3`V_(j7sSTwv0a|xgrUKQt7BF}IA z7*~uY&GE5Zw@ck9Cid!7ESJMXH|`MGaO$E7@EE9px LQ1P3LRsVkg?!G}> From 3b060562e29264f0b51a6a1325a724ef980105e0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 19:46:04 +0400 Subject: [PATCH 1102/1472] Fix UnicodeDecodeError in Python version of powerline client --- client/powerline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/powerline.py b/client/powerline.py index bbcfffbe..a00c38cd 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -54,7 +54,7 @@ else: args.append(b'--cwd=' + cwd) -env = (k + '=' + v for k, v in os.environ.items()) +env = (k + b'=' + v for k, v in os.environ.items()) env = (x if isinstance(x, bytes) else x.encode(fenc, 'replace') for x in env) args.extend((b'--env=' + x for x in env)) From 95dcef8bf8b42bbd5118bccf37bb14f3e88f0cd1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 19:47:03 +0400 Subject: [PATCH 1103/1472] Some style fixes --- client/powerline.py | 2 +- scripts/powerline-daemon | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/powerline.py b/client/powerline.py index a00c38cd..89e56f8e 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -17,7 +17,7 @@ use_filesystem = 'darwin' in platform # use_filesystem = True del platform -address = ('/tmp/powerline-ipc-%d' if use_filesystem else '\0powerline-ipc-%d')%os.getuid() +address = ('/tmp/powerline-ipc-%d' if use_filesystem else '\0powerline-ipc-%d') % os.getuid() sock = socket.socket(family=socket.AF_UNIX) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index b7627cc4..e110625f 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -324,8 +324,8 @@ def lockpidfile(): import fcntl import atexit import stat - fd = os.open(pidfile, os.O_WRONLY|os.O_CREAT, - stat.S_IRUSR|stat.S_IWUSR|stat.S_IRGRP|stat.S_IROTH) + fd = os.open(pidfile, os.O_WRONLY | os.O_CREAT, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) try: fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except EnvironmentError: From 925d3eb0e44e41880eb5c66c0d58094e4b8d6c6e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 19:48:07 +0400 Subject: [PATCH 1104/1472] Show exception in setup.py and try to fall back to powerline.sh --- setup.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 9e7013ae..16561d85 100755 --- a/setup.py +++ b/setup.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import os import sys import subprocess +import logging from setuptools import setup, find_packages @@ -28,11 +29,26 @@ def compile_client(): try: compile_client() -except Exception: +except Exception as e: + print('Compiling C version of powerline-client failed') + logging.exception(e) # FIXME Catch more specific exceptions import shutil - print('Compiling C version of powerline-client failed, using Python version instead') - shutil.copyfile('client/powerline.py', 'scripts/powerline') + if hasattr(shutil, 'which'): + which = shutil.which + else: + sys.path.append(CURRENT_DIR) + from powerline.lib import which + if which('socat') and which('sed') and which('sh'): + print('Using powerline.sh script instead of C version (requires socat, sed and sh)') + shutil.copyfile('client/powerline.sh', 'scripts/powerline') + can_use_scripts = True + else: + print('Using powerline.py script instead of C version') + shutil.copyfile('client/powerline.py', 'scripts/powerline') + can_use_scripts = True +else: + can_use_scripts = False setup( name='Powerline', @@ -66,8 +82,8 @@ setup( 'scripts/powerline-daemon', 'scripts/powerline-render', 'scripts/powerline-config', - ], - data_files=[('bin', ['scripts/powerline'])], + ] + (['scripts/powerline'] if can_use_scripts else []), + data_files=(None if can_use_scripts else (('bin', ['scripts/powerline']),)), keywords='', packages=find_packages(exclude=('tests', 'tests.*')), include_package_data=True, From 05384e31e446bb36d0ce914bf7d8682e98310afc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 20:07:55 +0400 Subject: [PATCH 1105/1472] Add `powerline-config shell command` and use it in all shell bindings --- powerline/bindings/bash/powerline.sh | 8 +-- powerline/bindings/config.py | 37 ++++++++++- powerline/bindings/fish/powerline-setup.fish | 9 ++- powerline/bindings/shell/powerline.sh | 11 ++-- powerline/bindings/tcsh/powerline.tcsh | 8 +-- powerline/bindings/zsh/powerline.zsh | 11 ++-- powerline/config.py | 3 +- powerline/lib/shell.py | 69 ++++++++++++++++++++ scripts/powerline-config | 13 ++++ 9 files changed, 140 insertions(+), 29 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 8e3782d6..501b48aa 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -54,14 +54,12 @@ _powerline_set_prompt() { _powerline_setup_prompt() { VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_COMMAND}" ; then - if which powerline &>/dev/null ; then - export POWERLINE_COMMAND=powerline - elif which powerline-render &>/dev/null ; then - export POWERLINE_COMMAND=powerline-render + if which powerline-config &>/dev/null ; then + export POWERLINE_COMMAND="$(powerline-config shell command)" else # `$0` is set to `-bash` when using SSH so that won't work local powerline_dir="$(dirname "$BASH_SOURCE")/../../.." - export POWERLINE_COMMAND="$powerline_dir/scripts/powerline" + export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" fi fi test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 3591903a..846c66f0 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -6,11 +6,12 @@ from collections import namedtuple import os import subprocess import re +import sys -from powerline.config import TMUX_CONFIG_DIRECTORY +from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY from powerline.lib.config import ConfigLoader from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config -from powerline.lib.shell import run_cmd +from powerline.lib.shell import run_cmd, which TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) @@ -119,3 +120,35 @@ def create_powerline_logger(args): common_config = finish_common_config(config['common']) logger = create_logger(common_config) return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config') + + +def check_command(cmd): + if which(cmd): + print(cmd) + sys.exit(0) + + +def shell_command(pl, args): + '''Deduce which command to use for ``powerline`` + + Candidates: + + * ``powerline``. Present only when installed system-wide. + * ``{powerline_root}/scripts/powerline``. Present after ``pip install -e`` + was run and C client was compiled (in this case ``pip`` does not install + binary file). + * ``{powerline_root}/client/powerline.sh``. Useful when ``sh``, ``sed`` and + ``socat`` are present, but ``pip`` or ``setup.py`` was not run. + * ``{powerline_root}/client/powerline.py``. Like above, but when one of + ``sh``, ``sed`` and ``socat`` was not present. + * ``powerline-render``. Should not really ever be used. + * ``{powerline_root}/scripts/powerline-render``. Same. + ''' + check_command('powerline') + check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline')) + if which('sh') and which('sed') and which('socat'): + check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.sh')) + check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.py')) + check_command('powerline-render') + check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline-render')) + sys.exit(1) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 32f50131..89fe9778 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -20,12 +20,11 @@ function powerline-setup if test -z "$POWERLINE_NO_FISH_PROMPT$POWERLINE_NO_SHELL_PROMPT" if test -z "$POWERLINE_COMMAND" - if which powerline >/dev/null - set -g -x POWERLINE_COMMAND powerline - else if which powerline-render >/dev/null - set -g -x POWERLINE_COMMAND powerline-render + if false ;and which powerline-config >/dev/null + set -g -x POWERLINE_COMMAND (powerline-config shell command) else - set -g -x POWERLINE_COMMAND (dirname (status -f))/../../../scripts/powerline + set -l powerline_dir (dirname (status -f))/../../.. + set -g -x POWERLINE_COMMAND (eval $powerline_dir/scripts/powerline-config shell command) end end function --on-variable POWERLINE_COMMAND _powerline_update diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index 6f45ffde..ae5c035f 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -1,3 +1,4 @@ +_POWERLINE_SOURCED="$_" _powerline_columns_fallback() { if which stty >/dev/null ; then # Ksh does not have “local” built-in @@ -97,13 +98,11 @@ _powerline_set_set_jobs() { _powerline_set_command() { if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-client >/dev/null ; then - export POWERLINE_COMMAND=powerline-client - elif which powerline >/dev/null ; then - export POWERLINE_COMMAND=powerline + if which powerline-config &>/dev/null ; then + export POWERLINE_COMMAND="$(powerline-config shell command)" else - # `$0` is set to `-bash` when using SSH so that won't work - export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" + local powerline_dir="$(dirname "$POWERLINE_SOURCED")/../../.." + export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" fi fi } diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 1ff61cb2..69f34a2e 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -11,12 +11,10 @@ if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) t endif if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then if ! $?POWERLINE_COMMAND then - if ( { which powerline > /dev/null } ) then - setenv POWERLINE_COMMAND powerline - else if ( { which powerline-render > /dev/null } ) then - setenv POWERLINE_COMMAND powerline-render + if ( { which powerline-config > /dev/null } ) then + setenv POWERLINE_COMMAND "`powerline-config shell command`" else - setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline + setenv POWERLINE_COMMAND "`$POWERLINE_SOURCED[2]:h:h:h:h:q/scripts/powerline-config shell command`" endif endif diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index a9717749..5439167a 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,3 +1,5 @@ +_POWERLINE_SOURCED="$0:A" + _powerline_columns_fallback() { if which stty &>/dev/null ; then local cols="$(stty size 2>/dev/null)" @@ -124,12 +126,11 @@ _powerline_setup_prompt() { zpython 'del _powerline_setup' else if test -z "${POWERLINE_COMMAND}" ; then - if which powerline &>/dev/null ; then - export POWERLINE_COMMAND=powerline - elif which powerline-render &>/dev/null ; then - export POWERLINE_COMMAND=powerline-render + if which powerline-config &>/dev/null ; then + export POWERLINE_COMMAND="$(powerline-config shell command)" else - export POWERLINE_COMMAND="$0:A:h:h:h:h/scripts/powerline" + local powerline_dir="$POWERLINE_SOURCED:h:h:h:h" + export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" fi fi diff --git a/powerline/config.py b/powerline/config.py index 53e530f4..c895da5e 100644 --- a/powerline/config.py +++ b/powerline/config.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals, print_function import os -BINDINGS_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'bindings') +POWERLINE_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BINDINGS_DIRECTORY = os.path.join(POWERLINE_ROOT, 'powerline', 'bindings') TMUX_CONFIG_DIRECTORY = os.path.join(BINDINGS_DIRECTORY, 'tmux') DEFAULT_SYSTEM_CONFIG_DIR = None diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index ea777770..4418c3d9 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -6,6 +6,7 @@ from subprocess import Popen, PIPE from locale import getlocale, getdefaultlocale, LC_MESSAGES from functools import partial import sys +import os if sys.platform.startswith('win32'): @@ -60,3 +61,71 @@ def readlines(cmd, cwd): with p.stdout: for line in p.stdout: yield line[:-1].decode(encoding) + + +try: + from shutil import which +except ImportError: + # shutil.which was added in python-3.3. Here is what was added: + # Lib/shutil.py, commit 5abe28a9c8fe701ba19b1db5190863384e96c798 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): # NOQA + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None diff --git a/scripts/powerline-config b/scripts/powerline-config index bcece7e6..2e636437 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -18,6 +18,11 @@ TMUX_ACTIONS = { } +SHELL_ACTIONS = { + 'command': config.shell_command, +} + + if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) subparsers = parser.add_subparsers() @@ -30,6 +35,14 @@ if __name__ == '__main__': help='If action is "source" then version-specific tmux configuration files are sourced.' ) + shell_parser = subparsers.add_parser('shell', help='Shell-specific commands') + shell_parser.add_argument( + 'function', + choices=tuple(SHELL_ACTIONS.values()), + type=(lambda v: SHELL_ACTIONS.get(v)), + help='If action is "command" then preferred powerline command is output', + ) + args = parser.parse_args() pl = config.create_powerline_logger(args) From 8374a66ca7ac9f2b41683aadd59d3d5541ed02fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 20:16:38 +0400 Subject: [PATCH 1106/1472] Also use the same code for tmux --- powerline/bindings/config.py | 35 ++++++++++++++++++-------- powerline/bindings/tmux/powerline.conf | 1 - 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 846c66f0..5899e740 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -111,6 +111,10 @@ def source_tmux_files(pl, args): version = get_tmux_version(pl) for fname, priority in sorted(get_tmux_configs(version), key=(lambda v: v[1])): run_tmux_command('source', fname) + cmd = deduce_command() + if cmd: + run_tmux_command('set-environment', '-g', 'POWERLINE_COMMAND', deduce_command()) + run_tmux_command('refresh-client') def create_powerline_logger(args): @@ -124,11 +128,10 @@ def create_powerline_logger(args): def check_command(cmd): if which(cmd): - print(cmd) - sys.exit(0) + return cmd -def shell_command(pl, args): +def deduce_command(): '''Deduce which command to use for ``powerline`` Candidates: @@ -144,11 +147,21 @@ def shell_command(pl, args): * ``powerline-render``. Should not really ever be used. * ``{powerline_root}/scripts/powerline-render``. Same. ''' - check_command('powerline') - check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline')) - if which('sh') and which('sed') and which('socat'): - check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.sh')) - check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.py')) - check_command('powerline-render') - check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline-render')) - sys.exit(1) + return ( + None + or check_command('powerline') + or check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline')) + or ((which('sh') and which('sed') and which('socat')) + and check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.sh'))) + or check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.py')) + or check_command('powerline-render') + or check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline-render')) + ) + + +def shell_command(pl, args): + cmd = deduce_command() + if cmd: + print(cmd) + else: + sys.exit(1) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 31f7be40..81928706 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,4 +1,3 @@ -if-shell 'test -z "$POWERLINE_COMMAND"' 'if-shell "which powerline" "set-environment -g POWERLINE_COMMAND powerline" "set-environment -g POWERLINE_COMMAND powerline-render"' if-shell 'test -z "$POWERLINE_CONFIG_COMMAND"' 'set-environment -g POWERLINE_CONFIG_COMMAND powerline-config' # Don't version-check for this core functionality -- anything too old to From 9290c2a23bcbae8e756b676645e1fd32f8f452ab Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 20:35:23 +0400 Subject: [PATCH 1107/1472] Make daemon work with aboveleft shell key --- powerline/shell.py | 25 ++++++++++++++++++++++++ scripts/powerline-daemon | 41 +++++++++++++++++++++++----------------- scripts/powerline-render | 27 +++++--------------------- 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/powerline/shell.py b/powerline/shell.py index bffa1df8..8cfb2de6 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -1,5 +1,7 @@ # vim:fileencoding=utf-8:noet +import os + from powerline import Powerline from powerline.lib import mergedicts, parsedotval @@ -73,3 +75,26 @@ def finish_args(args): args.theme_option = {} if args.renderer_arg: args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg)) + + +def write_output(args, powerline, segment_info, write): + if args.renderer_arg: + segment_info.update(args.renderer_arg) + if args.side.startswith('above'): + for line in powerline.render_above_lines( + width=args.width, + segment_info=segment_info, + mode=os.environ.get('_POWERLINE_MODE'), + ): + write(line) + write('\n') + args.side = args.side[len('above'):] + + if args.side: + rendered = powerline.render( + width=args.width, + side=args.side, + segment_info=segment_info, + mode=os.environ.get('_POWERLINE_MODE'), + ) + write(rendered) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index e110625f..7ffcdb2f 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -12,8 +12,9 @@ from signal import signal, SIGTERM from time import sleep from functools import partial from locale import getpreferredencoding +from io import StringIO -from powerline.shell import get_argparser, finish_args, ShellPowerline +from powerline.shell import get_argparser, finish_args, ShellPowerline, write_output from powerline.lib.monotonic import monotonic is_daemon = False @@ -77,35 +78,41 @@ def render(args): 'environ': environ, 'args': args, } - key = (args.ext[0], args.renderer_module, - tuple(args.config) if args.config else None, - tuple(args.theme_option) if args.theme_option else None,) - if args.renderer_arg: - segment_info.update(args.renderer_arg) + key = ( + args.ext[0], + args.renderer_module, + tuple(args.config) if args.config else None, + tuple(args.theme_option) if args.theme_option else None, + tuple(args.renderer_arg) if args.renderer_arg else None, + ) finish_args(args) - pl = None + powerline = None try: - pl = powerlines[key] + powerline = powerlines[key] except KeyError: try: - pl = powerlines[key] = PowerlineDaemon( + powerline = powerlines[key] = PowerlineDaemon( args, logger=logger, - config_loader=config_loader + config_loader=config_loader, + run_once=False, ) + if logger is None: + logger = powerline.logger + if config_loader is None: + config_loader = powerline.config_loader except SystemExit: # Somebody thought raising system exit was a good idea, return '' except Exception as e: - if pl: - pl.pl.exception('Failed to render {0}: {1}', str(key), str(e)) + if powerline: + powerline.pl.exception('Failed to render {0}: {1}', str(key), str(e)) else: return 'Failed to render {0}: {1}'.format(str(key), str(e)) - if logger is None: - logger = pl.logger - if config_loader is None: - config_loader = pl.config_loader - return pl.render(width=args.width, side=args.side, segment_info=segment_info) + s = StringIO() + write_output(args, powerline, segment_info, s.write) + s.seek(0) + return s.read() def eintr_retry_call(func, *args, **kwargs): diff --git a/scripts/powerline-render b/scripts/powerline-render index 8b125217..bdedb1b7 100755 --- a/scripts/powerline-render +++ b/scripts/powerline-render @@ -5,10 +5,11 @@ import sys import os try: - from powerline.shell import ShellPowerline, get_argparser, finish_args + from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output except ImportError: sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) - from powerline.shell import ShellPowerline, get_argparser, finish_args # NOQA + from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output # NOQA + def write(output): try: @@ -16,28 +17,10 @@ def write(output): except UnicodeEncodeError: sys.stdout.write(output.encode('utf-8')) + if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() finish_args(args) powerline = ShellPowerline(args, run_once=True) segment_info = {'args': args, 'environ': os.environ} - if args.renderer_arg: - segment_info.update(args.renderer_arg) - if args.side.startswith('above'): - for line in powerline.render_above_lines( - width=args.width, - segment_info=segment_info, - mode=os.environ.get('_POWERLINE_MODE'), - ): - write(line) - sys.stdout.write('\n') - args.side = args.side[len('above'):] - - if args.side: - rendered = powerline.render( - width=args.width, - side=args.side, - segment_info=segment_info, - mode=os.environ.get('_POWERLINE_MODE'), - ) - write(rendered) + write_output(args, powerline, segment_info, write) From fed43e8af50a428ee2d2058d9a682974918df578 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 21:02:26 +0400 Subject: [PATCH 1108/1472] Run shell tests with and without daemon --- tests/test_shells/postproc.py | 7 +-- tests/test_shells/screenrc | 2 +- tests/test_shells/test.sh | 92 +++++++++++++++++++------------- tests/test_shells/zsh.daemon.ok | Bin 0 -> 11672 bytes 4 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 tests/test_shells/zsh.daemon.ok diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index ce68c9d5..f34fa81c 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -8,9 +8,10 @@ import sys import codecs -shell = sys.argv[1] -fname = os.path.join('tests', 'shell', shell + '.full.log') -new_fname = os.path.join('tests', 'shell', shell + '.log') +test_type = sys.argv[1] +shell = sys.argv[2] +fname = os.path.join('tests', 'shell', shell + '.' + test_type + '.full.log') +new_fname = os.path.join('tests', 'shell', shell + '.' + test_type + '.log') pid_fname = os.path.join('tests', 'shell', '3rd', 'pid') diff --git a/tests/test_shells/screenrc b/tests/test_shells/screenrc index d998652a..7c9674e5 100644 --- a/tests/test_shells/screenrc +++ b/tests/test_shells/screenrc @@ -1,3 +1,3 @@ width 1024 height 1 -logfile "tests/shell/${SH}.full.log" +logfile "tests/shell/${SH}.${TEST_TYPE}.full.log" diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index b0f30fed..656f2f71 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -3,17 +3,23 @@ FAILED=0 ONLY_SHELL="$1" check_screen_log() { - SH="$1" - if test -e tests/test_shells/${SH}.ok ; then - diff -u tests/test_shells/${SH}.ok tests/shell/${SH}.log + TEST_TYPE="$1" + SH="$2" + if test -e tests/test_shells/${SH}.${TEST_TYPE}.ok ; then + diff -a -u tests/test_shells/${SH}.${TEST_TYPE}.ok tests/shell/${SH}.${TEST_TYPE}.log + return $? + elif test -e tests/test_shells/${SH}.ok ; then + diff -a -u tests/test_shells/${SH}.ok tests/shell/${SH}.${TEST_TYPE}.log return $? else - cat tests/shell/${SH}.log + cat tests/shell/${SH}.${TEST_TYPE}.log return 1 fi } run_test() { + TEST_TYPE="$1" + shift SH="$1" SESNAME="powerline-shell-test-${SH}-$$" ARGS=( "$@" ) @@ -32,6 +38,7 @@ run_test() { fi fi + export TEST_TYPE export SH screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ @@ -59,21 +66,21 @@ run_test() { while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do sleep 0.1s done - ./tests/test_shells/postproc.py ${SH} - if ! check_screen_log ${SH} ; then + ./tests/test_shells/postproc.py ${TEST_TYPE} ${SH} + if ! check_screen_log ${TEST_TYPE} ${SH} ; then echo '____________________________________________________________' # Repeat the diff to make it better viewable in travis output echo "Diff (cat -v):" echo '============================================================' - check_screen_log ${SH} | cat -v + check_screen_log ${TEST_TYPE} ${SH} | cat -v echo '____________________________________________________________' echo "Failed ${SH}. Full output:" echo '============================================================' - cat tests/shell/${SH}.full.log + cat tests/shell/${SH}.${TEST_TYPE}.full.log echo '____________________________________________________________' echo "Full output (cat -v):" echo '============================================================' - cat -v tests/shell/${SH}.full.log + cat -v tests/shell/${SH}.${TEST_TYPE}.full.log echo '____________________________________________________________' case ${SH} in *ksh) @@ -112,39 +119,52 @@ mkdir tests/shell/3rd/'(echo)' mkdir tests/shell/3rd/'$(echo)' mkdir tests/shell/3rd/'`echo`' -if ! run_test bash --norc --noprofile -i ; then - FAILED=1 -fi - -if ! run_test zsh -f -i ; then - FAILED=1 -fi - mkdir tests/shell/fish_home export XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" -if ! run_test fish -i ; then - FAILED=1 -fi - -if ! run_test tcsh -f -i ; then - FAILED=1 -fi - -if ! run_test bb -i ; then - FAILED=1 -fi unset ENV -if ! run_test mksh -i ; then - FAILED=1 -fi +powerline-daemon -k || true +sleep 1s -if ! run_test dash -i ; then - # dash tests are not stable, see #931 - # FAILED=1 - true -fi +for TEST_TYPE in "daemon" "nodaemon" ; do + if test $TEST_TYPE == daemon ; then + sh -c 'echo $$ > tests/shell/daemon_pid; ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & + fi + if ! run_test $TEST_TYPE bash --norc --noprofile -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE zsh -f -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE fish -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE tcsh -f -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE bb -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE mksh -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE dash -i ; then + # dash tests are not stable, see #931 + # FAILED=1 + true + fi + if test $TEST_TYPE == daemon ; then + ./scripts/powerline-daemon -k + wait $(cat tests/shell/daemon_pid) + fi +done test "x$ONLY_SHELL" = "x" && rm -r tests/shell exit $FAILED diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok new file mode 100644 index 0000000000000000000000000000000000000000..3e4000aed992ae14f684fbc9e44e4f914e4d5377 GIT binary patch literal 11672 zcmeHNTW;Dw6zw-#$j?=3N>Izg50X|Wm1u$#ssIsr^uwXS;7M`uBeET;D9TTM``Io4 zbpvg;z*4e+tf4cuNe2=fQ&qts0}|jF-!U`i-ZN+JOkzpS8%wevk`D=gyvGGa@pZ9K z6oh;qQOq4bfJ|Oz~j6q5Eh&>!^_Zq`3(OAaS~>0M3fS$m=lnlNf|M z%@}+HnF#o08G0UNOhqrg;dfAJ?CqDfkIJ=!^<{3WUM=Ucea&`}*>`e|WuOeXI#u)x zF41huG?3{o#{us(i6)YO>qlI?CK*@Xz7H|oPU{2Z*0?2~e;I+}>9R1!0F&f#o4{!g zii2OUX_$zfSy5~&P8TvANTE*G0*-roqNz}XFI`PX;M%7MU#J#1IzlIq&+?9UZK4#s zNXUE>b}E|;{6{kh1akNe0;R<=PjRFtk0+$tv7$SOp}y#-6KBD6GJR205DeN?2=z>O zii332aY1Q!Kr=x_$MC7E!RMYH<~`#ShG%lG5i+Mg{QG8^NY-eRy;H4)=my}I&;A&v zh3Qe-MoMoXdZSt;cIKDE`9J!hr|CMhI*JQeY__nV^wlNrc#0BUf{ScJGZh!90RJpdnOi*?fij^Q zaB6Uw+pIMBSmYbxysn&2cJQMtivmumhl(ST-_7Pc90i7>ucD{s3Fn04*lxrf0@ued zbtoS;Q;bf->2y925@;d?Vk((r5XLeJlgVfRK9}2;uChQ3_DW;Mr2#t8pj;u#?KF$W zSjM9dPBb7nh9uc^7@c2;Sxg4#M1!(LVzR|zGL|vvgA)ZvCR4*@YJ5*5?9@M$8{3sy z`DmkFt(Iz=>np(BDwO$GQWLmy?)-LDp?Q%z($TSNnfloUq;Y1ct(j^Mok6A_3&$7{ z<=7cB`n=Wx9`uUC0k5Yx?#$uo`GO9zS(oZF9*kiFKc|8BHZjQ_<~{qE54NfF6lupl G9P$SVyM#di literal 0 HcmV?d00001 From f3d972849ebedf0993d42b5f85e2fe6ce16245f4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 21:10:15 +0400 Subject: [PATCH 1109/1472] Fall back to powerline-render when using powerline.sh --- client/powerline.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/powerline.sh b/client/powerline.sh index d1657738..a56bc296 100755 --- a/client/powerline.sh +++ b/client/powerline.sh @@ -10,4 +10,8 @@ ADDRESS="powerline-ipc-${UID:-`id -u`}" done env -0 | sed 's/\(\x00\)\([^\x00]\)\|^/\1--env=\2/g' printf -- '--cwd=%s\0' "$PWD" -) | socat -t 10 - abstract-client:"$ADDRESS" +) | socat -lf/dev/null -t 10 - abstract-client:"$ADDRESS" + +if test $? -ne 0 ; then + powerline-render "$@" +fi From 9da3e04bc2519f173d6c594539102aef68734ac5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 21:27:31 +0400 Subject: [PATCH 1110/1472] Do not hardcode scripts/powerline in input.* --- tests/test_shells/input.bash | 7 ++++--- tests/test_shells/input.bb | 5 +++-- tests/test_shells/input.dash | 5 +++-- tests/test_shells/input.fish | 7 ++++--- tests/test_shells/input.mksh | 5 +++-- tests/test_shells/input.tcsh | 5 +++-- tests/test_shells/input.zsh | 9 +++++---- tests/test_shells/test.sh | 2 ++ 8 files changed, 27 insertions(+), 18 deletions(-) diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 748e78fa..ff7ad797 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -1,8 +1,9 @@ -POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +export VIRTUAL_ENV= +source powerline/bindings/bash/powerline.sh +POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" -export VIRTUAL_ENV= -source powerline/bindings/bash/powerline.sh ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/input.bb b/tests/test_shells/input.bb index d9ae1342..2d4fbc42 100644 --- a/tests/test_shells/input.bb +++ b/tests/test_shells/input.bb @@ -1,8 +1,9 @@ -POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +. powerline/bindings/shell/powerline.sh +POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= -. powerline/bindings/shell/powerline.sh ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash index d9ae1342..2d4fbc42 100644 --- a/tests/test_shells/input.dash +++ b/tests/test_shells/input.dash @@ -1,8 +1,9 @@ -POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +. powerline/bindings/shell/powerline.sh +POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= -. powerline/bindings/shell/powerline.sh ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 3e69ad5e..f8da144e 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -1,9 +1,10 @@ -set POWERLINE_COMMAND "$PWD/scripts/powerline -p $PWD/powerline/config_files" +set fish_function_path $fish_function_path "$PWD/powerline/bindings/fish" +powerline-setup +set POWERLINE_COMMAND "$POWERLINE_COMMAND -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" setenv VIRTUAL_ENV -set fish_function_path $fish_function_path "$PWD/powerline/bindings/fish" -powerline-setup ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. setenv VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh index a4a5928a..a2f0a7dd 100644 --- a/tests/test_shells/input.mksh +++ b/tests/test_shells/input.mksh @@ -1,8 +1,9 @@ -POWERLINE_COMMAND="$PWD/scripts/powerline -p $PWD/powerline/config_files" +. powerline/bindings/shell/powerline.sh +POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= -. powerline/bindings/shell/powerline.sh ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/input.tcsh b/tests/test_shells/input.tcsh index 4d48a465..0bc6b78a 100644 --- a/tests/test_shells/input.tcsh +++ b/tests/test_shells/input.tcsh @@ -1,6 +1,7 @@ -setenv POWERLINE_COMMAND $PWD:q/scripts/powerline" -p "$PWD:q/powerline/config_files" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" +source powerline/bindings/tcsh/powerline.tcsh +setenv POWERLINE_COMMAND "$POWERLINE_COMMAND -p "$PWD:q/powerline/config_files" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" unsetenv VIRTUAL_ENV -source powerline/bindings/tcsh/powerline.tcsh ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. setenv VIRTUAL_ENV $HOME:q"/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index b40c3283..952ee756 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -1,14 +1,15 @@ unsetopt promptsp transientrprompt -POWERLINE_COMMAND=( $PWD/scripts/powerline -p $PWD/powerline/config_files ) -POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) -POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) setopt interactivecomments # POWERLINE_CONFIG_PATH=$PWD/powerline/config_files # POWERLINE_THEME_CONFIG=( default_leftonly.segment_data.hostname.args.only_if_ssh=false ) # POWERLINE_CONFIG=( ext.shell.theme=default_leftonly ) POWERLINE_NO_ZSH_ZPYTHON=1 # TODO: make tests work with zsh/zpython +source powerline/bindings/zsh/powerline.zsh +POWERLINE_COMMAND=( $POWERLINE_COMMAND -p $PWD/powerline/config_files ) +POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) +POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) export VIRTUAL_ENV= -source powerline/bindings/zsh/powerline.zsh ; cd tests/shell/3rd +cd tests/shell/3rd cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 656f2f71..433b7319 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -127,6 +127,8 @@ unset ENV powerline-daemon -k || true sleep 1s +scripts/powerline-config shell command + for TEST_TYPE in "daemon" "nodaemon" ; do if test $TEST_TYPE == daemon ; then sh -c 'echo $$ > tests/shell/daemon_pid; ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & From 28aee92c517d5e8758a47dc7543ba19aeca6f6b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 21:56:37 +0400 Subject: [PATCH 1111/1472] Fix zsh daemon tests: disable hostname and user before testing select Reason: hostname and user do not have stable width and select prompt width with daemon depends on previous prompt width. --- tests/test_shells/input.zsh | 16 ++++++++------ tests/test_shells/zsh.daemon.ok | Bin 11672 -> 11878 bytes tests/test_shells/{zsh.ok => zsh.nodaemon.ok} | 20 ++++++++++-------- 3 files changed, 20 insertions(+), 16 deletions(-) rename tests/test_shells/{zsh.ok => zsh.nodaemon.ok} (90%) diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 952ee756..51095f5f 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -1,4 +1,4 @@ -unsetopt promptsp transientrprompt +unsetopt promptsp notransientrprompt setopt interactivecomments # POWERLINE_CONFIG_PATH=$PWD/powerline/config_files # POWERLINE_THEME_CONFIG=( default_leftonly.segment_data.hostname.args.only_if_ssh=false ) @@ -16,12 +16,6 @@ VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false -select abc in def ghi jkl -do - echo $abc - break -done -1 kill `cat pid` ; sleep 1s cd "$DIR1" cd ../"$DIR2" @@ -37,5 +31,13 @@ POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,4] ${${POWERLINE_COMMAND[5]}/_leftonly} echo abc false +POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.hostname.display=false ) +POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.user.display=false ) +select abc in def ghi jkl +do + echo $abc + break +done +1 true is the last line exit diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 3e4000aed992ae14f684fbc9e44e4f914e4d5377..7469e23b61ee6790cceceee1aa8a298fed046535 100644 GIT binary patch delta 203 zcmbOc{VZm~eBRAlg&#{!uI6r;yg>8Ifxv6<2@hOQV ziFz6N#U*)(xv6?7nZ*S;iIui#i8;lo3YwD-GRjP@7U5+&v|ux%HV-4n6kVLAloqEJ zVVDreFFWxLH_(8|2Q@EGp3j>4agI1L@IKpL|Ov8Lxb_Zag0OCfss5T$7FT YOg0~4lxLi*z}P)mpWAKoeBQ^B0Eapid ; while true ; do sleep 0.1s ; done' & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  select abc in def ghi jkl - select  do - select   echo $abc - select   break - select  done -1) def 2) ghi 3) jkl - Select variant  1 -def -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  kill `cat pid` ; sleep 1s +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1] + terminated bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done'   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" @@ -31,3 +23,13 @@ def  INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  echo abc abc  INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  false + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.hostname.display=false ) + INSERT  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.user.display=false ) + INSERT  ⋯  tests  shell  3rd  select abc in def ghi jkl + select  do + select   echo $abc + select   break + select  done +1) def 2) ghi 3) jkl + Select variant  1 +def From 44cca975666e5b8f6406310e323bc239fb80b1e5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 22:03:22 +0400 Subject: [PATCH 1112/1472] Check whether daemon log is as empty as it should --- tests/test_shells/test.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 433b7319..f224e22f 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -165,6 +165,13 @@ for TEST_TYPE in "daemon" "nodaemon" ; do if test $TEST_TYPE == daemon ; then ./scripts/powerline-daemon -k wait $(cat tests/shell/daemon_pid) + if ! test -z "$(cat tests/shell/daemon_log)" ; then + echo '____________________________________________________________' + echo "Daemon log:" + echo '============================================================' + cat tests/shell/daemon_log + FAILED=1 + fi fi done From ea7f3b650134e639be4acd68f9d504ee42db5c49 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 22:20:06 +0400 Subject: [PATCH 1113/1472] Add powerline-daemon --quiet --- scripts/powerline-daemon | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 7ffcdb2f..bc054680 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -300,7 +300,7 @@ def check_existing(): return sock -def test_connect(): +def kill_daemon(): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: try: @@ -351,6 +351,7 @@ def lockpidfile(): def main(): p = ArgumentParser(description= 'Daemon to improve the performance of powerline') + p.add_argument('--quiet', '-q', action='store_true', help='Without other options: do not complain about already running powerline-daemon instance. Will still exit with 1. With `--kill\' and `--replace\': do not show any messages. With `--foreground\': ignored. Does not silence exceptions in any case.') a = p.add_mutually_exclusive_group().add_argument a('--kill', '-k', action='store_true', help='Kill an already running instance') a('--foreground', '-f', action='store_true', help='Run in the foreground (dont daemonize)') @@ -358,15 +359,19 @@ def main(): args = p.parse_args() if args.kill: - if test_connect(): - print ('Kill command sent to daemon, if it does not die in a couple of seconds use kill to kill it') + if kill_daemon(): + if not args.quiet: + print ('Kill command sent to daemon, if it does not die in a couple of seconds use kill to kill it') + raise SystemExit(0) else: - print ('No running daemon found') - return + if not args.quiet: + print ('No running daemon found') + raise SystemExit(1) if args.replace: - while test_connect(): - print ('Kill command sent to daemon, waiting for daemon to exit, press Ctrl-C to terminate wait and exit') + while kill_daemon(): + if not args.quiet: + print ('Kill command sent to daemon, waiting for daemon to exit, press Ctrl-C to terminate wait and exit') sleep(2) if use_filesystem and not args.foreground: @@ -377,15 +382,17 @@ def main(): if use_filesystem: # Create a locked pid file containing the daemon's PID if lockpidfile() is None: - print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), - file=sys.stderr) + if not args.quiet: + print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), + file=sys.stderr) raise SystemExit(1) # Bind to address or bail if we cannot bind sock = check_existing() if sock is None: - print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), - file=sys.stderr) + if not args.quiet: + print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), + file=sys.stderr) raise SystemExit(1) if args.foreground: From 4ddac2a2f5452c57cf3ceea44771a3f69e3b51ed Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 22:27:58 +0400 Subject: [PATCH 1114/1472] Do not run `set-environment` in tmux if POWERLINE_COMMAND is set --- powerline/bindings/config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 5899e740..4dd79cc2 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -111,10 +111,11 @@ def source_tmux_files(pl, args): version = get_tmux_version(pl) for fname, priority in sorted(get_tmux_configs(version), key=(lambda v: v[1])): run_tmux_command('source', fname) - cmd = deduce_command() - if cmd: - run_tmux_command('set-environment', '-g', 'POWERLINE_COMMAND', deduce_command()) - run_tmux_command('refresh-client') + if not os.environ.get('POWERLINE_COMMAND'): + cmd = deduce_command() + if cmd: + run_tmux_command('set-environment', '-g', 'POWERLINE_COMMAND', deduce_command()) + run_tmux_command('refresh-client') def create_powerline_logger(args): From 8bc5bb3ff4368588334cd2b3ad8f362aaad10f87 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 22:24:23 +0400 Subject: [PATCH 1115/1472] Update documentation --- .../installation/troubleshooting-common.rst | 7 +------ docs/source/overview.rst | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index bb688d7e..55345394 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -124,12 +124,7 @@ I am suffering bad lags before displaying shell prompt To get rid of these lags there currently are two options: -* Take ``powerline-daemon`` script and one of ``powerline-client`` - implementations from ``feature/daemon`` branch (all ``powerline-client`` - implementations leave in ``client`` folder: you need to either compile - ``powerline.c`` or install ``socat`` and use ``powerline.sh`` - (``powerline.py`` is much slower)). Fortunately this branch will be merged in - the future. +* Run ``powerline-daemon``. Powerline does not automatically start it for you. * Compile and install ``libzpython`` module that lives in https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 9ddc798a..f50940a6 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -149,6 +149,19 @@ hand: ``powerline`` is installed and run just like any other plugin using Shell prompts ------------- +.. note:: + It is advised that you run ``powerline-daemon`` before using any of the + below solutions. To do this add + + .. code-block:: bash + + powerline-daemon -q + + just before sourcing powerline bindings script or running + ``powerline-setup``. Use ``|| true`` or equivalent if you run your + configuration with ``set -e`` because ``powerline-daemon`` will exit with + ``1`` if daemon is already running. + Bash prompt ^^^^^^^^^^^ @@ -227,6 +240,14 @@ is the absolute path to your Powerline installation directory:: powerline support. You may specify location of this script via ``$POWERLINE_CONFIG_COMMAND`` environment variable. +.. note:: + It is advised that you run ``powerline-daemon`` before adding the above line + to tmux.conf. To do so add:: + + run-shell "powerline-daemon -q" + + to :file:`.tmux.conf`. + IPython prompt -------------- From 151b3f1117c8b3fe464281cc836d4b4e36f3f0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 25 Jan 2014 16:20:06 +0100 Subject: [PATCH 1116/1472] Use readthedocs theme for docs --- docs/source/conf.py | 14 ++++++++++++-- setup.py | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 645a493c..70c104f6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,10 +10,20 @@ extensions = ['powerline_autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sp source_suffix = '.rst' master_doc = 'index' project = u'Powerline' -copyright = u'Kim Silkebækken' version = 'beta' release = 'beta' exclude_patterns = ['_build'] pygments_style = 'sphinx' -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' html_static_path = ['_static'] +html_show_copyright = False + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + except ImportError: + pass diff --git a/setup.py b/setup.py index 16561d85..d397e03e 100755 --- a/setup.py +++ b/setup.py @@ -92,6 +92,7 @@ setup( extras_require={ 'docs': [ 'Sphinx', + 'sphinx_rtd_theme', ], }, test_suite='tests' if not OLD_PYTHON else None, From ecb3b690db1b2802c9e0734990eecb89f2a82951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 28 Aug 2013 10:41:03 +0200 Subject: [PATCH 1117/1472] Add updated font installation docs --- docs/source/font-installation.rst | 142 ++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 docs/source/font-installation.rst diff --git a/docs/source/font-installation.rst b/docs/source/font-installation.rst new file mode 100644 index 00000000..d4d6b173 --- /dev/null +++ b/docs/source/font-installation.rst @@ -0,0 +1,142 @@ +*********************************** +Font installation and configuration +*********************************** + +Powerline uses several special glyphs to get the arrow effect and some +custom symbols for developers. This requires that you either have a symbol +font or a patched font on your system. Your terminal emulator must also +support either patched fonts or fontconfig for Powerline to work properly. + +You can also enable :ref:`24-bit color support ` +if your terminal emulator supports it. + +.. table:: Application/terminal emulator feature support matrix + :name: term-feature-support-matrix + + ===================== ======= =================================== ===================== ===================== ===================== + Name OS Recommended installation method Patched font support Fontconfig support 24-bit color support + ===================== ======= =================================== ===================== ===================== ===================== + Gnome Terminal Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| + Gvim Linux `Patched font`_ |i_yes| |i_no| |i_yes| + Konsole Linux `Fontconfig`_ |i_yes| |i_yes| |i_yes| + lxterminal Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| + rxvt-unicode Linux `rxvt-unicode`_ or `Patched font`_ |i_partial| [#]_ |i_no| |i_no| + st Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| + Xfce Terminal Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| + xterm Linux `Patched font`_ |i_yes| |i_no| |i_partial| [#]_ + iTerm2 OS X `Patched font`_ |i_yes| |i_no| |i_no| + MacVim OS X `Patched font`_ |i_yes| |i_no| |i_yes| + Terminal.app OS X `Patched font`_ |i_yes| |i_no| |i_no| + ===================== ======= =================================== ===================== ===================== ===================== + +.. |i_yes| image:: _static/img/icons/tick.png +.. |i_no| image:: _static/img/icons/cross.png +.. |i_partial| image:: _static/img/icons/error.png + +.. [#] Must be compiled with ``--enable-unicode3`` for the + patched font to work. +.. [#] Uses nearest color from 8-bit palette. + +Fontconfig +========== + +This method only works on Linux. It's the recommended method if your +terminal emulator supports it as you don't have to patch any fonts, and it +generally works well with any coding font. + +#. Download the latest version of the symbol font and fontconfig file:: + + wget https://github.com/Lokaltog/powerline/raw/develop/font/PowerlineSymbols.otf + wget https://github.com/Lokaltog/powerline/raw/develop/font/10-powerline-symbols.conf + +#. Move the symbol font to a valid X font path. Valid font paths can be + listed with ``xset q``:: + + mv PowerlineSymbols.otf ~/.fonts/ + +#. Update font cache for the path you moved the font to (you may need to be + root to update the cache for system-wide paths):: + + fc-cache -vf ~/.fonts/ + +#. Install the fontconfig file. For newer versions of fontconfig the config + path is ``~/.config/fontconfig/conf.d/``, for older versions it's + ``~/.fonts.conf.d/``:: + + mv 10-powerline-symbols.conf ~/.config/fontconfig/conf.d/ + +If you can't see the custom symbols, please close all instances of your +terminal emulator. You may need to restart X for the changes to take +effect. + +If you *still* can't see the custom symbols, double-check that you have +installed the font to a valid X font path, and that you have installed the +fontconfig file to a valid fontconfig path. Alternatively try to install +a `Patched font`_. + +Patched font +============ + +This method is the fallback method and works for every terminal emulator and +OS, with the exception of `rxvt-unicode`_. + +Download the font of your choice from `powerline-fonts`_. If you can't find +your preferred font in the `powerline-fonts`_ repo, you'll have to patch +your own font instead. See :ref:`font-patching` for instructions. + +.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts + +Installation on Linux +--------------------- + +#. Move the patched font to a valid X font path. Valid font paths can be + listed with ``xset q``:: + + mv 'MyFont for Powerline.otf' ~/.fonts/ + +#. Update font cache for the path you moved the font to (you may need to be + root to update the cache for system-wide paths):: + + fc-cache -vf ~/.fonts/ + +After installing the patched font you need to update Gvim or your terminal +emulator to use the patched font. The correct font usually ends with *for +Powerline*. + +If you can't see the custom symbols, please close all instances of your +terminal emulator. You may need to restart X for the changes to take +effect. + +If you *still* can't see the custom symbols, double-check that you have +installed the font to a valid X font path. + +Installation on OS X +-------------------- + +Install your patched font by double-clicking the font file in Finder, then +clicking :guilabel:`Install this font` in the preview window. + +After installing the patched font you need to update MacVim or your terminal +emulator to use the patched font. The correct font usually ends with *for +Powerline*. + +Special cases +============= + +rxvt-unicode +------------ + +.. note:: Symbol font support generally doesn't work well in + rxvt-unicode. It's recommended that you either disable the symbols or + switch to a better terminal emulator if you want to use Powerline. + +rxvt-unicode allows using a `Patched font`_ only if it's compiled with the +``--enable-unicode3`` flag. + +For unsupported fonts (e.g. bitmap fonts like Terminus), you can't use +``PowerlineSymbols.otf`` as a fallback since rxvt-unicode has trouble +determining the font's metrics. A solution to this issue is to get +a `Patched font`_ and add this as a fallback font to your +``.Xresources``/``.Xdefaults``:: + + URxvt*font: xft:Terminus:pixelsize=12,xft:Inconsolata\ for\ Powerline:pixelsize=12 From c2379df50a6284268b5952f3b02517282ccf6768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Sat, 25 Jan 2014 16:19:47 +0100 Subject: [PATCH 1118/1472] Remove old docs and update documentation structure Most of the files are empty and need to be copied or rewritten from the old docs. Font patching docs have been removed entirely and will be moved to the powerline-fontpatcher repo. Ref #769 --- .../configuration-and-customization.rst | 15 + docs/source/configuration-reference.rst | 3 + docs/source/configuration.rst | 636 ------------------ docs/source/font-installation.rst | 142 ---- docs/source/fontpatching.rst | 136 ---- docs/source/index.rst | 19 +- docs/source/installation.rst | 8 + docs/source/installation/linux.rst | 103 +-- docs/source/installation/osx.rst | 124 +--- .../installation/troubleshooting-common.rst | 136 ---- docs/source/introduction.rst | 65 -- ...se-credits.rst => license-and-credits.rst} | 14 +- docs/source/overview.rst | 378 ++--------- docs/source/segment-reference.rst | 8 + .../common.rst | 0 .../{segments => segment-reference}/shell.rst | 0 .../{segments => segment-reference}/vim.rst | 0 .../{tipstricks.rst => tips-and-tricks.rst} | 18 +- docs/source/troubleshooting.rst | 8 + docs/source/troubleshooting/linux.rst | 3 + docs/source/troubleshooting/osx.rst | 3 + docs/source/usage.rst | 15 + docs/source/usage/other.rst | 6 + docs/source/usage/shell-prompts.rst | 15 + docs/source/usage/wm-widgets.rst | 9 + 25 files changed, 175 insertions(+), 1689 deletions(-) create mode 100644 docs/source/configuration-and-customization.rst create mode 100644 docs/source/configuration-reference.rst delete mode 100644 docs/source/configuration.rst delete mode 100644 docs/source/font-installation.rst delete mode 100644 docs/source/fontpatching.rst create mode 100644 docs/source/installation.rst delete mode 100644 docs/source/installation/troubleshooting-common.rst delete mode 100644 docs/source/introduction.rst rename docs/source/{license-credits.rst => license-and-credits.rst} (76%) create mode 100644 docs/source/segment-reference.rst rename docs/source/{segments => segment-reference}/common.rst (100%) rename docs/source/{segments => segment-reference}/shell.rst (100%) rename docs/source/{segments => segment-reference}/vim.rst (100%) rename docs/source/{tipstricks.rst => tips-and-tricks.rst} (93%) create mode 100644 docs/source/troubleshooting.rst create mode 100644 docs/source/troubleshooting/linux.rst create mode 100644 docs/source/troubleshooting/osx.rst create mode 100644 docs/source/usage.rst create mode 100644 docs/source/usage/other.rst create mode 100644 docs/source/usage/shell-prompts.rst create mode 100644 docs/source/usage/wm-widgets.rst diff --git a/docs/source/configuration-and-customization.rst b/docs/source/configuration-and-customization.rst new file mode 100644 index 00000000..6d5e8c79 --- /dev/null +++ b/docs/source/configuration-and-customization.rst @@ -0,0 +1,15 @@ +******************************* +Configuration and customization +******************************* + +Quick setup guide +----------------- + +References +---------- + +.. toctree:: + :glob: + + configuration-reference + segment-reference diff --git a/docs/source/configuration-reference.rst b/docs/source/configuration-reference.rst new file mode 100644 index 00000000..1e4eef8b --- /dev/null +++ b/docs/source/configuration-reference.rst @@ -0,0 +1,3 @@ +*********************** +Configuration reference +*********************** diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst deleted file mode 100644 index 23a3022e..00000000 --- a/docs/source/configuration.rst +++ /dev/null @@ -1,636 +0,0 @@ -************* -Configuration -************* - -.. note:: **You DO NOT have to fork the main GitHub repo to personalize your - Powerline configuration!** Please read through the :ref:`quick-guide` for - a quick introduction to user configuration. - -Powerline is configured with one main configuration file, and with separate -configuration files for themes and colorschemes. All configuration files are -written in JSON, with the exception of segment definitions, which are -written in Python. - -Powerline provides default configurations in the following locations: - -`Main configuration`_ - :file:`powerline/config.json` -`Colorschemes`_ - :file:`powerline/colorschemes/{name}.json`, - :file:`powerline/colorscheme/__main__.json`, - :file:`powerline/colorschemes/{extension}/{name}.json` -`Themes`_ - :file:`powerline/themes/{extension}/default.json` - -The default configuration files are stored in the main package. User -configuration files are stored in :file:`$XDG_CONFIG_HOME/powerline` for -Linux users, and in :file:`~/.config/powerline` for OS X users. This usually -corresponds to :file:`~/.config/powerline` on both platforms. - -.. _quick-guide: - -Quick setup guide -================= - -This guide will help you with the initial configuration of Powerline. - -Start by copying the entire set of default configuration files to the -corresponding path in your user config directory: - -.. code-block:: sh - - mkdir ~/.config/powerline - cp -R /path/to/powerline/config_files/* ~/.config/powerline - -Each extension (vim, tmux, etc.) has its own theme, and they are located in -:file:`{config directory}/themes/{extension}/default.json`. - -If you want to move, remove or customize any of the provided segments, you -can do that by updating the segment dictionary in the theme you want to -customize. A segment dictionary looks like this: - -.. code-block:: javascript - - { - "name": "segment_name" - ... - } - -You can move the segment dictionaries around to change the segment -positions, or remove the entire dictionary to remove the segment from the -prompt or statusline. - -.. note:: It's essential that the contents of all your configuration files - is valid JSON! It's strongly recommended that you run your configuration - files through ``jsonlint`` after changing them. - -Some segments need a user configuration to work properly. Here's a couple of -segments that you may want to customize right away: - -**E-mail alert segment** - You have to set your username and password (and possibly server/port) - for the e-mail alert segment. If you're using GMail it's recommended - that you `generate an application-specific password - `_ for this purpose. - - Open a theme file, scroll down to the ``email_imap_alert`` segment and - set your ``username`` and ``password``. The server defaults to GMail's - IMAP server, but you can set the server/port by adding a ``server`` and - a ``port`` argument. -**Weather segment** - The weather segment will try to find your location using a GeoIP lookup, - so unless you're on a VPN you probably won't have to change the location - query. - - If you want to change the location query or the temperature unit you'll - have to update the segment arguments. Open a theme file, scroll down to - the weather segment and update it to include unit/location query - arguments: - - .. code-block:: javascript - - { - "name": "weather", - "priority": 50, - "args": { - "unit": "F", - "location_query": "oslo, norway" - } - }, - -.. _config-main: - -Main configuration -================== - -:Location: :file:`powerline/config.json` - -The main configuration file defines some common options that applies to all -extensions, as well as some extension-specific options like themes and -colorschemes. - -Common configuration --------------------- - -Common configuration is a subdictionary that is a value of ``common`` key in -:file:`powerline/config.json` file. - -.. _config-common-term_truecolor: - -``term_truecolor`` - Defines whether to output cterm indices (8-bit) or RGB colors (24-bit) - to the terminal emulator. See the :ref:`term-feature-support-matrix` for - information on whether your terminal emulator supports 24-bit colors. - -.. _config-common-ambiwidth: - -``ambiwidth`` - Tells powerline what to do with characters with East Asian Width Class - Ambigious (such as Euro, Registered Sign, Copyright Sign, Greek - letters, Cyrillic letters). Valid values: any positive integer; it is - suggested that you only set it to 1 (default) or 2. - -``watcher`` - Select filesystem watcher. Variants are - - ======= =================================== - Variant Description - ======= =================================== - auto Selects most performant watcher. - inotify Select inotify watcher. Linux only. - stat Select stat-based polling watcher. - ======= =================================== - - Default is ``auto``. - -.. _config-common-additional_escapes: - -``additional_escapes`` - Valid for shell extensions, makes sense only if :ref:`term_truecolor - ` is enabled. Is to be set from command-line - (unless you are sure you always need it). Controls additional escaping that - is needed for tmux/screen to work with terminal true color escape codes: - normally tmux/screen prevent terminal emulator from receiving these control - codes thus rendering powerline prompt colorless. Valid values: ``"tmux"``, - ``"screen"``, ``null`` (default). - -``dividers`` - Defines the dividers used in all Powerline extensions. This option - should usually only be changed if you don't have a patched font, or if - you use a font patched with the legacy font patcher. - - The ``hard`` dividers are used to divide segments with different - background colors, while the ``soft`` dividers are used to divide - segments with the same background color. - -.. _config-common-paths: - -``paths`` - Defines additional paths which will be searched for modules when using - :ref:`module segment option `. Paths defined here - have priority when searching for modules. - -``log_file`` - Defines path which will hold powerline logs. If not present, logging will be - done to stderr. - -``log_level`` - String, determines logging level. Defaults to ``WARNING``. - -``log_format`` - String, determines format of the log messages. Defaults to - ``'%(asctime)s:%(level)s:%(message)s'``. - -``interval`` - Number, determines time (in seconds) between checks for changed - configuration. Checks are done in a seprate thread. Use ``null`` to check - for configuration changes on ``.render()`` call in main thread. - Defaults to ``None``. - -``reload_config`` - Boolean, determines whether configuration should be reloaded at all. - Defaults to ``True``. - -Extension-specific configuration --------------------------------- - -Common configuration is a subdictionary that is a value of ``ext`` key in -:file:`powerline/config.json` file. - -``colorscheme`` - Defines the colorscheme used for this extension. - -``theme`` - .. _config-ext-theme: - - Defines the theme used for this extension. - -``local_themes`` - .. _config-ext-local_themes: - - Defines themes used when certain conditions are met, e.g. for - buffer-specific statuslines in vim. Value depends on extension used. For vim - it is a dictionary ``{matcher_name : theme_name}``, where ``matcher_name`` - is either ``matcher_module.module_attribute`` or ``module_attribute`` - (``matcher_module`` defaults to ``powerline.matchers.vim``) and - ``module_attribute`` should point to a function that returns boolean value - indicating that current buffer has (not) matched conditions. - -.. _config-colors: - -Color definitions -================= - -:Location: :file:`powerline/colors.json` - -.. _config-colors-colors: - -``colors`` - Color definitions, consisting of a dict where the key is the name of the - color, and the value is one of the following: - - * A cterm color index. - * A list with a cterm color index and a hex color string (e.g. ``[123, - "aabbcc"]``). This is useful for colorschemes that use colors that - aren't available in color terminals. - -``gradients`` - Gradient definitions, consisting of a dict where the key is the name of the - gradient, and the value is a list containing one or two items, second item - is optional: - - * A list of cterm color indicies. - * A list of hex color strings. - - It is expected that you define gradients from least alert color to most - alert or use non-alert colors. - -.. _config-colorschemes: - -Colorschemes -============ - -:Location: :file:`powerline/colorschemes/{name}.json`, - :file:`powerline/colorscheme/__main__.json`, - :file:`powerline/colorschemes/{extension}/{name}.json` - -Colorscheme files are processed in order given: definitions from each next file -override those from each previous file. It is required that either -:file:`powerline/colorschemes/{name}.json`, or -:file:`powerline/colorschemes/{extension}/{name}.json` exists. - -``name`` - Name of the colorscheme. - -.. _config-colorschemes-groups: - -``groups`` - Segment highlighting groups, consisting of a dict where the key is the - name of the highlighting group (usually the function name for function - segments), and the value is either - - #) a dict that defines the foreground color, background color and - attributes: - - ``fg`` - Foreground color. Must be defined in :ref:`colors - `. - - ``bg`` - Background color. Must be defined in :ref:`colors - `. - - ``attr`` - List of attributes. Valid values are one or more of ``bold``, - ``italic`` and ``underline``. Note that some attributes may be - unavailable in some applications or terminal emulators. If you do not - need any attributes leave this empty. - - #) a string (an alias): a name of existing group. This group’s definition - will be used when this color is requested. - -``mode_translations`` - Mode-specific highlighting for extensions that support it (e.g. the vim - extension). It's an easy way of changing a color in a specific mode. - Consists of a dict where the key is the mode and the value is a dict - with the following options: - - ``colors`` - A dict where the key is the color to be translated in this mode, and - the value is the new color. Both the key and the value must be defined - in :ref:`colors `. - - ``groups`` - Segment highlighting groups for this mode. Same syntax as the main - :ref:`groups ` option. - -.. _config-themes: - -Themes -====== - -:Location: :file:`powerline/themes/{extension}/{name}.json` - -``name`` - Name of the theme. - -.. _config-themes-default_module: - -``default_module`` - Python module where segments will be looked by default. - -.. _config-themes-segment_data: - -``segment_data`` - A dict where keys are segment names or strings ``{module}.{name}``. Used to - specify default values for various keys: - :ref:`after `, - :ref:`args ` (only for function segments), - :ref:`before `, - :ref:`contents ` (only for string segments - if :ref:`name ` is defined), - :ref:`display ` values of these - keys are first searched in the segment description, then in ``segment_data`` - key of a local theme, then in ``segment_data`` key of a :ref:`default theme - `. For the :ref:`default theme ` itself - step 2 is obviously avoided. - -``segments`` - A dict with a ``left`` and a ``right`` lists, consisting of segment - dictionaries. Shell themes may also contain ``above`` list of dictionaries. - Each item in ``above`` list may have ``left`` and ``right`` keys like this - dictionary, but no ``above`` key. - - .. _config-themes-above: - - ``above`` list is used for multiline shell configurations. - - ``left`` and ``right`` lists are used for segments that should be put on the - left or right side in the output. Actual mechanizm of putting segments on - the left or the right depends on used renderer, but most renderers require - one to specify segment with :ref:`width ` ``auto`` - on either side to make generated line fill all of the available width. - - Each segment dictionary has the following options: - - ``type`` - The segment type. Can be one of ``function`` (default), ``string`` - or ``filler``: - - ``function`` - The segment contents is the return value of the function defined - in the :ref:`name option `. - - ``string`` - A static string segment where the contents is defined in the - :ref:`contents option `, and the - highlighting group is defined in the :ref:`highlight_group - option `. - - ``module`` - .. _config-themes-seg-module: - - Function module, only required for function segments. Defaults to - ``powerline.segments.{extension}``. Default is overriden by - :ref:`default_module theme option `. - - ``name`` - .. _config-themes-seg-name: - - Function name, only required for function segments. - - ``highlight_group`` - .. _config-themes-seg-highlight_group: - - Highlighting group for this segment. Consists of a prioritized list - of highlighting groups, where the first highlighting group that is - available in the colorscheme is used. - - Ignored for segments that have ``function`` type. - - ``before`` - .. _config-themes-seg-before: - - A string which will be prepended to the segment contents. - - ``after`` - .. _config-themes-seg-after: - - A string which will be appended to the segment contents. - - ``contents`` - .. _config-themes-seg-contents: - - Segment contents, only required for ``string`` segments. - - ``args`` - .. _config-themes-seg-args: - - A dict of arguments to be passed to a ``function`` segment. - - ``align`` - Aligns the segments contents to the left (``l``), center (``c``) or - right (``r``). - - ``width`` - .. _config-themes-seg-width: - - Enforces a specific width for this segment. - - This segment will work as a spacer if the width is set to ``auto``. - Several spacers may be used, and the space will be distributed - equally among all the spacer segments. Spacers may have contents, - either returned by a function or a static string, and the contents - can be aligned with the ``align`` property. - - ``priority`` - Optional segment priority. Segments with priority ``None`` (the default - priority, represented by ``null`` in json) will always be included, - regardless of the width of the prompt/statusline. - - If the priority is any number, the segment may be removed if the - prompt/statusline width is too small for all the segments to be - rendered. A lower number means that the segment has a higher priority. - - Segments are removed according to their priority, with low priority - segments being removed first. - - ``draw_hard_divider``, ``draw_soft_divider`` - Whether to draw a divider between this and the adjacent segment. The - adjacent segment is to the *right* for segments on the *left* side, and - vice versa. Hard dividers are used between segments with different - background colors, soft ones are used between segments with same - background. Both options default to ``True``. - - ``draw_inner_divider`` - Determines whether inner soft dividers are to be drawn for function - segments. Only applicable for functions returning multiple segments. - Defaults to ``False``. - - ``exclude_modes`` - A list of modes where this segment will be excluded: The segment is - included in all modes, *except* for the modes in this list. - - ``include_modes`` - A list of modes where this segment will be included: The segment is - *not* included in any modes, *except* for the modes in this list. - - ``display`` - .. _config-themes-seg-display: - - Boolean. If false disables displaying of the segment. - Defaults to ``True``. - -Segments -======== - -Segments are written in Python, and the default segments provided with -Powerline are located in :file:`powerline/segments/{extension}.py`. -User-defined segments can be defined in any module in ``sys.path`` or -:ref:`paths common configuration option `, import is -always absolute. - -Segments are regular Python functions, and they may accept arguments. All -arguments should have a default value which will be used for themes that -don't provide an ``args`` dict. - -A segment function must return one of the following values: - -* ``None``, which will remove the segment from the prompt/statusline. -* A string, which will be the segment contents. -* A list of dicts consisting of a ``contents`` string, and - a ``highlight_group`` list. This is useful for providing a particular - highlighting group depending on the segment contents. - -Local configuration -=================== - -Depending on the application used it is possible to override configuration. Here -is the list: - -Vim overrides -------------- - -Vim configuration can be overridden using the following options: - -``g:powerline_config_overrides`` - Dictionary, recursively merged with contents of - :file:`powerline/config.json`. - -``g:powerline_theme_overrides__{theme_name}`` - Dictionary, recursively merged with contents of - :file:`powerline/themes/vim/{theme_name}.json`. Note that this way you can’t - redefine some value (e.g. segment) in list, only the whole list itself: only - dictionaries are merged recursively. - -``g:powerline_config_path`` - Path (must be expanded, ``~`` shortcut is not supported). Points to the - directory which will be searched for configuration. When this option is - present, none of the other locations are searched. - -``g:powerline_no_python_error`` - If this variable is set to a true value it will prevent Powerline from reporting - an error when loaded in a copy of vim without the necessary Python support. - -Powerline script overrides --------------------------- - -Powerline script has a number of options controlling powerline behavior. Here -``VALUE`` always means “some JSON object”. - -``-c KEY.NESTED_KEY=VALUE`` or ``--config=KEY.NESTED_KEY=VALUE`` - Overrides options from :file:`powerline/config.json`. - ``KEY.KEY2.KEY3=VALUE`` is a shortcut for ``KEY={"KEY2": {"KEY3": VALUE}}``. - Multiple options (i.e. ``-c K1=V1 -c K2=V2``) are allowed, result (in the - example: ``{"K1": V1, "K2": V2}``) is recursively merged with the contents - of the file. - - If ``VALUE`` is omitted then corresponding key will be removed from the - configuration (if it was present). - -``-t THEME_NAME.KEY.NESTED_KEY=VALUE`` or ``--theme_option=THEME_NAME.KEY.NESTED_KEY=VALUE`` - Overrides options from :file:`powerline/themes/{ext}/{THEME_NAME}.json`. - ``KEY.NESTED_KEY=VALUE`` is processed like described above, ``{ext}`` is the - first argument to powerline script. May be passed multiple times. - - If ``VALUE`` is omitted then corresponding key will be removed from the - configuration (if it was present). - -``-p PATH`` or ``--config_path=PATH`` - Sets directory where configuration should be read from. If present, no - default locations are searched for configuration. No expansions are - performed by powerline script itself, but ``-p ~/.powerline`` will likely be - expanded by the shell to something like ``-p /home/user/.powerline``. - -Zsh/zpython overrides ---------------------- - -Here overrides are controlled by similarly to the powerline script, but values -are taken from zsh variables. - -``POWERLINE_CONFIG`` - Overrides options from :file:`powerline/config.json`. Should be a zsh - associative array with keys equal to ``KEY.NESTED_KEY`` and values being - JSON strings. Pair ``KEY.KEY1 VALUE`` is equivalent to ``{"KEY": {"KEY1": - VALUE}}``. All pairs are then recursively merged into one dictionary and - this dictionary is recursively merged with the contents of the file. - -``POWERLINE_THEME_CONFIG`` - Overrides options from :file:`powerline/themes/shell/*.json`. Should be - a zsh associative array with keys equal to ``THEME_NAME.KEY.NESTED_KEY`` and - values being JSON strings. Is processed like the above ``POWERLINE_CONFIG``, - but only subdictionaries for ``THEME_NAME`` key are merged with theme - configuration when theme with given name is requested. - -``POWERLINE_CONFIG_PATH`` - Sets directory where configuration should be read from. If present, no - default locations are searched for configuration. No expansions are - performed by powerline script itself, but zsh usually performs them on its - own if you set variable without quotes: ``POWERLINE_CONFIG_PATH=~/example``. - Expansion depends on zsh configuration. - -Ipython overrides ------------------ - -Ipython overrides depend on ipython version. Before ipython-0.11 you should pass -additional keyword arguments to setup() function. After ipython-0.11 you should -use ``c.Powerline.KEY``. Supported ``KEY`` strings or keyword argument names: - -``config_overrides`` - Overrides options from :file:`powerline/config.json`. Should be a dictionary - that will be recursively merged with the contents of the file. - -``theme_overrides`` - Overrides options from :file:`powerline/themes/ipython/*.json`. Should be - a dictionary where keys are theme names and values are dictionaries which - will be recursively merged with the contents of the given theme. - -``path`` - Sets directory where configuration should be read from. If present, no - default locations are searched for configuration. No expansions are - performed thus you cannot use paths starting with ``~/``. - -Prompt command --------------- - -In addition to the above configuration options you can use -``$POWERLINE_COMMAND`` environment variable to tell shell or tmux to use -specific powerline implementation. This is mostly useful for putting powerline -into different directory or replacing ``powerline`` script with -``powerline-client`` for performance reasons. - -.. note:: - - ``$POWERLINE_COMMAND`` appears in shell scripts without quotes thus you can - specify additional parameters in bash. In zsh you will have to make - ``$POWERLINE_COMMAND`` an array parameter to achieve the same result. In - tmux it is passed to ``eval`` and depends on the shell used. - POSIX-compatible shells, zsh, bash and fish will split this variable in this - case. - -If you want to disable prompt in shell, but still have tmux support or if you -want to disable tmux support you can use variables -``$POWERLINE_NO_{SHELL}_PROMPT``/``$POWERLINE_NO_SHELL_PROMPT`` and -``$POWERLINE_NO_{SHELL}_TMUX_SUPPORT``/``$POWERLINE_NO_SHELL_TMUX_SUPPORT`` -(substitute ``{SHELL}`` with the name of the shell (all-caps) you want to -disable support for (e.g. ``BASH``) or use all-inclusive ``SHELL`` that will -disable support for all shells). These variables have no effect after -configuration script was sourced (in fish case: after ``powerline-setup`` -function was run). To disable specific feature support set one of these -variables to some non-empty value. - -If you do not want to disable prompt in shell, but yet do not want to launch -python twice to get :ref:`above ` lines you do not use in -tcsh you should set ``$POWERLINE_NO_TCSH_ABOVE`` or -``$POWERLINE_NO_SHELL_ABOVE`` variable. - -If you do not want to see additional space which is added to the right prompt in -fish in order to support multiline prompt you should set -``$POWERLINE_NO_FISH_ABOVE`` or ``$POWERLINE_NO_SHELL_ABOVE`` variables. - -.. note:: - - Most supported shells’ configuration scripts check for additional - configuration variables being empty. But tcsh configuration script checks - for variables being *defined*, not empty. diff --git a/docs/source/font-installation.rst b/docs/source/font-installation.rst deleted file mode 100644 index d4d6b173..00000000 --- a/docs/source/font-installation.rst +++ /dev/null @@ -1,142 +0,0 @@ -*********************************** -Font installation and configuration -*********************************** - -Powerline uses several special glyphs to get the arrow effect and some -custom symbols for developers. This requires that you either have a symbol -font or a patched font on your system. Your terminal emulator must also -support either patched fonts or fontconfig for Powerline to work properly. - -You can also enable :ref:`24-bit color support ` -if your terminal emulator supports it. - -.. table:: Application/terminal emulator feature support matrix - :name: term-feature-support-matrix - - ===================== ======= =================================== ===================== ===================== ===================== - Name OS Recommended installation method Patched font support Fontconfig support 24-bit color support - ===================== ======= =================================== ===================== ===================== ===================== - Gnome Terminal Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| - Gvim Linux `Patched font`_ |i_yes| |i_no| |i_yes| - Konsole Linux `Fontconfig`_ |i_yes| |i_yes| |i_yes| - lxterminal Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| - rxvt-unicode Linux `rxvt-unicode`_ or `Patched font`_ |i_partial| [#]_ |i_no| |i_no| - st Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| - Xfce Terminal Linux `Fontconfig`_ |i_yes| |i_yes| |i_no| - xterm Linux `Patched font`_ |i_yes| |i_no| |i_partial| [#]_ - iTerm2 OS X `Patched font`_ |i_yes| |i_no| |i_no| - MacVim OS X `Patched font`_ |i_yes| |i_no| |i_yes| - Terminal.app OS X `Patched font`_ |i_yes| |i_no| |i_no| - ===================== ======= =================================== ===================== ===================== ===================== - -.. |i_yes| image:: _static/img/icons/tick.png -.. |i_no| image:: _static/img/icons/cross.png -.. |i_partial| image:: _static/img/icons/error.png - -.. [#] Must be compiled with ``--enable-unicode3`` for the - patched font to work. -.. [#] Uses nearest color from 8-bit palette. - -Fontconfig -========== - -This method only works on Linux. It's the recommended method if your -terminal emulator supports it as you don't have to patch any fonts, and it -generally works well with any coding font. - -#. Download the latest version of the symbol font and fontconfig file:: - - wget https://github.com/Lokaltog/powerline/raw/develop/font/PowerlineSymbols.otf - wget https://github.com/Lokaltog/powerline/raw/develop/font/10-powerline-symbols.conf - -#. Move the symbol font to a valid X font path. Valid font paths can be - listed with ``xset q``:: - - mv PowerlineSymbols.otf ~/.fonts/ - -#. Update font cache for the path you moved the font to (you may need to be - root to update the cache for system-wide paths):: - - fc-cache -vf ~/.fonts/ - -#. Install the fontconfig file. For newer versions of fontconfig the config - path is ``~/.config/fontconfig/conf.d/``, for older versions it's - ``~/.fonts.conf.d/``:: - - mv 10-powerline-symbols.conf ~/.config/fontconfig/conf.d/ - -If you can't see the custom symbols, please close all instances of your -terminal emulator. You may need to restart X for the changes to take -effect. - -If you *still* can't see the custom symbols, double-check that you have -installed the font to a valid X font path, and that you have installed the -fontconfig file to a valid fontconfig path. Alternatively try to install -a `Patched font`_. - -Patched font -============ - -This method is the fallback method and works for every terminal emulator and -OS, with the exception of `rxvt-unicode`_. - -Download the font of your choice from `powerline-fonts`_. If you can't find -your preferred font in the `powerline-fonts`_ repo, you'll have to patch -your own font instead. See :ref:`font-patching` for instructions. - -.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts - -Installation on Linux ---------------------- - -#. Move the patched font to a valid X font path. Valid font paths can be - listed with ``xset q``:: - - mv 'MyFont for Powerline.otf' ~/.fonts/ - -#. Update font cache for the path you moved the font to (you may need to be - root to update the cache for system-wide paths):: - - fc-cache -vf ~/.fonts/ - -After installing the patched font you need to update Gvim or your terminal -emulator to use the patched font. The correct font usually ends with *for -Powerline*. - -If you can't see the custom symbols, please close all instances of your -terminal emulator. You may need to restart X for the changes to take -effect. - -If you *still* can't see the custom symbols, double-check that you have -installed the font to a valid X font path. - -Installation on OS X --------------------- - -Install your patched font by double-clicking the font file in Finder, then -clicking :guilabel:`Install this font` in the preview window. - -After installing the patched font you need to update MacVim or your terminal -emulator to use the patched font. The correct font usually ends with *for -Powerline*. - -Special cases -============= - -rxvt-unicode ------------- - -.. note:: Symbol font support generally doesn't work well in - rxvt-unicode. It's recommended that you either disable the symbols or - switch to a better terminal emulator if you want to use Powerline. - -rxvt-unicode allows using a `Patched font`_ only if it's compiled with the -``--enable-unicode3`` flag. - -For unsupported fonts (e.g. bitmap fonts like Terminus), you can't use -``PowerlineSymbols.otf`` as a fallback since rxvt-unicode has trouble -determining the font's metrics. A solution to this issue is to get -a `Patched font`_ and add this as a fallback font to your -``.Xresources``/``.Xdefaults``:: - - URxvt*font: xft:Terminus:pixelsize=12,xft:Inconsolata\ for\ Powerline:pixelsize=12 diff --git a/docs/source/fontpatching.rst b/docs/source/fontpatching.rst deleted file mode 100644 index fcba993a..00000000 --- a/docs/source/fontpatching.rst +++ /dev/null @@ -1,136 +0,0 @@ -.. _font-patching: - -************* -Font patching -************* - -Powerline provides a font patcher for custom glyphs like the segment -dividers (arrows), branch symbol, padlock symbol, etc. The font patcher -requires FontForge with Python bindings to work. - -Check out the `powerline-fonts -`_ repository on GitHub for -patched versions of some popular programming fonts. - -.. warning:: The code points have changed in this version of Powerline! This - means that you either have to patch your font again, or change the glyphs - Powerline uses in your user configuration. - -.. note:: Powerline no longer works with rxvt-unicode unless you either use - rxvt-unicode compiled with ``--enable-unicode3``, or you use fonts patched - with the legacy font patcher and change the glyphs in your user - configuration. - -Glyph table -=========== - -Powerline stores all special glyphs in the Unicode *Private Use Area* -(``U+E000``-``U+F8FF``). - -========== ===== =========== -Code point Glyph Description -========== ===== =========== -``U+E0A0``  Version control branch -``U+E0A1``  LN (line) symbol -``U+E0A2``  Closed padlock -``U+E0B0``  Rightwards black arrowhead -``U+E0B1``  Rightwards arrowhead -``U+E0B2``  Leftwards black arrowhead -``U+E0B3``  Leftwards arrowhead -========== ===== =========== - -Usage -===== - -The font patcher, :file:`fontpatcher.py` is located in the `powerline-fontpatcher -`_ repository on GitHub. -It requires Python 2.7 and FontForge compiled with Python bindings to work. - -Patched fonts are renamed by default (" for Powerline" is added to the font -name) so they don't conflict with existing fonts. Use the ``--no-rename`` -option to disable font renaming. - -.. note:: Bitmap fonts are not supported, and will probably never be - supported officially due to difficulty creating a font patcher that works - for bitmap fonts. The recommended method of patching bitmap fonts is to draw - the glyphs manually using a tool like ``gbdfed``. - -Linux ------ - -1. Install fontforge with Python bindings. For Ubuntu users the required - package is ``python-fontforge``, for Arch Linux users the required - package is ``fontforge``. It should be something similar for other - distros. - -2. Run the font patcher:: - - $ /path/to/fontpatcher.py MyFontFile.ttf - -3. Copy the font file into :file:`~/.fonts` (or another X font directory):: - - $ cp "MyFontFile for Powerline.otf" ~/.fonts - -4. Update your font cache:: - - $ fc-cache -vf ~/.fonts - - If you're using vim in a terminal you may need to close all open terminal - windows after updating the font cache. - -5. **Gvim users:** Update the GUI font in your :file:`vimrc` file:: - - set guifont=MyFont\ for\ Powerline - - **Terminal users:** Update your terminal configuration to use the patched - font. - -6. Open vim and enjoy your new statusline! - -OS X ----- - -1. Check if you have a FontForge version with Python support by running - ``fontforge -version``. You should see something like this:: - - $ fontforge -version - Copyright (c) 2000-2011 by George Williams. - Executable based on sources from 13:48 GMT 22-Feb-2011-D. - Library based on sources from 13:48 GMT 22-Feb-2011. - fontforge 20110222 - libfontforge 20110222 - - Make sure that the executable version number doesn't have ``NoPython`` in - it. If everything looks OK, skip ahead to step 4. - -2. If you have FontForge but with ``NoPython`` in the version number, please - try to update to a later version:: - - $ brew uninstall fontforge - $ brew update - $ brew install fontforge - - **Note:** You may have to use ``--use-clang`` instead of ``--use-gcc`` - when compiling FontForge. - -3. If you don't have FontForge, install it with Homebrew:: - - $ brew update - $ brew install fontforge - -4. Patch your fonts by passing the ``fontpatcher`` script as a parameter to - FontForge:: - - $ fontforge -script /path/to/fontpatcher.py MyFontFile.ttf - -5. Install the font by double-clicking the font file in Finder and click - "Install this font" from the preview window. - -6. **Gvim users:** Update the GUI font in your :file:`vimrc` file:: - - set guifont=MyFont\ for\ Powerline - - **Terminal users:** Update your terminal configuration to use the patched - font. - -7. Open vim and enjoy your new statusline! diff --git a/docs/source/index.rst b/docs/source/index.rst index 2afd8241..5a502249 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,20 +6,13 @@ Powerline :maxdepth: 2 :glob: - introduction overview - configuration - tipstricks - fontpatching - license-credits - -Segments -======== - -.. toctree:: - segments/common - segments/shell - segments/vim + installation + usage + configuration-and-customization + troubleshooting + tips-and-tricks + license-and-credits Indices and tables ================== diff --git a/docs/source/installation.rst b/docs/source/installation.rst new file mode 100644 index 00000000..3696e7c0 --- /dev/null +++ b/docs/source/installation.rst @@ -0,0 +1,8 @@ +************ +Installation +************ + +.. toctree:: + + Linux + OS X diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index e8ff6d72..377770a3 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -1,106 +1,9 @@ -:tocdepth: 2 - -.. _installation-linux: - ********************* Installation on Linux ********************* -The following distribution-specific packages are officially supported, and -they provide an easy way of installing and upgrading Powerline. The packages -will automatically do most of the configuration for you, but you should -still skim through this guide so you know how the plugin works. - -* `Arch Linux (AUR), Python 2 version `_ -* `Arch Linux (AUR), Python 3 version `_ -* Gentoo Live ebuild in `raiagent `_ overlay - -If you're running a distribution without an official package you'll have to -follow the installation guide below: - -Plugin installation -=================== - -1. Install Python 3.2+ or Python 2.6+. -2. Install Powerline using the following command:: - - pip install --user git+git://github.com/Lokaltog/powerline - -.. note:: You need to use the GitHub URI when installing Powerline! This - project is currently unavailable on the PyPI due to a naming conflict - with an unrelated project. - -.. note:: If you are powerline developer you should be aware that ``pip install - --editable`` does not currently fully work. If you - install powerline this way you will be missing ``powerline`` executable and - need to symlink it. It will be located in ``scripts/powerline``. - -Font installation -================= - -Powerline provides two ways of installing the required fonts on Linux. The -recommended method is using ``fontconfig`` if your terminal emulator -supports it. See the :ref:`term-feature-support-matrix` for details about -what features your terminal emulator supports. - -Fontconfig ----------- - -1. Download the `latest version of PowerlineSymbols - `_ - and the `latest version of the fontconfig file - `_. -2. Move :file:`PowerlineSymbols.otf` to :file:`~/.fonts/` (or another X font - directory). -3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. -4. Move :file:`10-powerline-symbols.conf` to either :file:`~/.fonts.conf.d/` - or :file:`~/.config/fontconfig/conf.d/`, depending on your fontconfig - version. -5. If you don't see the arrow symbols, please close all instances of your - terminal emulator or gvim. You may also have to restart X for the changes - to take effect. If you *still* don't see the arrow symbols, please submit - an issue on GitHub. - -Patched font +Requirements ------------ -1. Download the font of your choice from `powerline-fonts`_. If you can't - find your preferred font in the `powerline-fonts`_ repo, you'll have to - patch your own font instead. See :ref:`font-patching` for instructions. -2. Move your patched font to :file:`~/.fonts/` (or another X font - directory). -3. Run ``fc-cache -vf ~/.fonts`` to update your font cache. -4. Update Gvim or your terminal emulator to use the patched font. (the - correct font usually ends with *for Powerline*). -5. If you don't see the arrow symbols, please close all instances of your - terminal emulator or gvim. You may also have to restart X for the changes - to take effect. If you *still* don't see the arrow symbols, please submit - an issue on GitHub. - -.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts - -Troubleshooting -=============== - -.. contents:: - :local: - -I can't see any fancy symbols, what's wrong? --------------------------------------------- - -* Make sure that you've configured gvim or your terminal emulator to use - a patched font (see :ref:`font-patching`). -* You need to set your ``LANG`` and ``LC_*`` environment variables to - a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's - documentation for information about setting these variables correctly. -* Make sure that vim is compiled with the ``--with-features=big`` flag. -* If you're using rxvt-unicode, make sure that it's compiled with the - ``--enable-unicode3`` flag. - -The fancy symbols look a bit blurry or "off"! ---------------------------------------------- - -* Make sure that you have patched all variants of your font (i.e. both the - regular and the bold font files). - -.. include:: troubleshooting-common.rst +Installation +------------ diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index c991eac7..b44f5529 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -1,125 +1,9 @@ -:tocdepth: 2 - -.. _installation-osx: - ******************** Installation on OS X ******************** -Plugin installation -=================== +Requirements +------------ -Python package --------------- - -1. Install a proper Python version (see `issue #39 - `_ for a discussion - regarding the required Python version on OS X):: - - sudo port select python python27-apple - -2. Install Powerline using the following command:: - - pip install --user git+git://github.com/Lokaltog/powerline - -.. note:: You need to use the GitHub URI when installing Powerline! This - project is currently unavailable on the PyPI due to a naming conflict - with an unrelated project. - -.. note:: If you are powerline developer you should be aware that ``pip install - --editable`` does not currently fully work. If you - install powerline this way you will be missing ``powerline`` executable and - need to symlink it. It will be located in ``scripts/powerline``. - -Vim installation ----------------- - -Any terminal vim version with Python 3.2+ or Python 2.6+ support should work, -but if you're using MacVim you need to install it using the following command:: - - brew install macvim --env-std --override-system-vim - -Font installation -================= - -You need a patched font for Powerline to work on OS X. Check out the -`powerline-fonts`_ repository on GitHub for patched versions of some popular -programming fonts. - -1. Download the font of your choice and install it by double-clicking the - font file in Finder and then click :guilabel:`Install this font` in the - preview window. - - If you can't find your preferred font in the `powerline-fonts`_ repo, - you'll have to patch your own font instead. See :ref:`font-patching` for - instructions. -2. Configure MacVim or your terminal emulator to use the patched font (the - correct font usually ends with *for Powerline*). - -.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts - -Troubleshooting -=============== - -.. contents:: - :local: - -I can't see any fancy symbols, what's wrong? --------------------------------------------- - -* If you're using iTerm2, please update to `this revision - `_ - or newer. -* You need to set your ``LANG`` and ``LC_*`` environment variables to - a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's - documentation for information about setting these variables correctly. - -The colors look weird in the default OS X Terminal app! -------------------------------------------------------- - -* The arrows may have the wrong colors if you have changed the "minimum - contrast" slider in the color tab of your OS X settings. -* The default OS X Terminal app is known to have some issues with the - Powerline colors. Please use another terminal emulator. iTerm2 should work - fine. - -The colors look weird in iTerm2! --------------------------------- - -* The arrows may have the wrong colors if you have changed the "minimum - contrast" slider in the color tab of your OS X settings. -* Please disable background transparency to resolve this issue. - -Statusline is getting wrapped to the next line in iTerm2 --------------------------------------------------------- - -* Turn off “Treat ambigious-width characters as double width” in `Preferences - --> Text`. -* Alternative: remove fancy dividers (they suck in this case), set - :ref:`ambiwidth ` to 2. - -I receive a ``NameError`` when trying to use Powerline with MacVim! -------------------------------------------------------------------- - -* Please install MacVim using this command:: - - brew install macvim --env-std --override-system-vim - - Then install Powerline locally with ``pip install --user``, or by - running these commands in the ``powerline`` directory:: - - ./setup.py build - ./setup.py install --user - -I receive an ``ImportError`` when trying to use Powerline on OS X! ------------------------------------------------------------------- - -* This is caused by an invalid ``sys.path`` when using system vim and system - Python. Please try to select another Python distribution:: - - sudo port select python python27-apple - -* See `issue #39 `_ for - a discussion and other possible solutions for this issue. - -.. include:: troubleshooting-common.rst +Installation +------------ diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst deleted file mode 100644 index 55345394..00000000 --- a/docs/source/installation/troubleshooting-common.rst +++ /dev/null @@ -1,136 +0,0 @@ -I'm using tmux and Powerline looks like crap, what's wrong? ------------------------------------------------------------ - -* You need to tell tmux that it has 256-color capabilities. Add this to your - :file:`.tmux.conf` to solve this issue:: - - set -g default-terminal "screen-256color" - -* If you're using iTerm2, make sure that you have enabled the setting - :guilabel:`Set locale variables automatically` in :menuselection:`Profiles - --> Terminal --> Environment`. - -I’m using tmux/screen and Powerline is colorless ------------------------------------------------- - -* If the above advices do not help, then you need to disable - :ref:`term_truecolor `. -* Alternative: set :ref:`additional_escapes ` - to ``"tmux"`` or ``"screen"``. Note that it is known to work perfectly in - screen, but in tmux it may produce ugly spaces. - - -After an update something stopped working ------------------------------------------ - -Assuming powerline was working before update and stopped only after there are -two possible explanations: - -* You have more then one powerline installation (e.g. ``pip`` and ``Vundle`` - installations) and you have updated only one. -* Update brought some bug to powerline. - -In the second case you, of course, should report the bug to `powerline bug -tracker `_. In the first you should make -sure you either have only one powerline installation or you update all of them -simultaneously (beware that in the second case you are not supported). To -diagnose this problem you may do the following: - -#) If this problem is observed within the shell make sure that - - .. code-block:: shell - - python -c 'import powerline; print (powerline.__file__)' - - which should report something like - :file:`/usr/lib64/python2.7/site-packages/powerline/__init__.pyc` (if - powerline is installed system-wide) or - :file:`/home/USER/.../powerline/__init__.pyc` (if powerline was cloned - somewhere, e.g. in :file:`/home/USER/.vim/bundle/powerline`) reports the same - location you use to source in your shell configuration: in first case it - should be some location in :file:`/usr` (e.g. - :file:`/usr/share/zsh/site-contrib/powerline.zsh`), in the second it should - be something like - :file:`/home/USER/.../powerline/bindings/zsh/powerline.zsh`. If this is true - it may be a powerline bug, but if locations do not match you should not - report the bug until you observe it on configuration where locations do - match. -#) If this problem is observed within the vim instance you should check out the - output of the following Ex mode commands - - .. code-block:: vim - - python import powerline as pl ; print (pl.__file__) - python3 import powerline as pl ; print (pl.__file__) - - One (but not both) of them will most likely error out, this is OK. The same - rules apply as in the 1), but in place of sourcing you should seek for the - place where you modify `runtimepath` vim option. If you install powerline - using `VAM `_ then no - explicit modifications of runtimpath were performed in your vimrc - (runtimepath is modified by VAM in this case), but powerline will be placed - in :file:`{plugin_root_dir}/powerline` where `{plugin_root_dir}` is stored in - VAM settings dictionary: do `echo g:vim_addon_manager.plugin_root_dir`. - -There is a hint if you want to place powerline repository somewhere, but still -make powerline package importable anywhere: use - - .. code-block:: shell - - pip install --user --editable path/to/powerline - -My vim statusline has strange characters like ``^B`` in it! ------------------------------------------------------------ - -* Please add ``set encoding=utf-8`` to your :file:`vimrc`. - -My vim statusline has a lot of ``^`` or underline characters in it! -------------------------------------------------------------------- - -* You need to configure the ``fillchars`` setting to disable statusline - fillchars (see ``:h fillchars`` for details). Add this to your - :file:`vimrc` to solve this issue: - - .. code-block:: vim - - set fillchars+=stl:\ ,stlnc:\ - -My vim statusline is hidden/only appears in split windows! ----------------------------------------------------------- - -* Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. - -My vim statusline is not displayed completely and has too much spaces ---------------------------------------------------------------------- - -* Be sure you have ``ambiwidth`` option set to ``single``. -* Alternative: set :ref:`ambiwidth ` to 2, remove fancy - dividers (they suck when ``ambiwidth`` is set to double). - -When using z powerline shows wrong number of jobs -------------------------------------------------- - -This happens because `z `_ is launching some jobs in -the background from ``$POWERLINE_COMMAND`` and these jobs fail to finish before -powerline prompt is run. - -Solution to this problem is simple: be sure that :file:`z.sh` is sourced -strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background -jobs are spawned by `z `_ after powerline has done -its job. - -I am suffering bad lags before displaying shell prompt ------------------------------------------------------- - -To get rid of these lags there currently are two options: - -* Run ``powerline-daemon``. Powerline does not automatically start it for you. -* Compile and install ``libzpython`` module that lives in - https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. - -Prompt is spoiled after completing files in ksh ------------------------------------------------ - -This is exactly why powerline has official mksh support, but not official ksh -support. If you know the solution feel free to share it in `powerline bug -tracker `_. diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst deleted file mode 100644 index f030aec3..00000000 --- a/docs/source/introduction.rst +++ /dev/null @@ -1,65 +0,0 @@ -************ -Introduction -************ - -This is the next version of Powerline, implemented in Python. It aims to -resolve some of the "unresolvable" problems of the vimscript implementation, -as well as providing a common code base for all projects that use Powerline -in some way (e.g. shell prompts and tmux themes). - -The project is currently in beta, and most of the functionality in the old -vimscript project is already implemented. - -Screenshots -=========== - -**Mode-dependent highlighting** - -* .. image:: _static/img/pl-mode-normal.png - :alt: Normal mode -* .. image:: _static/img/pl-mode-insert.png - :alt: Insert mode -* .. image:: _static/img/pl-mode-visual.png - :alt: Visual mode -* .. image:: _static/img/pl-mode-replace.png - :alt: Replace mode - -**Automatic truncation of segments in small windows** - -* .. image:: _static/img/pl-truncate1.png - :alt: Truncation illustration -* .. image:: _static/img/pl-truncate2.png - :alt: Truncation illustration -* .. image:: _static/img/pl-truncate3.png - :alt: Truncation illustration - -The font in the screenshots is `Pragmata Pro`_ by Fabrizio Schiavi. - -.. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm - -Feature highlights -================== - -* **Better performance.** Python performs quite a bit better than vimscript, - and by having most of the code in Python instead of vimscript it's also - much easier to profile the code and eliminate bottlenecks. -* **A much leaner code base.** With most of the functionality of the old - project implemented the new version consists of less than half the amount - of code. -* **Automatic removal of less important segments in small windows.** Not all - information is equally important to have in the statusline, and segments - with e.g. encoding and file format information are automatically removed - in smaller windows. -* **Dynamic statusline evaluation in Python.** Statuslines are dynamically - rendered in Python instead of relying on vim's statusline flags, which - allows much more flexibility when creating statuslines. -* **The possibility of adding more segments.** Because of vim's hardcoded - limitation of 80 statusline options, the vimscript implementation - triggered an error when adding more segments to the default theme. Since - segment contents are now rendered as plain text in Python it's possible to - add many more segments in the statusline before reaching this limit. -* **New and improved theme and colorscheme syntax.** Themes and colorschemes - are now written in JSON, with a much cleaner syntax that's easier to learn - and work with. Themes and colorschemes are also much more configurable, - and it's easy to write your own and store them in your home config - directory. diff --git a/docs/source/license-credits.rst b/docs/source/license-and-credits.rst similarity index 76% rename from docs/source/license-credits.rst rename to docs/source/license-and-credits.rst index 8fafc92c..bdd7ede8 100644 --- a/docs/source/license-credits.rst +++ b/docs/source/license-and-credits.rst @@ -2,28 +2,22 @@ License and credits ******************* -License -======= - -Powerline is licensed under the `MIT license +Powerline is licensed under the `MIT license `_. -Credits -======= - Authors ------- * `Kim Silkebækken `_ -* `ZyX-I `_ +* `Nikolay Pavlov `_ * `Kovid Goyal `_ Contributors ------------ -* `List of contributors +* `List of contributors `_ -* The glyphs in the font patcher are created by Fabrizio Schiavi, creator of +* The glyphs in the font patcher are created by Fabrizio Schiavi, creator of the excellent coding font `Pragmata Pro`_. .. _`Pragmata Pro`: http://www.fsd.it/fonts/pragmatapro.htm diff --git a/docs/source/overview.rst b/docs/source/overview.rst index f50940a6..ed2abefd 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -2,332 +2,66 @@ Overview ******** -Requirements -============ +**Powerline is a statusline plugin for vim, and provides statuslines and +prompts for several other applications, including zsh, bash, tmux, IPython, +Awesome and Qtile.** -Powerline requires Python 3.3 or Python 2.7 to work. +Features +-------- -Vim plugin requirements ------------------------ +* **Extensible and feature rich, written in Python.** Powerline was + completely rewritten in Python to get rid of as much vimscript as + possible. This has allowed much better extensibility, leaner and better + config files, and a structured, object-oriented codebase with no mandatory + third-party dependencies other than a Python interpreter. +* **Stable and testable code base.** Using Python has allowed unit testing + of all the project code. The code is tested to work in Python 2.6+ and + Python 3. +* **Support for prompts and statuslines in many applications.** Originally + created exclusively for vim statuslines, the project has evolved to + provide statuslines in tmux and several WMs, and prompts for shells like + bash/zsh and other applications. It's simple to write renderers for any + other applications that Powerline doesn't yet support. +* **Configuration and colorschemes written in JSON.** JSON is + a standardized, simple and easy to use file format that allows for easy + user configuration across all of Powerline's supported applications. +* **Fast and lightweight, with daemon support for even better performance.** + Although the code base spans a couple of thousand lines of code with no + goal of "less than X lines of code", the main focus is on good performance + and as little code as possible while still providing a rich set of + features. The new daemon also ensures that only one Python instance is + launched for prompts and statuslines, which provides excellent + performance. -The vim plugin requires a vim version with Python support compiled in. You -can check if your vim supports Python by running ``vim --version | grep -+python``. +*But I hate Python / I don't need shell prompts / this is just too much +hassle for me / what happened to the original vim-powerline project / …* -If your vim version doesn't have support for Python, you'll have to compile -it with the ``--enable-pythoninterp`` flag (``--enable-python3interp`` if -you want Python 3 support instead). Note that this also requires the related -Python headers to be installed on your system. Please consult your -distribution's documentation for details on how to compile and install -packages. +You should check out some of the Powerline derivatives. The most lightweight +and feature-rich alternative is currently Bailey Ling's `vim-airline +`_ project. -Vim version 7.3.661 or newer is recommended for performance reasons. - -Terminal emulator requirements ------------------------------- - -Powerline uses several special glyphs to get the arrow effect and some -custom symbols for developers. This requires that you either have a symbol -font or a patched font on your system. Your terminal emulator must also -support either patched fonts or fontconfig for Powerline to work properly. - -You can also enable :ref:`24-bit color support ` -if your terminal emulator supports it. - -.. table:: Application/terminal emulator feature support matrix - :name: term-feature-support-matrix - - ===================== ======= ===================== ===================== ===================== - Name OS Patched font support Fontconfig support 24-bit color support - ===================== ======= ===================== ===================== ===================== - Gnome Terminal Linux |i_yes| |i_yes| |i_no| - Gvim Linux |i_yes| |i_no| |i_yes| - iTerm2 OS X |i_yes| |i_no| |i_no| - Konsole Linux |i_yes| |i_yes| |i_yes| - lxterminal Linux |i_yes| |i_yes| |i_no| - MacVim OS X |i_yes| |i_no| |i_yes| - rxvt-unicode Linux |i_partial| [#]_ |i_no| |i_no| - st Linux |i_yes| |i_yes| |i_no| - Terminal.app OS X |i_yes| |i_no| |i_no| - Xfce Terminal Linux |i_yes| |i_yes| |i_no| - xterm Linux |i_yes| |i_no| |i_partial| [#]_ - ===================== ======= ===================== ===================== ===================== - -.. |i_yes| image:: _static/img/icons/tick.png -.. |i_no| image:: _static/img/icons/cross.png -.. |i_partial| image:: _static/img/icons/error.png - -.. [#] Must be compiled with ``--enable-unicode3`` for the - patched font to work. -.. [#] Uses nearest color from 8-bit palette. - -Optional dependencies ---------------------- - -The following software is not required by all segments, but recommended for -optimal performance and extra features: - -Python packages -^^^^^^^^^^^^^^^ - -* ``pygit2`` -* ``mercurial`` -* ``psutil`` -* ``i3-py``, `available on github `_. Only used with i3wm bindings and segments. - -Other applications -^^^^^^^^^^^^^^^^^^ - -* ``git`` version 1.7.2 and later. Not needed if you have ``pygit2``. - -Installation -============ - -.. note:: This project is currently unavailable from PyPI due to a naming conflict - with an unrelated project. Please read the detailed instructions for your platform - below. - -* :ref:`installation-linux` -* :ref:`installation-osx` - -Usage -===== - -.. _vim-vimrc: +Screenshots +----------- Vim statusline --------------- - -If installed using pip just add - -.. code-block:: vim - - python from powerline.vim import setup as powerline_setup - python powerline_setup() - python del powerline_setup - -(replace ``python`` with ``python3`` if appropriate) to your :file:`vimrc`. - -If you just cloned the repository add the following line to your :file:`vimrc`, -where ``{repository_root}`` is the absolute path to your Powerline installation -directory: - -.. code-block:: vim - - set rtp+={repository_root}/powerline/bindings/vim - -If you're using Vundle or Pathogen and don't want Powerline functionality in -any other applications, simply add Powerline as a bundle and point the path -above to the Powerline bundle directory, e.g. -``~/.vim/bundle/powerline/powerline/bindings/vim``. For vim-addon-manager it is -even easier since you don’t need to write this big path or install anything by -hand: ``powerline`` is installed and run just like any other plugin using - -.. code-block:: vim - - call vam#ActivateAddons(['powerline']) - -.. note:: - If you use supplied :file:`powerline.vim` file to load powerline there are - additional configuration variables available: ``g:powerline_pycmd`` and - ``g:powerline_pyeval``. First sets command used to load powerline: expected - values are ``"py"`` and ``"py3"``. Second sets function used in statusline, - expected values are ``"pyeval"`` and ``"py3eval"``. - - If ``g:powerline_pycmd`` is set to the one of the expected values then - ``g:powerline_pyeval`` will be set accordingly. If it is set to some other - value then you must also set ``g:powerline_pyeval``. Powerline will not - check that Vim is compiled with Python support if you set - ``g:powerline_pycmd`` to an unexpected value. - - These values are to be used to specify the only Python that is to be loaded - if you have both versions: Vim may disable loading one python version if - other was already loaded. They should also be used if you have two python - versions able to load simultaneously, but with powerline installed only for - python-3 version. - -Shell prompts -------------- - -.. note:: - It is advised that you run ``powerline-daemon`` before using any of the - below solutions. To do this add - - .. code-block:: bash - - powerline-daemon -q - - just before sourcing powerline bindings script or running - ``powerline-setup``. Use ``|| true`` or equivalent if you run your - configuration with ``set -e`` because ``powerline-daemon`` will exit with - ``1`` if daemon is already running. - -Bash prompt -^^^^^^^^^^^ - -Add the following line to your :file:`bashrc`, where ``{repository_root}`` is -the absolute path to your Powerline installation directory: - -.. code-block:: bash - - . {repository_root}/powerline/bindings/bash/powerline.sh - -Zsh prompt -^^^^^^^^^^ - -Add the following line to your :file:`zshrc`, where ``{repository_root}`` is the -absolute path to your Powerline installation directory: - -.. code-block:: bash - - . {repository_root}/powerline/bindings/zsh/powerline.zsh - -Fish prompt -^^^^^^^^^^^ - -Add the following line to your :file:`config.fish`, where ``{repository_root}`` -is the absolute path to your Powerline installation directory: - -.. code-block:: bash - - set fish_function_path $fish_function_path "{repository_root}/powerline/bindings/fish" - powerline-setup - -.. _tmux-statusline: - -Busybox (ash), mksh and dash prompt -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -After launching busybox run the following command: - -.. code-block:: bash - - . {repository_root}/powerline/bindings/shell/powerline.sh - -Mksh users may put this line into ``~/.mkshrc`` file. Dash users may use the -following in ``~/.profile``: - -.. code-block:: bash - - if test "x$0" != "x${0#dash}" ; then - export ENV={repository_root}/powerline/bindings/shell/powerline.sh - fi - -.. note:: - Dash users that already have ``$ENV`` defined should either put the ``. - …/shell/powerline.sh`` line in the ``$ENV`` file or create a new file which - will source (using ``.`` command) both former ``$ENV`` file and - :file:`powerline.sh` files and set ``$ENV`` to the path of this new file. - -.. warning:: - Job count is using some weird hack that uses signals and temporary files for - interprocess communication. It may be wrong sometimes. Not the case in mksh. - -.. warning:: - Busybox has two shells: ``ash`` and ``hush``. Second is known to segfault in - busybox 1.22.1 when using :file:`powerline.sh` script. - -Tmux statusline ---------------- - -Add the following lines to your :file:`.tmux.conf`, where ``{repository_root}`` -is the absolute path to your Powerline installation directory:: - - source "{repository_root}/powerline/bindings/tmux/powerline.conf" - -.. note:: - The availability of the ``powerline-config`` command is required for - powerline support. You may specify location of this script via - ``$POWERLINE_CONFIG_COMMAND`` environment variable. - -.. note:: - It is advised that you run ``powerline-daemon`` before adding the above line - to tmux.conf. To do so add:: - - run-shell "powerline-daemon -q" - - to :file:`.tmux.conf`. - -IPython prompt --------------- - -For IPython<0.11 add the following lines to your -:file:`.ipython/ipy_user_conf.py`:: - - # top - from powerline.bindings.ipython.pre_0_11 import setup as powerline_setup - - # main() function (assuming you launched ipython without configuration to - # create skeleton ipy_user_conf.py file): - powerline_setup() - -For IPython>=0.11 add the following line to your :file:`ipython_config.py` -file in the profile you are using:: - - c.InteractiveShellApp.extensions = [ - 'powerline.bindings.ipython.post_0_11' - ] - -IPython=0.11* is not supported and does not work. IPython<0.10 was not -tested (not installable by pip). - -Awesome widget --------------- - -.. note:: Powerline currently only supports awesome 3.5. - -.. note:: The Powerline widget will spawn a shell script that runs in the - background and updates the statusline with ``awesome-client``. - -Add the following to your :file:`rc.lua`, where ``{repository_root}`` is the -absolute path to your Powerline installation directory: - -.. code-block:: lua - - package.path = package.path .. ';{repository_root}/powerline/bindings/awesome/?.lua' - require('powerline') - -Then add the ``powerline_widget`` to your ``wibox``: - -.. code-block:: lua - - right_layout:add(powerline_widget) - -Qtile widget ------------- - -Add the following to your :file:`~/.config/qtile/config.py`: - -.. code-block:: python - - from powerline.bindings.qtile.widget import Powerline - - screens = [ - Screen( - top=bar.Bar([ - # ... - Powerline(timeout=2), - # ... - ], - ), - ), - ] - -I3 bar ------- - -.. note:: Until the patch is done in i3, you will need a custom ``i3bar`` build - called ``i3bgbar``. The source is available `here - `_. - -Add the following to your :file:`~/.i3/config`:: - - bar { - i3bar_command i3bgbar - - status_command python /path/to/powerline/bindings/i3/powerline-i3.py - font pango:PowerlineFont 12 - } - -where ``i3bgbar`` may be replaced with the path to the custom i3bar binary and -``PowerlineFont`` is any system font with powerline support. +^^^^^^^^^^^^^^ + +**Mode-dependent highlighting** + +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-normal.png + :alt: Normal mode +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-insert.png + :alt: Insert mode +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-visual.png + :alt: Visual mode +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-replace.png + :alt: Replace mode + +**Automatic truncation of segments in small windows** + +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate1.png + :alt: Truncation illustration +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate2.png + :alt: Truncation illustration +* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate3.png + :alt: Truncation illustration diff --git a/docs/source/segment-reference.rst b/docs/source/segment-reference.rst new file mode 100644 index 00000000..83b20b12 --- /dev/null +++ b/docs/source/segment-reference.rst @@ -0,0 +1,8 @@ +***************** +Segment reference +***************** + +.. toctree:: + :glob: + + segment-reference/* diff --git a/docs/source/segments/common.rst b/docs/source/segment-reference/common.rst similarity index 100% rename from docs/source/segments/common.rst rename to docs/source/segment-reference/common.rst diff --git a/docs/source/segments/shell.rst b/docs/source/segment-reference/shell.rst similarity index 100% rename from docs/source/segments/shell.rst rename to docs/source/segment-reference/shell.rst diff --git a/docs/source/segments/vim.rst b/docs/source/segment-reference/vim.rst similarity index 100% rename from docs/source/segments/vim.rst rename to docs/source/segment-reference/vim.rst diff --git a/docs/source/tipstricks.rst b/docs/source/tips-and-tricks.rst similarity index 93% rename from docs/source/tipstricks.rst rename to docs/source/tips-and-tricks.rst index 91cbb6ee..59db85cd 100644 --- a/docs/source/tipstricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -1,6 +1,6 @@ -************* -Tips & Tricks -************* +*************** +Tips and tricks +*************** Vim === @@ -8,10 +8,10 @@ Vim Fix terminal timeout when pressing escape ----------------------------------------- -When you're pressing :kbd:`Escape` to leave insert mode in the terminal, it -will by default take a second or another keystroke to leave insert mode -completely and update the statusline. If you find this annoying, you can add -the following snippet to your :file:`vimrc` to escape insert mode +When you're pressing :kbd:`Escape` to leave insert mode in the terminal, it +will by default take a second or another keystroke to leave insert mode +completely and update the statusline. If you find this annoying, you can add +the following snippet to your :file:`vimrc` to escape insert mode immediately: .. code-block:: vim @@ -28,11 +28,11 @@ immediately: Useful settings --------------- -You may find the following vim settings useful when using the Powerline +You may find the following vim settings useful when using the Powerline statusline: .. code-block:: vim - + set laststatus=2 " Always display the statusline in all windows set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst new file mode 100644 index 00000000..31c5fc80 --- /dev/null +++ b/docs/source/troubleshooting.rst @@ -0,0 +1,8 @@ +*************** +Troubleshooting +*************** + +.. toctree:: + + Linux + OS X diff --git a/docs/source/troubleshooting/linux.rst b/docs/source/troubleshooting/linux.rst new file mode 100644 index 00000000..70bd739d --- /dev/null +++ b/docs/source/troubleshooting/linux.rst @@ -0,0 +1,3 @@ +************************ +Troubleshooting on Linux +************************ diff --git a/docs/source/troubleshooting/osx.rst b/docs/source/troubleshooting/osx.rst new file mode 100644 index 00000000..e4fd024b --- /dev/null +++ b/docs/source/troubleshooting/osx.rst @@ -0,0 +1,3 @@ +*********************** +Troubleshooting on OS X +*********************** diff --git a/docs/source/usage.rst b/docs/source/usage.rst new file mode 100644 index 00000000..a03340bf --- /dev/null +++ b/docs/source/usage.rst @@ -0,0 +1,15 @@ +***** +Usage +***** + +Application support matrix +-------------------------- + +Plugins +------- + +.. toctree:: + + usage/shell-prompts + usage/wm-widgets + usage/other diff --git a/docs/source/usage/other.rst b/docs/source/usage/other.rst new file mode 100644 index 00000000..40e3b686 --- /dev/null +++ b/docs/source/usage/other.rst @@ -0,0 +1,6 @@ +************* +Other plugins +************* + +tmux statusline +--------------- diff --git a/docs/source/usage/shell-prompts.rst b/docs/source/usage/shell-prompts.rst new file mode 100644 index 00000000..56da391c --- /dev/null +++ b/docs/source/usage/shell-prompts.rst @@ -0,0 +1,15 @@ +************* +Shell prompts +************* + +bash +---- + +zsh +--- + +fish +---- + +IPython +------- diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst new file mode 100644 index 00000000..f661199d --- /dev/null +++ b/docs/source/usage/wm-widgets.rst @@ -0,0 +1,9 @@ +********************** +Window manager widgets +********************** + +Awesome +------- + +Qtile +----- From c043fa5e221ddee659b38e363fc259f511c0f9c2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 2 Aug 2014 23:51:20 +0400 Subject: [PATCH 1119/1472] Copy documentation from old variant Note: fontpatcher documentation was removed. Closes #632 Closes #769 --- .../configuration-and-customization.rst | 15 - docs/source/configuration-reference.rst | 3 - docs/source/configuration.rst | 112 ++++++ docs/source/configuration/local.rst | 154 ++++++++ docs/source/configuration/reference.rst | 367 ++++++++++++++++++ docs/source/configuration/segments.rst | 32 ++ .../segments}/common.rst | 0 .../segments}/shell.rst | 0 .../segments}/vim.rst | 0 docs/source/index.rst | 2 +- docs/source/installation.rst | 47 +++ docs/source/installation/linux.rst | 28 +- docs/source/installation/osx.rst | 41 +- docs/source/segment-reference.rst | 8 - docs/source/troubleshooting.rst | 143 +++++++ docs/source/troubleshooting/linux.rst | 18 + docs/source/troubleshooting/osx.rst | 58 +++ docs/source/usage.rst | 58 ++- docs/source/usage/other.rst | 98 ++++- docs/source/usage/shell-prompts.rst | 69 +++- docs/source/usage/wm-widgets.rst | 63 ++- 21 files changed, 1265 insertions(+), 51 deletions(-) delete mode 100644 docs/source/configuration-and-customization.rst delete mode 100644 docs/source/configuration-reference.rst create mode 100644 docs/source/configuration.rst create mode 100644 docs/source/configuration/local.rst create mode 100644 docs/source/configuration/reference.rst create mode 100644 docs/source/configuration/segments.rst rename docs/source/{segment-reference => configuration/segments}/common.rst (100%) rename docs/source/{segment-reference => configuration/segments}/shell.rst (100%) rename docs/source/{segment-reference => configuration/segments}/vim.rst (100%) delete mode 100644 docs/source/segment-reference.rst diff --git a/docs/source/configuration-and-customization.rst b/docs/source/configuration-and-customization.rst deleted file mode 100644 index 6d5e8c79..00000000 --- a/docs/source/configuration-and-customization.rst +++ /dev/null @@ -1,15 +0,0 @@ -******************************* -Configuration and customization -******************************* - -Quick setup guide ------------------ - -References ----------- - -.. toctree:: - :glob: - - configuration-reference - segment-reference diff --git a/docs/source/configuration-reference.rst b/docs/source/configuration-reference.rst deleted file mode 100644 index 1e4eef8b..00000000 --- a/docs/source/configuration-reference.rst +++ /dev/null @@ -1,3 +0,0 @@ -*********************** -Configuration reference -*********************** diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst new file mode 100644 index 00000000..50b39e54 --- /dev/null +++ b/docs/source/configuration.rst @@ -0,0 +1,112 @@ +******************************* +Configuration and customization +******************************* + +.. note:: **You DO NOT have to fork the main GitHub repo to personalize your + Powerline configuration!** Please read through the :ref:`quick-guide` for + a quick introduction to user configuration. + +Powerline is configured with one main configuration file, and with separate +configuration files for themes and colorschemes. All configuration files are +written in JSON, with the exception of segment definitions, which are +written in Python. + +Powerline provides default configurations in the following locations: + +:ref:`Main configuration ` + :file:`powerline/config.json` +:ref:`Colorschemes ` + :file:`powerline/colorschemes/{name}.json`, + :file:`powerline/colorscheme/__main__.json`, + :file:`powerline/colorschemes/{extension}/{name}.json` +:ref:`Themes ` + :file:`powerline/themes/{extension}/default.json` + +The default configuration files are stored in the main package. User +configuration files are stored in :file:`$XDG_CONFIG_HOME/powerline` for +Linux users, and in :file:`~/.config/powerline` for OS X users. This usually +corresponds to :file:`~/.config/powerline` on both platforms. + +If you need per-instance configuration please refer to :ref:`Local configuration +overrides `. + +.. _quick-guide: + +Quick setup guide +================= + +This guide will help you with the initial configuration of Powerline. + +Start by copying the entire set of default configuration files to the +corresponding path in your user config directory: + +.. code-block:: sh + + mkdir ~/.config/powerline + cp -R /path/to/powerline/config_files/* ~/.config/powerline + +Each extension (vim, tmux, etc.) has its own theme, and they are located in +:file:`{config directory}/themes/{extension}/default.json`. + +If you want to move, remove or customize any of the provided segments, you +can do that by updating the segment dictionary in the theme you want to +customize. A segment dictionary looks like this: + +.. code-block:: javascript + + { + "name": "segment_name" + ... + } + +You can move the segment dictionaries around to change the segment +positions, or remove the entire dictionary to remove the segment from the +prompt or statusline. + +.. note:: It's essential that the contents of all your configuration files + is valid JSON! It's strongly recommended that you run your configuration + files through ``jsonlint`` after changing them. + +Some segments need a user configuration to work properly. Here's a couple of +segments that you may want to customize right away: + +**E-mail alert segment** + You have to set your username and password (and possibly server/port) + for the e-mail alert segment. If you're using GMail it's recommended + that you `generate an application-specific password + `_ for this purpose. + + Open a theme file, scroll down to the ``email_imap_alert`` segment and + set your ``username`` and ``password``. The server defaults to GMail's + IMAP server, but you can set the server/port by adding a ``server`` and + a ``port`` argument. +**Weather segment** + The weather segment will try to find your location using a GeoIP lookup, + so unless you're on a VPN you probably won't have to change the location + query. + + If you want to change the location query or the temperature unit you'll + have to update the segment arguments. Open a theme file, scroll down to + the weather segment and update it to include unit/location query + arguments: + + .. code-block:: javascript + + { + "name": "weather", + "priority": 50, + "args": { + "unit": "F", + "location_query": "oslo, norway" + } + }, + +References +========== + +.. toctree:: + :glob: + + configuration/reference + configuration/segments + configuration/local diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst new file mode 100644 index 00000000..d8d55e84 --- /dev/null +++ b/docs/source/configuration/local.rst @@ -0,0 +1,154 @@ +.. _local-configuration-overrides: + +***************************** +Local configuration overrides +***************************** + +Depending on the application used it is possible to override configuration. Here +is the list: + +Vim overrides +============= + +Vim configuration can be overridden using the following options: + +``g:powerline_config_overrides`` + Dictionary, recursively merged with contents of + :file:`powerline/config.json`. + +``g:powerline_theme_overrides__{theme_name}`` + Dictionary, recursively merged with contents of + :file:`powerline/themes/vim/{theme_name}.json`. Note that this way you can’t + redefine some value (e.g. segment) in list, only the whole list itself: only + dictionaries are merged recursively. + +``g:powerline_config_path`` + Path (must be expanded, ``~`` shortcut is not supported). Points to the + directory which will be searched for configuration. When this option is + present, none of the other locations are searched. + +``g:powerline_no_python_error`` + If this variable is set to a true value it will prevent Powerline from reporting + an error when loaded in a copy of vim without the necessary Python support. + +Powerline script overrides +========================== + +Powerline script has a number of options controlling powerline behavior. Here +``VALUE`` always means “some JSON object”. + +``-c KEY.NESTED_KEY=VALUE`` or ``--config=KEY.NESTED_KEY=VALUE`` + Overrides options from :file:`powerline/config.json`. + ``KEY.KEY2.KEY3=VALUE`` is a shortcut for ``KEY={"KEY2": {"KEY3": VALUE}}``. + Multiple options (i.e. ``-c K1=V1 -c K2=V2``) are allowed, result (in the + example: ``{"K1": V1, "K2": V2}``) is recursively merged with the contents + of the file. + + If ``VALUE`` is omitted then corresponding key will be removed from the + configuration (if it was present). + +``-t THEME_NAME.KEY.NESTED_KEY=VALUE`` or ``--theme_option=THEME_NAME.KEY.NESTED_KEY=VALUE`` + Overrides options from :file:`powerline/themes/{ext}/{THEME_NAME}.json`. + ``KEY.NESTED_KEY=VALUE`` is processed like described above, ``{ext}`` is the + first argument to powerline script. May be passed multiple times. + + If ``VALUE`` is omitted then corresponding key will be removed from the + configuration (if it was present). + +``-p PATH`` or ``--config_path=PATH`` + Sets directory where configuration should be read from. If present, no + default locations are searched for configuration. No expansions are + performed by powerline script itself, but ``-p ~/.powerline`` will likely be + expanded by the shell to something like ``-p /home/user/.powerline``. + +Zsh/zpython overrides +===================== + +Here overrides are controlled by similarly to the powerline script, but values +are taken from zsh variables. + +``POWERLINE_CONFIG`` + Overrides options from :file:`powerline/config.json`. Should be a zsh + associative array with keys equal to ``KEY.NESTED_KEY`` and values being + JSON strings. Pair ``KEY.KEY1 VALUE`` is equivalent to ``{"KEY": {"KEY1": + VALUE}}``. All pairs are then recursively merged into one dictionary and + this dictionary is recursively merged with the contents of the file. + +``POWERLINE_THEME_CONFIG`` + Overrides options from :file:`powerline/themes/shell/*.json`. Should be + a zsh associative array with keys equal to ``THEME_NAME.KEY.NESTED_KEY`` and + values being JSON strings. Is processed like the above ``POWERLINE_CONFIG``, + but only subdictionaries for ``THEME_NAME`` key are merged with theme + configuration when theme with given name is requested. + +``POWERLINE_CONFIG_PATH`` + Sets directory where configuration should be read from. If present, no + default locations are searched for configuration. No expansions are + performed by powerline script itself, but zsh usually performs them on its + own if you set variable without quotes: ``POWERLINE_CONFIG_PATH=~/example``. + Expansion depends on zsh configuration. + +Ipython overrides +================= + +Ipython overrides depend on ipython version. Before ipython-0.11 you should pass +additional keyword arguments to setup() function. After ipython-0.11 you should +use ``c.Powerline.KEY``. Supported ``KEY`` strings or keyword argument names: + +``config_overrides`` + Overrides options from :file:`powerline/config.json`. Should be a dictionary + that will be recursively merged with the contents of the file. + +``theme_overrides`` + Overrides options from :file:`powerline/themes/ipython/*.json`. Should be + a dictionary where keys are theme names and values are dictionaries which + will be recursively merged with the contents of the given theme. + +``path`` + Sets directory where configuration should be read from. If present, no + default locations are searched for configuration. No expansions are + performed thus you cannot use paths starting with ``~/``. + +Prompt command +============== + +In addition to the above configuration options you can use +``$POWERLINE_COMMAND`` environment variable to tell shell or tmux to use +specific powerline implementation. This is mostly useful for putting powerline +into different directory or replacing ``powerline`` script with +``powerline-client`` for performance reasons. + +.. note:: + + ``$POWERLINE_COMMAND`` appears in shell scripts without quotes thus you can + specify additional parameters in bash. In zsh you will have to make + ``$POWERLINE_COMMAND`` an array parameter to achieve the same result. In + tmux it is passed to ``eval`` and depends on the shell used. + POSIX-compatible shells, zsh, bash and fish will split this variable in this + case. + +If you want to disable prompt in shell, but still have tmux support or if you +want to disable tmux support you can use variables +``$POWERLINE_NO_{SHELL}_PROMPT``/``$POWERLINE_NO_SHELL_PROMPT`` and +``$POWERLINE_NO_{SHELL}_TMUX_SUPPORT``/``$POWERLINE_NO_SHELL_TMUX_SUPPORT`` +(substitute ``{SHELL}`` with the name of the shell (all-caps) you want to +disable support for (e.g. ``BASH``) or use all-inclusive ``SHELL`` that will +disable support for all shells). These variables have no effect after +configuration script was sourced (in fish case: after ``powerline-setup`` +function was run). To disable specific feature support set one of these +variables to some non-empty value. + +If you do not want to disable prompt in shell, but yet do not want to launch +python twice to get :ref:`above ` lines you do not use in +tcsh you should set ``$POWERLINE_NO_TCSH_ABOVE`` or +``$POWERLINE_NO_SHELL_ABOVE`` variable. + +If you do not want to see additional space which is added to the right prompt in +fish in order to support multiline prompt you should set +``$POWERLINE_NO_FISH_ABOVE`` or ``$POWERLINE_NO_SHELL_ABOVE`` variables. + +.. note:: + + Most supported shells’ configuration scripts check for additional + configuration variables being empty. But tcsh configuration script checks + for variables being *defined*, not empty. diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst new file mode 100644 index 00000000..b096f922 --- /dev/null +++ b/docs/source/configuration/reference.rst @@ -0,0 +1,367 @@ +*********************** +Configuration reference +*********************** + +.. _config-main: + +Main configuration +================== + +:Location: :file:`powerline/config.json` + +The main configuration file defines some common options that applies to all +extensions, as well as some extension-specific options like themes and +colorschemes. + +Common configuration +-------------------- + +Common configuration is a subdictionary that is a value of ``common`` key in +:file:`powerline/config.json` file. + +.. _config-common-term_truecolor: + +``term_truecolor`` + Defines whether to output cterm indices (8-bit) or RGB colors (24-bit) + to the terminal emulator. See the :ref:`term-feature-support-matrix` for + information on whether your terminal emulator supports 24-bit colors. + +.. _config-common-ambiwidth: + +``ambiwidth`` + Tells powerline what to do with characters with East Asian Width Class + Ambigious (such as Euro, Registered Sign, Copyright Sign, Greek + letters, Cyrillic letters). Valid values: any positive integer; it is + suggested that you only set it to 1 (default) or 2. + +``watcher`` + Select filesystem watcher. Variants are + + ======= =================================== + Variant Description + ======= =================================== + auto Selects most performant watcher. + inotify Select inotify watcher. Linux only. + stat Select stat-based polling watcher. + ======= =================================== + + Default is ``auto``. + +.. _config-common-additional_escapes: + +``additional_escapes`` + Valid for shell extensions, makes sense only if :ref:`term_truecolor + ` is enabled. Is to be set from command-line + (unless you are sure you always need it). Controls additional escaping that + is needed for tmux/screen to work with terminal true color escape codes: + normally tmux/screen prevent terminal emulator from receiving these control + codes thus rendering powerline prompt colorless. Valid values: ``"tmux"``, + ``"screen"``, ``null`` (default). + +``dividers`` + Defines the dividers used in all Powerline extensions. This option + should usually only be changed if you don't have a patched font, or if + you use a font patched with the legacy font patcher. + + The ``hard`` dividers are used to divide segments with different + background colors, while the ``soft`` dividers are used to divide + segments with the same background color. + +.. _config-common-paths: + +``paths`` + Defines additional paths which will be searched for modules when using + :ref:`module segment option `. Paths defined here + have priority when searching for modules. + +``log_file`` + Defines path which will hold powerline logs. If not present, logging will be + done to stderr. + +``log_level`` + String, determines logging level. Defaults to ``WARNING``. + +``log_format`` + String, determines format of the log messages. Defaults to + ``'%(asctime)s:%(level)s:%(message)s'``. + +``interval`` + Number, determines time (in seconds) between checks for changed + configuration. Checks are done in a seprate thread. Use ``null`` to check + for configuration changes on ``.render()`` call in main thread. + Defaults to ``None``. + +``reload_config`` + Boolean, determines whether configuration should be reloaded at all. + Defaults to ``True``. + +Extension-specific configuration +-------------------------------- + +Common configuration is a subdictionary that is a value of ``ext`` key in +:file:`powerline/config.json` file. + +``colorscheme`` + Defines the colorscheme used for this extension. + +``theme`` + .. _config-ext-theme: + + Defines the theme used for this extension. + +``local_themes`` + .. _config-ext-local_themes: + + Defines themes used when certain conditions are met, e.g. for + buffer-specific statuslines in vim. Value depends on extension used. For vim + it is a dictionary ``{matcher_name : theme_name}``, where ``matcher_name`` + is either ``matcher_module.module_attribute`` or ``module_attribute`` + (``matcher_module`` defaults to ``powerline.matchers.vim``) and + ``module_attribute`` should point to a function that returns boolean value + indicating that current buffer has (not) matched conditions. + +.. _config-colors: + +Color definitions +================= + +:Location: :file:`powerline/colors.json` + +.. _config-colors-colors: + +``colors`` + Color definitions, consisting of a dict where the key is the name of the + color, and the value is one of the following: + + * A cterm color index. + * A list with a cterm color index and a hex color string (e.g. ``[123, + "aabbcc"]``). This is useful for colorschemes that use colors that + aren't available in color terminals. + +``gradients`` + Gradient definitions, consisting of a dict where the key is the name of the + gradient, and the value is a list containing one or two items, second item + is optional: + + * A list of cterm color indicies. + * A list of hex color strings. + + It is expected that you define gradients from least alert color to most + alert or use non-alert colors. + +.. _config-colorschemes: + +Colorschemes +============ + +:Location: :file:`powerline/colorschemes/{name}.json`, + :file:`powerline/colorscheme/__main__.json`, + :file:`powerline/colorschemes/{extension}/{name}.json` + +Colorscheme files are processed in order given: definitions from each next file +override those from each previous file. It is required that either +:file:`powerline/colorschemes/{name}.json`, or +:file:`powerline/colorschemes/{extension}/{name}.json` exists. + +``name`` + Name of the colorscheme. + +.. _config-colorschemes-groups: + +``groups`` + Segment highlighting groups, consisting of a dict where the key is the + name of the highlighting group (usually the function name for function + segments), and the value is either + + #) a dict that defines the foreground color, background color and + attributes: + + ``fg`` + Foreground color. Must be defined in :ref:`colors + `. + + ``bg`` + Background color. Must be defined in :ref:`colors + `. + + ``attr`` + List of attributes. Valid values are one or more of ``bold``, + ``italic`` and ``underline``. Note that some attributes may be + unavailable in some applications or terminal emulators. If you do not + need any attributes leave this empty. + + #) a string (an alias): a name of existing group. This group’s definition + will be used when this color is requested. + +``mode_translations`` + Mode-specific highlighting for extensions that support it (e.g. the vim + extension). It's an easy way of changing a color in a specific mode. + Consists of a dict where the key is the mode and the value is a dict + with the following options: + + ``colors`` + A dict where the key is the color to be translated in this mode, and + the value is the new color. Both the key and the value must be defined + in :ref:`colors `. + + ``groups`` + Segment highlighting groups for this mode. Same syntax as the main + :ref:`groups ` option. + +.. _config-themes: + +Themes +====== + +:Location: :file:`powerline/themes/{extension}/{name}.json` + +``name`` + Name of the theme. + +.. _config-themes-default_module: + +``default_module`` + Python module where segments will be looked by default. + +.. _config-themes-segment_data: + +``segment_data`` + A dict where keys are segment names or strings ``{module}.{name}``. Used to + specify default values for various keys: + :ref:`after `, + :ref:`args ` (only for function segments), + :ref:`before `, + :ref:`contents ` (only for string segments + if :ref:`name ` is defined), + :ref:`display ` values of these + keys are first searched in the segment description, then in ``segment_data`` + key of a local theme, then in ``segment_data`` key of a :ref:`default theme + `. For the :ref:`default theme ` itself + step 2 is obviously avoided. + +``segments`` + A dict with a ``left`` and a ``right`` lists, consisting of segment + dictionaries. Shell themes may also contain ``above`` list of dictionaries. + Each item in ``above`` list may have ``left`` and ``right`` keys like this + dictionary, but no ``above`` key. + + .. _config-themes-above: + + ``above`` list is used for multiline shell configurations. + + ``left`` and ``right`` lists are used for segments that should be put on the + left or right side in the output. Actual mechanizm of putting segments on + the left or the right depends on used renderer, but most renderers require + one to specify segment with :ref:`width ` ``auto`` + on either side to make generated line fill all of the available width. + + Each segment dictionary has the following options: + + ``type`` + The segment type. Can be one of ``function`` (default), ``string`` + or ``filler``: + + ``function`` + The segment contents is the return value of the function defined + in the :ref:`name option `. + + ``string`` + A static string segment where the contents is defined in the + :ref:`contents option `, and the + highlighting group is defined in the :ref:`highlight_group + option `. + + ``module`` + .. _config-themes-seg-module: + + Function module, only required for function segments. Defaults to + ``powerline.segments.{extension}``. Default is overriden by + :ref:`default_module theme option `. + + ``name`` + .. _config-themes-seg-name: + + Function name, only required for function segments. + + ``highlight_group`` + .. _config-themes-seg-highlight_group: + + Highlighting group for this segment. Consists of a prioritized list + of highlighting groups, where the first highlighting group that is + available in the colorscheme is used. + + Ignored for segments that have ``function`` type. + + ``before`` + .. _config-themes-seg-before: + + A string which will be prepended to the segment contents. + + ``after`` + .. _config-themes-seg-after: + + A string which will be appended to the segment contents. + + ``contents`` + .. _config-themes-seg-contents: + + Segment contents, only required for ``string`` segments. + + ``args`` + .. _config-themes-seg-args: + + A dict of arguments to be passed to a ``function`` segment. + + ``align`` + Aligns the segments contents to the left (``l``), center (``c``) or + right (``r``). + + ``width`` + .. _config-themes-seg-width: + + Enforces a specific width for this segment. + + This segment will work as a spacer if the width is set to ``auto``. + Several spacers may be used, and the space will be distributed + equally among all the spacer segments. Spacers may have contents, + either returned by a function or a static string, and the contents + can be aligned with the ``align`` property. + + ``priority`` + Optional segment priority. Segments with priority ``None`` (the default + priority, represented by ``null`` in json) will always be included, + regardless of the width of the prompt/statusline. + + If the priority is any number, the segment may be removed if the + prompt/statusline width is too small for all the segments to be + rendered. A lower number means that the segment has a higher priority. + + Segments are removed according to their priority, with low priority + segments being removed first. + + ``draw_hard_divider``, ``draw_soft_divider`` + Whether to draw a divider between this and the adjacent segment. The + adjacent segment is to the *right* for segments on the *left* side, and + vice versa. Hard dividers are used between segments with different + background colors, soft ones are used between segments with same + background. Both options default to ``True``. + + ``draw_inner_divider`` + Determines whether inner soft dividers are to be drawn for function + segments. Only applicable for functions returning multiple segments. + Defaults to ``False``. + + ``exclude_modes`` + A list of modes where this segment will be excluded: The segment is + included in all modes, *except* for the modes in this list. + + ``include_modes`` + A list of modes where this segment will be included: The segment is + *not* included in any modes, *except* for the modes in this list. + + ``display`` + .. _config-themes-seg-display: + + Boolean. If false disables displaying of the segment. + Defaults to ``True``. diff --git a/docs/source/configuration/segments.rst b/docs/source/configuration/segments.rst new file mode 100644 index 00000000..e6e48f36 --- /dev/null +++ b/docs/source/configuration/segments.rst @@ -0,0 +1,32 @@ +***************** +Segment reference +***************** + +Segments +======== + +Segments are written in Python, and the default segments provided with +Powerline are located in :file:`powerline/segments/{extension}.py`. +User-defined segments can be defined in any module in ``sys.path`` or +:ref:`paths common configuration option `, import is +always absolute. + +Segments are regular Python functions, and they may accept arguments. All +arguments should have a default value which will be used for themes that +don't provide an ``args`` dict. + +A segment function must return one of the following values: + +* ``None``, which will remove the segment from the prompt/statusline. +* A string, which will be the segment contents. +* A list of dicts consisting of a ``contents`` string, and + a ``highlight_group`` list. This is useful for providing a particular + highlighting group depending on the segment contents. + +Available segments +================== + +.. toctree:: + :glob: + + segments/* diff --git a/docs/source/segment-reference/common.rst b/docs/source/configuration/segments/common.rst similarity index 100% rename from docs/source/segment-reference/common.rst rename to docs/source/configuration/segments/common.rst diff --git a/docs/source/segment-reference/shell.rst b/docs/source/configuration/segments/shell.rst similarity index 100% rename from docs/source/segment-reference/shell.rst rename to docs/source/configuration/segments/shell.rst diff --git a/docs/source/segment-reference/vim.rst b/docs/source/configuration/segments/vim.rst similarity index 100% rename from docs/source/segment-reference/vim.rst rename to docs/source/configuration/segments/vim.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 5a502249..53e32232 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,7 +9,7 @@ Powerline overview installation usage - configuration-and-customization + configuration troubleshooting tips-and-tricks license-and-credits diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 3696e7c0..b78497a9 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -2,6 +2,53 @@ Installation ************ +Generic requirements +==================== + +* Python 2.6 or later, 3.2 or later, PyPy 2.0 or later. It is the only + non-optional requirement. +* C compiler. Required to build powerline client on linux. If it is not present + then powerline will fall back to shell script or python client. +* ``socat`` program. Required for shell variant of client which runs a bit + faster than python version of the client, but still slower than C version. +* ``psutil`` python package. Required for some segments like cpu_percent. Some + segments have linux-only fallbacks for ``psutil`` functionality. +* ``mercurial`` python package (note: *not* standalone executable). Required to + work with mercurial repositories. +* ``pygit2`` python package or ``git`` executable. Required to work with ``git`` + repositories. +* ``bzr`` python package (note: *not* standalone executable). Required to work + with bazaar repositories. +* ``i3-py``, `available on github `_. Required + for i3wm bindings and segments. + +.. note:: + Until mercurial and bazaar support Python-3 or PyPy powerline will not + support repository information when running in these interpreters. + +Pip installation +================ + +This project is currently unavailable from PyPI due to a naming conflict with an +unrelated project, thus you will have to use the following command to install +powerline with ``pip``:: + + pip install --user git+git://github.com/Lokaltog/powerline + +. You may also choose to clone powerline repository somewhere and use:: + + pip install -e --user {path_to_powerline} + +, but note that in this case ``pip`` will not install ``powerline`` executable +and you will have to do something like:: + + ln -s {path_to_powerline}/scripts/powerline ~/.local/bin + +(:file:`~/.local/bin` should be replaced with some path present in ``$PATH``). + +Installation on various platforms +================================= + .. toctree:: Linux diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index 377770a3..f4df8f40 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -2,8 +2,28 @@ Installation on Linux ********************* -Requirements ------------- +The following distribution-specific packages are officially supported, and they +provide an easy way of installing and upgrading Powerline. The packages will +automatically do most of the configuration for you. -Installation ------------- +* `Arch Linux (AUR), Python 2 version `_ +* `Arch Linux (AUR), Python 3 version `_ +* Gentoo Live ebuild in `raiagent `_ overlay + +If you're running a distribution without an official package you'll have to +follow the installation guide below: + +1. Install Python 3.2+ or Python 2.6+ with ``pip``. This step is + distribution-specific, so no commands provided. +2. Install Powerline using the following command:: + + pip install --user git+git://github.com/Lokaltog/powerline + +.. note:: You need to use the GitHub URI when installing Powerline! This + project is currently unavailable on the PyPI due to a naming conflict + with an unrelated project. + +.. note:: If you are powerline developer you should be aware that ``pip install + --editable`` does not currently fully work. If you + install powerline this way you will be missing ``powerline`` executable and + need to symlink it. It will be located in ``scripts/powerline``. diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index b44f5529..e2e1c9b8 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -2,8 +2,41 @@ Installation on OS X ******************** -Requirements ------------- +Python package +============== -Installation ------------- +1. Install a proper Python version (see `issue #39 + `_ for a discussion + regarding the required Python version on OS X):: + + sudo port select python python27-apple + + . You may use homebrew for this:: + + brew install python + + . + +2. Install Powerline using the following command:: + + pip install --user git+git://github.com/Lokaltog/powerline + +.. warning:: When using ``brew install`` to install Python one must not supply + ``--user`` flag to ``pip``. + +.. note:: You need to use the GitHub URI when installing Powerline! This + project is currently unavailable on the PyPI due to a naming conflict + with an unrelated project. + +.. note:: If you are powerline developer you should be aware that ``pip install + --editable`` does not currently fully work. If you install powerline this way + you will be missing ``powerline`` executable and need to symlink it. It will + be located in ``scripts/powerline``. + +Vim installation +================ + +Any terminal vim version with Python 3.2+ or Python 2.6+ support should work, +but if you're using MacVim you need to install it using the following command:: + + brew install macvim --env-std --override-system-vim diff --git a/docs/source/segment-reference.rst b/docs/source/segment-reference.rst deleted file mode 100644 index 83b20b12..00000000 --- a/docs/source/segment-reference.rst +++ /dev/null @@ -1,8 +0,0 @@ -***************** -Segment reference -***************** - -.. toctree:: - :glob: - - segment-reference/* diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 31c5fc80..68bdc399 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -2,7 +2,150 @@ Troubleshooting *************** +System-specific issues +====================== + .. toctree:: Linux OS X + +Common issues +============= + +I'm using tmux and Powerline looks like crap, what's wrong? +----------------------------------------------------------- + +* You need to tell tmux that it has 256-color capabilities. Add this to your + :file:`.tmux.conf` to solve this issue:: + + set -g default-terminal "screen-256color" + +* If you're using iTerm2, make sure that you have enabled the setting + :guilabel:`Set locale variables automatically` in :menuselection:`Profiles + --> Terminal --> Environment`. + +I’m using tmux/screen and Powerline is colorless +------------------------------------------------ + +* If the above advices do not help, then you need to disable + :ref:`term_truecolor `. +* Alternative: set :ref:`additional_escapes ` + to ``"tmux"`` or ``"screen"``. Note that it is known to work perfectly in + screen, but in tmux it may produce ugly spaces. + + +After an update something stopped working +----------------------------------------- + +Assuming powerline was working before update and stopped only after there are +two possible explanations: + +* You have more then one powerline installation (e.g. ``pip`` and ``Vundle`` + installations) and you have updated only one. +* Update brought some bug to powerline. + +In the second case you, of course, should report the bug to `powerline bug +tracker `_. In the first you should make +sure you either have only one powerline installation or you update all of them +simultaneously (beware that in the second case you are not supported). To +diagnose this problem you may do the following: + +#) If this problem is observed within the shell make sure that + + .. code-block:: sh + + python -c 'import powerline; print (powerline.__file__)' + + which should report something like + :file:`/usr/lib64/python2.7/site-packages/powerline/__init__.pyc` (if + powerline is installed system-wide) or + :file:`/home/USER/.../powerline/__init__.pyc` (if powerline was cloned + somewhere, e.g. in :file:`/home/USER/.vim/bundle/powerline`) reports the same + location you use to source in your shell configuration: in first case it + should be some location in :file:`/usr` (e.g. + :file:`/usr/share/zsh/site-contrib/powerline.zsh`), in the second it should + be something like + :file:`/home/USER/.../powerline/bindings/zsh/powerline.zsh`. If this is true + it may be a powerline bug, but if locations do not match you should not + report the bug until you observe it on configuration where locations do + match. +#) If this problem is observed within the vim instance you should check out the + output of the following Ex mode commands + + .. code-block:: vim + + python import powerline as pl ; print (pl.__file__) + python3 import powerline as pl ; print (pl.__file__) + + One (but not both) of them will most likely error out, this is OK. The same + rules apply as in the 1), but in place of sourcing you should seek for the + place where you modify `runtimepath` vim option. If you install powerline + using `VAM `_ then no + explicit modifications of runtimpath were performed in your vimrc + (runtimepath is modified by VAM in this case), but powerline will be placed + in :file:`{plugin_root_dir}/powerline` where `{plugin_root_dir}` is stored in + VAM settings dictionary: do `echo g:vim_addon_manager.plugin_root_dir`. + +There is a hint if you want to place powerline repository somewhere, but still +make powerline package importable anywhere: use + + .. code-block:: sh + + pip install --user --editable path/to/powerline + +My vim statusline has strange characters like ``^B`` in it! +----------------------------------------------------------- + +* Please add ``set encoding=utf-8`` to your :file:`vimrc`. + +My vim statusline has a lot of ``^`` or underline characters in it! +------------------------------------------------------------------- + +* You need to configure the ``fillchars`` setting to disable statusline + fillchars (see ``:h fillchars`` for details). Add this to your + :file:`vimrc` to solve this issue: + + .. code-block:: vim + + set fillchars+=stl:\ ,stlnc:\ + +My vim statusline is hidden/only appears in split windows! +---------------------------------------------------------- + +* Make sure that you have ``set laststatus=2`` in your :file:`vimrc`. + +My vim statusline is not displayed completely and has too much spaces +--------------------------------------------------------------------- + +* Be sure you have ``ambiwidth`` option set to ``single``. +* Alternative: set :ref:`ambiwidth ` to 2, remove fancy + dividers (they suck when ``ambiwidth`` is set to double). + +When using z powerline shows wrong number of jobs +------------------------------------------------- + +This happens because `z `_ is launching some jobs in +the background from ``$POWERLINE_COMMAND`` and these jobs fail to finish before +powerline prompt is run. + +Solution to this problem is simple: be sure that :file:`z.sh` is sourced +strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background +jobs are spawned by `z `_ after powerline has done +its job. + +I am suffering bad lags before displaying shell prompt +------------------------------------------------------ + +To get rid of these lags there currently are two options: + +* Run ``powerline-daemon``. Powerline does not automatically start it for you. +* Compile and install ``libzpython`` module that lives in + https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. + +Prompt is spoiled after completing files in ksh +----------------------------------------------- + +This is exactly why powerline has official mksh support, but not official ksh +support. If you know the solution feel free to share it in `powerline bug +tracker`_. diff --git a/docs/source/troubleshooting/linux.rst b/docs/source/troubleshooting/linux.rst index 70bd739d..ed069d94 100644 --- a/docs/source/troubleshooting/linux.rst +++ b/docs/source/troubleshooting/linux.rst @@ -1,3 +1,21 @@ ************************ Troubleshooting on Linux ************************ + +I can't see any fancy symbols, what's wrong? +-------------------------------------------- + +* Make sure that you've configured gvim or your terminal emulator to use + a patched font. +* You need to set your ``LANG`` and ``LC_*`` environment variables to + a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's + documentation for information about setting these variables correctly. +* Make sure that vim is compiled with the ``--with-features=big`` flag. +* If you're using rxvt-unicode, make sure that it's compiled with the + ``--enable-unicode3`` flag. + +The fancy symbols look a bit blurry or "off"! +--------------------------------------------- + +* Make sure that you have patched all variants of your font (i.e. both the + regular and the bold font files). diff --git a/docs/source/troubleshooting/osx.rst b/docs/source/troubleshooting/osx.rst index e4fd024b..653a06be 100644 --- a/docs/source/troubleshooting/osx.rst +++ b/docs/source/troubleshooting/osx.rst @@ -1,3 +1,61 @@ *********************** Troubleshooting on OS X *********************** + +I can't see any fancy symbols, what's wrong? +-------------------------------------------- + +* If you're using iTerm2, please update to `this revision + `_ + or newer. +* You need to set your ``LANG`` and ``LC_*`` environment variables to + a UTF-8 locale (e.g. ``LANG=en_US.utf8``). Consult your Linux distro's + documentation for information about setting these variables correctly. + +The colors look weird in the default OS X Terminal app! +------------------------------------------------------- + +* The arrows may have the wrong colors if you have changed the "minimum + contrast" slider in the color tab of your OS X settings. +* The default OS X Terminal app is known to have some issues with the + Powerline colors. Please use another terminal emulator. iTerm2 should work + fine. + +The colors look weird in iTerm2! +-------------------------------- + +* The arrows may have the wrong colors if you have changed the "minimum + contrast" slider in the color tab of your OS X settings. +* Please disable background transparency to resolve this issue. + +Statusline is getting wrapped to the next line in iTerm2 +-------------------------------------------------------- + +* Turn off “Treat ambigious-width characters as double width” in `Preferences + --> Text`. +* Alternative: remove fancy dividers (they suck in this case), set + :ref:`ambiwidth ` to 2. + +I receive a ``NameError`` when trying to use Powerline with MacVim! +------------------------------------------------------------------- + +* Please install MacVim using this command:: + + brew install macvim --env-std --override-system-vim + + Then install Powerline locally with ``pip install --user``, or by + running these commands in the ``powerline`` directory:: + + ./setup.py build + ./setup.py install --user + +I receive an ``ImportError`` when trying to use Powerline on OS X! +------------------------------------------------------------------ + +* This is caused by an invalid ``sys.path`` when using system vim and system + Python. Please try to select another Python distribution:: + + sudo port select python python27-apple + +* See `issue #39 `_ for + a discussion and other possible solutions for this issue. diff --git a/docs/source/usage.rst b/docs/source/usage.rst index a03340bf..d6ee2794 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -2,8 +2,62 @@ Usage ***** -Application support matrix --------------------------- +Application-specific requirements +--------------------------------- + +Vim plugin requirements +^^^^^^^^^^^^^^^^^^^^^^^ + +The vim plugin requires a vim version with Python support compiled in. You +can check if your vim supports Python by running ``vim --version | grep ++python``. + +If your vim version doesn't have support for Python, you'll have to compile +it with the ``--enable-pythoninterp`` flag (``--enable-python3interp`` if +you want Python 3 support instead). Note that this also requires the related +Python headers to be installed on your system. Please consult your +distribution's documentation for details on how to compile and install +packages. + +Vim version 7.3.661 or newer is recommended for performance reasons. + +Terminal emulator requirements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Powerline uses several special glyphs to get the arrow effect and some +custom symbols for developers. This requires that you either have a symbol +font or a patched font on your system. Your terminal emulator must also +support either patched fonts or fontconfig for Powerline to work properly. + +You can also enable :ref:`24-bit color support ` +if your terminal emulator supports it. + +.. table:: Application/terminal emulator feature support matrix + :name: term-feature-support-matrix + + ===================== ======= ===================== ===================== ===================== + Name OS Patched font support Fontconfig support 24-bit color support + ===================== ======= ===================== ===================== ===================== + Gnome Terminal Linux |i_yes| |i_yes| |i_no| + Gvim Linux |i_yes| |i_no| |i_yes| + iTerm2 OS X |i_yes| |i_no| |i_no| + Konsole Linux |i_yes| |i_yes| |i_yes| + lxterminal Linux |i_yes| |i_yes| |i_no| + MacVim OS X |i_yes| |i_no| |i_yes| + rxvt-unicode Linux |i_partial| [#]_ |i_no| |i_no| + st Linux |i_yes| |i_yes| |i_no| + Terminal.app OS X |i_yes| |i_no| |i_no| + Xfce Terminal Linux |i_yes| |i_yes| |i_no| + xterm Linux |i_yes| |i_no| |i_partial| [#]_ + ===================== ======= ===================== ===================== ===================== + +.. |i_yes| image:: _static/img/icons/tick.png +.. |i_no| image:: _static/img/icons/cross.png +.. |i_partial| image:: _static/img/icons/error.png + +.. [#] Must be compiled with ``--enable-unicode3`` for the + patched font to work. +.. [#] Uses nearest color from 8-bit palette. Plugins ------- diff --git a/docs/source/usage/other.rst b/docs/source/usage/other.rst index 40e3b686..d312494c 100644 --- a/docs/source/usage/other.rst +++ b/docs/source/usage/other.rst @@ -2,5 +2,99 @@ Other plugins ************* -tmux statusline ---------------- +.. _vim-vimrc: + +Vim statusline +============== + +If installed using pip just add + +.. code-block:: vim + + python from powerline.vim import setup as powerline_setup + python powerline_setup() + python del powerline_setup + +(replace ``python`` with ``python3`` if appropriate) to your :file:`vimrc`. + +If you just cloned the repository add the following line to your :file:`vimrc`, +where ``{repository_root}`` is the absolute path to your Powerline installation +directory: + +.. code-block:: vim + + set rtp+={repository_root}/powerline/bindings/vim + +If you're using Vundle or Pathogen and don't want Powerline functionality in +any other applications, simply add Powerline as a bundle and point the path +above to the Powerline bundle directory, e.g. +``~/.vim/bundle/powerline/powerline/bindings/vim``. For vim-addon-manager it is +even easier since you don’t need to write this big path or install anything by +hand: ``powerline`` is installed and run just like any other plugin using + +.. code-block:: vim + + call vam#ActivateAddons(['powerline']) + +.. note:: + If you use supplied :file:`powerline.vim` file to load powerline there are + additional configuration variables available: ``g:powerline_pycmd`` and + ``g:powerline_pyeval``. First sets command used to load powerline: expected + values are ``"py"`` and ``"py3"``. Second sets function used in statusline, + expected values are ``"pyeval"`` and ``"py3eval"``. + + If ``g:powerline_pycmd`` is set to the one of the expected values then + ``g:powerline_pyeval`` will be set accordingly. If it is set to some other + value then you must also set ``g:powerline_pyeval``. Powerline will not + check that Vim is compiled with Python support if you set + ``g:powerline_pycmd`` to an unexpected value. + + These values are to be used to specify the only Python that is to be loaded + if you have both versions: Vim may disable loading one python version if + other was already loaded. They should also be used if you have two python + versions able to load simultaneously, but with powerline installed only for + python-3 version. + +Tmux statusline +=============== + +Add the following lines to your :file:`.tmux.conf`, where ``{repository_root}`` +is the absolute path to your Powerline installation directory:: + + source "{repository_root}/powerline/bindings/tmux/powerline.conf" + +.. note:: + The availability of the ``powerline-config`` command is required for + powerline support. You may specify location of this script via + ``$POWERLINE_CONFIG_COMMAND`` environment variable. + +.. note:: + It is advised that you run ``powerline-daemon`` before adding the above line + to tmux.conf. To do so add:: + + run-shell "powerline-daemon -q" + + to :file:`.tmux.conf`. + +IPython prompt +============== + +For IPython<0.11 add the following lines to your +:file:`.ipython/ipy_user_conf.py`:: + + # top + from powerline.bindings.ipython.pre_0_11 import setup as powerline_setup + + # main() function (assuming you launched ipython without configuration to + # create skeleton ipy_user_conf.py file): + powerline_setup() + +For IPython>=0.11 add the following line to your :file:`ipython_config.py` +file in the profile you are using:: + + c.InteractiveShellApp.extensions = [ + 'powerline.bindings.ipython.post_0_11' + ] + +IPython=0.11* is not supported and does not work. IPython<0.10 was not +tested (not installable by pip). diff --git a/docs/source/usage/shell-prompts.rst b/docs/source/usage/shell-prompts.rst index 56da391c..e2dbc1d6 100644 --- a/docs/source/usage/shell-prompts.rst +++ b/docs/source/usage/shell-prompts.rst @@ -2,14 +2,67 @@ Shell prompts ************* -bash ----- +Bash prompt +=========== -zsh ---- +Add the following line to your :file:`bashrc`, where ``{repository_root}`` is +the absolute path to your Powerline installation directory: -fish ----- +.. code-block:: bash -IPython -------- + . {repository_root}/powerline/bindings/bash/powerline.sh + +Zsh prompt +========== + +Add the following line to your :file:`zshrc`, where ``{repository_root}`` is the +absolute path to your Powerline installation directory: + +.. code-block:: bash + + . {repository_root}/powerline/bindings/zsh/powerline.zsh + +Fish prompt +=========== + +Add the following line to your :file:`config.fish`, where ``{repository_root}`` +is the absolute path to your Powerline installation directory: + +.. code-block:: bash + + set fish_function_path $fish_function_path "{repository_root}/powerline/bindings/fish" + powerline-setup + +.. _tmux-statusline: + +Busybox (ash), mksh and dash prompt +===================================== + +After launching busybox run the following command: + +.. code-block:: bash + + . {repository_root}/powerline/bindings/shell/powerline.sh + +Mksh users may put this line into ``~/.mkshrc`` file. Dash users may use the +following in ``~/.profile``: + +.. code-block:: bash + + if test "x$0" != "x${0#dash}" ; then + export ENV={repository_root}/powerline/bindings/shell/powerline.sh + fi + +.. note:: + Dash users that already have ``$ENV`` defined should either put the ``. + …/shell/powerline.sh`` line in the ``$ENV`` file or create a new file which + will source (using ``.`` command) both former ``$ENV`` file and + :file:`powerline.sh` files and set ``$ENV`` to the path of this new file. + +.. warning:: + Job count is using some weird hack that uses signals and temporary files for + interprocess communication. It may be wrong sometimes. Not the case in mksh. + +.. warning:: + Busybox has two shells: ``ash`` and ``hush``. Second is known to segfault in + busybox 1.22.1 when using :file:`powerline.sh` script. diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst index f661199d..5383a2fd 100644 --- a/docs/source/usage/wm-widgets.rst +++ b/docs/source/usage/wm-widgets.rst @@ -2,8 +2,63 @@ Window manager widgets ********************** -Awesome -------- +Awesome widget +============== -Qtile ------ +.. note:: Powerline currently only supports awesome 3.5. + +.. note:: The Powerline widget will spawn a shell script that runs in the + background and updates the statusline with ``awesome-client``. + +Add the following to your :file:`rc.lua`, where ``{repository_root}`` is the +absolute path to your Powerline installation directory: + +.. code-block:: lua + + package.path = package.path .. ';{repository_root}/powerline/bindings/awesome/?.lua' + require('powerline') + +Then add the ``powerline_widget`` to your ``wibox``: + +.. code-block:: lua + + right_layout:add(powerline_widget) + +Qtile widget +============ + +Add the following to your :file:`~/.config/qtile/config.py`: + +.. code-block:: python + + from powerline.bindings.qtile.widget import Powerline + + screens = [ + Screen( + top=bar.Bar([ + # ... + Powerline(timeout=2), + # ... + ], + ), + ), + ] + +I3 bar +====== + +.. note:: Until the patch is done in i3, you will need a custom ``i3bar`` build + called ``i3bgbar``. The source is available `here + `_. + +Add the following to your :file:`~/.i3/config`:: + + bar { + i3bar_command i3bgbar + + status_command python /path/to/powerline/bindings/i3/powerline-i3.py + font pango:PowerlineFont 12 + } + +where ``i3bgbar`` may be replaced with the path to the custom i3bar binary and +``PowerlineFont`` is any system font with powerline support. From cbcfd317a693526fef793fc29815de93c76c5f11 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 00:08:00 +0400 Subject: [PATCH 1120/1472] Update terminal support matrix Fixes #763 Closes #660 --- docs/source/usage.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/source/usage.rst b/docs/source/usage.rst index d6ee2794..0bf33b35 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -38,16 +38,15 @@ if your terminal emulator supports it. ===================== ======= ===================== ===================== ===================== Name OS Patched font support Fontconfig support 24-bit color support ===================== ======= ===================== ===================== ===================== - Gnome Terminal Linux |i_yes| |i_yes| |i_no| Gvim Linux |i_yes| |i_no| |i_yes| iTerm2 OS X |i_yes| |i_no| |i_no| Konsole Linux |i_yes| |i_yes| |i_yes| lxterminal Linux |i_yes| |i_yes| |i_no| MacVim OS X |i_yes| |i_no| |i_yes| rxvt-unicode Linux |i_partial| [#]_ |i_no| |i_no| - st Linux |i_yes| |i_yes| |i_no| + st Linux |i_yes| |i_yes| |i_yes| [#]_ Terminal.app OS X |i_yes| |i_no| |i_no| - Xfce Terminal Linux |i_yes| |i_yes| |i_no| + libvte-based [#]_ Linux |i_yes| |i_yes| |i_yes| [#]_ xterm Linux |i_yes| |i_no| |i_partial| [#]_ ===================== ======= ===================== ===================== ===================== @@ -55,8 +54,10 @@ if your terminal emulator supports it. .. |i_no| image:: _static/img/icons/cross.png .. |i_partial| image:: _static/img/icons/error.png -.. [#] Must be compiled with ``--enable-unicode3`` for the - patched font to work. +.. [#] Must be compiled with ``--enable-unicode3`` for the patched font to work. +.. [#] Since version 0.5. +.. [#] Including XFCE terminal and GNOME terminal. +.. [#] Since version 0.36. .. [#] Uses nearest color from 8-bit palette. Plugins From b6be30470ba23598e078ba3dce080c8e9e769291 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 00:10:34 +0400 Subject: [PATCH 1121/1472] Remove timeout tip Closes #808 Closes #266 --- docs/source/tips-and-tricks.rst | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/docs/source/tips-and-tricks.rst b/docs/source/tips-and-tricks.rst index 59db85cd..83ae0ea5 100644 --- a/docs/source/tips-and-tricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -5,26 +5,6 @@ Tips and tricks Vim === -Fix terminal timeout when pressing escape ------------------------------------------ - -When you're pressing :kbd:`Escape` to leave insert mode in the terminal, it -will by default take a second or another keystroke to leave insert mode -completely and update the statusline. If you find this annoying, you can add -the following snippet to your :file:`vimrc` to escape insert mode -immediately: - -.. code-block:: vim - - if ! has('gui_running') - set ttimeoutlen=10 - augroup FastEscape - autocmd! - au InsertEnter * set timeoutlen=0 - au InsertLeave * set timeoutlen=1000 - augroup END - endif - Useful settings --------------- From a7d3fdd3d173add4f9bc3efe0408de35d40e5816 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 00:12:14 +0400 Subject: [PATCH 1122/1472] Add note about ``tmux -2`` Closes #847 --- docs/source/troubleshooting.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 68bdc399..136c2d67 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -20,10 +20,11 @@ I'm using tmux and Powerline looks like crap, what's wrong? :file:`.tmux.conf` to solve this issue:: set -g default-terminal "screen-256color" - * If you're using iTerm2, make sure that you have enabled the setting - :guilabel:`Set locale variables automatically` in :menuselection:`Profiles - --> Terminal --> Environment`. + :guilabel:`Set locale variables automatically` in :menuselection:`Profiles --> + Terminal --> Environment`. +* Make sure tmux knows that terminal it is running in support 256 colors. You + may tell it tmux by using ``-2`` option when launching it. I’m using tmux/screen and Powerline is colorless ------------------------------------------------ From 963823d6605f593741abdf09f9f233a9d4071c43 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 01:59:14 +0400 Subject: [PATCH 1123/1472] Increase depth to 3 Reason: this way I have anything I want in one click. Less precise toctree is in any case present in the side bar. --- docs/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 53e32232..bab34ecb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,7 +3,7 @@ Powerline ********* .. toctree:: - :maxdepth: 2 + :maxdepth: 3 :glob: overview From 207c24a9f52c3c91e7a68f076d3aa53accec8a3e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 02:04:53 +0400 Subject: [PATCH 1124/1472] Add note about `https://` protocol Closes #881 --- docs/source/installation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index b78497a9..25f592b0 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -46,6 +46,12 @@ and you will have to do something like:: (:file:`~/.local/bin` should be replaced with some path present in ``$PATH``). +.. note:: + If your ISP blocks git protocol for some reason github also provides ``ssh`` + (``git+ssh://git@github.com/Lokaltog/powerline``) and ``https`` + (``git+https://github.com/Lokaltog/powerline``) protocols. ``git`` protocol + should be the fastest, but least secure one though. + Installation on various platforms ================================= From 14f2beea91dbd1a329bb43fabe9ca910abf269c0 Mon Sep 17 00:00:00 2001 From: esn89 Date: Tue, 23 Apr 2013 08:02:39 -0700 Subject: [PATCH 1125/1472] Added Source Code Pro documentation.rst Added a short blurb on Source Code Pro and how to make the glyphs show up. Closes #460 --- docs/source/tips-and-tricks.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/source/tips-and-tricks.rst b/docs/source/tips-and-tricks.rst index 83ae0ea5..42a3a317 100644 --- a/docs/source/tips-and-tricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -27,3 +27,16 @@ In your ``~/.Xdefault`` file add the following: This will allow urxvt to fallback onto the Inconsolata fonts in case it does not find the right glyths within the terminus font. + +Source Code Pro font and urxvt +============================== + +Much like the terminus font that was mentioned above, a similar fix can be applied to the Source Code Pro fonts. + +In the ``~/.Xdefaults`` add the following: + +``URxvt*font: xft:Source\ Code\ Pro\ Medium:pixelsize=13:antialias=true:hinting=true,xft:Source\ Code\ Pro\ Medium:pixelsize=13:antialias=true:hinting=true`` + +I noticed that Source Code Pro has the glyphs there already, but the pixel size of the fonts play a role in whether or not +the > or the < separators showing up or not. Using font size 12, glyphs on the right hand side of the powerline are present, +but the ones on the left don't. Pixel size 14, brings the reverse problem. Font size 13 seems to work just fine. From 29a467696dd25031384b5c943d610d92722813b1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 02:22:16 +0400 Subject: [PATCH 1126/1472] Fix styling of rxvt-unicode tips --- docs/source/tips-and-tricks.rst | 34 +++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/source/tips-and-tricks.rst b/docs/source/tips-and-tricks.rst index 42a3a317..85713038 100644 --- a/docs/source/tips-and-tricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -16,27 +16,33 @@ statusline: set laststatus=2 " Always display the statusline in all windows set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline) +Rxvt-unicode +============ + Terminus font and urxvt -======================= +----------------------- -The Terminus fonts does not have the powerline glyths and unless someone submits a patch to -the font author, it is unlikely to happen. However, Andre Klärner came up with this work around: -In your ``~/.Xdefault`` file add the following: +The Terminus fonts does not have the powerline glyphs and unless someone submits +a patch to the font author, it is unlikely to happen. However, Andre Klärner +came up with this work around: In your ``~/.Xdefault`` file add the following:: -``urxvt*font: xft:Terminus:pixelsize=12,xft:Inconsolata\ for\ Powerline:pixelsize=12`` + urxvt*font: xft:Terminus:pixelsize=12,xft:Inconsolata\ for\ Powerline:pixelsize=12 -This will allow urxvt to fallback onto the Inconsolata fonts in case it does not find the right -glyths within the terminus font. +This will allow urxvt to fallback onto the Inconsolata fonts in case it does not +find the right glyphs within the terminus font. Source Code Pro font and urxvt -============================== +------------------------------ -Much like the terminus font that was mentioned above, a similar fix can be applied to the Source Code Pro fonts. +Much like the terminus font that was mentioned above, a similar fix can be +applied to the Source Code Pro fonts. -In the ``~/.Xdefaults`` add the following: +In the ``~/.Xdefaults`` add the following:: -``URxvt*font: xft:Source\ Code\ Pro\ Medium:pixelsize=13:antialias=true:hinting=true,xft:Source\ Code\ Pro\ Medium:pixelsize=13:antialias=true:hinting=true`` + URxvt*font: xft:Source\ Code\ Pro\ Medium:pixelsize=13:antialias=true:hinting=true,xft:Source\ Code\ Pro\ Medium:pixelsize=13:antialias=true:hinting=true -I noticed that Source Code Pro has the glyphs there already, but the pixel size of the fonts play a role in whether or not -the > or the < separators showing up or not. Using font size 12, glyphs on the right hand side of the powerline are present, -but the ones on the left don't. Pixel size 14, brings the reverse problem. Font size 13 seems to work just fine. +I noticed that Source Code Pro has the glyphs there already, but the pixel size +of the fonts play a role in whether or not the > or the < separators showing up +or not. Using font size 12, glyphs on the right hand side of the powerline are +present, but the ones on the left don't. Pixel size 14, brings the reverse +problem. Font size 13 seems to work just fine. From 62a815bddff6dea9de3181576f6fe5dd86f37370 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 02:42:38 +0400 Subject: [PATCH 1127/1472] Fix segment_data key description in configuration reference --- docs/source/configuration/reference.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index b096f922..1c3ee800 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -228,12 +228,12 @@ Themes ``segment_data`` A dict where keys are segment names or strings ``{module}.{name}``. Used to specify default values for various keys: - :ref:`after `, + :ref:`after `, :ref:`args ` (only for function segments), - :ref:`before `, - :ref:`contents ` (only for string segments + :ref:`before `, + :ref:`contents ` (only for string segments if :ref:`name ` is defined), - :ref:`display `. When using :ref:`local themes ` values of these keys are first searched in the segment description, then in ``segment_data`` key of a local theme, then in ``segment_data`` key of a :ref:`default theme @@ -361,6 +361,7 @@ Themes *not* included in any modes, *except* for the modes in this list. ``display`` + .. _config-themes-seg-display: Boolean. If false disables displaying of the segment. From 74d19b4f52244ea6b13cfa8434c35772214a411f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 02:43:13 +0400 Subject: [PATCH 1128/1472] Add font installation instructions --- docs/source/installation.rst | 32 +++++++++++++++ docs/source/installation/linux.rst | 66 ++++++++++++++++++++++++++++++ docs/source/installation/osx.rst | 10 +++++ docs/source/tips-and-tricks.rst | 2 + docs/source/usage.rst | 2 + 5 files changed, 112 insertions(+) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 25f592b0..ab4fe6e8 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -52,6 +52,38 @@ and you will have to do something like:: (``git+https://github.com/Lokaltog/powerline``) protocols. ``git`` protocol should be the fastest, but least secure one though. +Fonts installation +================== + +Powerline uses several special glyphs to get the arrow effect and some custom +symbols for developers. This requires that you either have a symbol font or +a patched font on your system. Your terminal emulator must also support either +patched fonts or fontconfig for Powerline to work properly. + +You can also enable :ref:`24-bit color support ` +if your terminal emulator supports it (see :ref:`the terminal emulator support +matrix `). + +There are basically two ways to get powerline glyphs displayed: use +:file:`PowerlineSymbols.otf` font as a fallback for one of the existing fonts or +install a patched font. + +.. _installation-patched-fonts: + +Patched fonts +------------- + +This method is the fallback method and works for every terminal, with the +exception of :ref:`rxvt-unicode `. + +Download the font of your choice from `powerline-fonts`_. If you can't find +your preferred font in the `powerline-fonts`_ repo, you'll have to patch your +own font instead. + +.. _powerline-fonts: https://github.com/Lokaltog/powerline-fonts + +After downloading this font refer to platform-specific instructions. + Installation on various platforms ================================= diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index f4df8f40..5f8cd6d7 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -27,3 +27,69 @@ follow the installation guide below: --editable`` does not currently fully work. If you install powerline this way you will be missing ``powerline`` executable and need to symlink it. It will be located in ``scripts/powerline``. + +Fonts installation +================== + +Fontconfig +---------- + +This method only works on Linux. It's the recommended method if your +terminal emulator supports it as you don't have to patch any fonts, and it +generally works well with any coding font. + +#. Download the latest version of the symbol font and fontconfig file:: + + wget https://github.com/Lokaltog/powerline/raw/develop/font/PowerlineSymbols.otf + wget https://github.com/Lokaltog/powerline/raw/develop/font/10-powerline-symbols.conf + +#. Move the symbol font to a valid X font path. Valid font paths can be + listed with ``xset q``:: + + mv PowerlineSymbols.otf ~/.fonts/ + +#. Update font cache for the path you moved the font to (you may need to be + root to update the cache for system-wide paths):: + + fc-cache -vf ~/.fonts/ + +#. Install the fontconfig file. For newer versions of fontconfig the config + path is ``~/.config/fontconfig/conf.d/``, for older versions it's + ``~/.fonts.conf.d/``:: + + mv 10-powerline-symbols.conf ~/.config/fontconfig/conf.d/ + +If you can't see the custom symbols, please close all instances of your +terminal emulator. You may need to restart X for the changes to take +effect. + +If you *still* can't see the custom symbols, double-check that you have +installed the font to a valid X font path, and that you have installed the +fontconfig file to a valid fontconfig path. Alternatively try to install +a :ref:`patched font `. + +Patched font installation +------------------------- + +After downloading font you should do the following: + +#. Move the patched font to a valid X font path. Valid font paths can be + listed with ``xset q``:: + + mv 'MyFont for Powerline.otf' ~/.fonts/ + +#. Update font cache for the path you moved the font to (you may need to be + root to update the cache for system-wide paths):: + + fc-cache -vf ~/.fonts/ + +After installing the patched font you need to update Gvim or your terminal +emulator to use the patched font. The correct font usually ends with *for +Powerline*. + +If you can't see the custom symbols, please close all instances of your +terminal emulator. You may need to restart X for the changes to take +effect. + +If you *still* can't see the custom symbols, double-check that you have +installed the font to a valid X font path. diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index e2e1c9b8..204666a7 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -40,3 +40,13 @@ Any terminal vim version with Python 3.2+ or Python 2.6+ support should work, but if you're using MacVim you need to install it using the following command:: brew install macvim --env-std --override-system-vim + +Fonts installation +================== + +Install downloaded patched font by double-clicking the font file in Finder, then +clicking :guilabel:`Install this font` in the preview window. + +After installing the patched font you need to update MacVim or your terminal +emulator to use the patched font. The correct font usually ends with *for +Powerline*. diff --git a/docs/source/tips-and-tricks.rst b/docs/source/tips-and-tricks.rst index 85713038..9961b8eb 100644 --- a/docs/source/tips-and-tricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -16,6 +16,8 @@ statusline: set laststatus=2 " Always display the statusline in all windows set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline) +.. _tips-and-tricks-urxvt: + Rxvt-unicode ============ diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 0bf33b35..ccaa8925 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -21,6 +21,8 @@ packages. Vim version 7.3.661 or newer is recommended for performance reasons. +.. _usage-terminal-emulators: + Terminal emulator requirements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 2e34e1914c93f8e168a1a51eb6c938c1393cceaf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 02:52:14 +0400 Subject: [PATCH 1129/1472] Add code for Vundle installations and related warning Ref #414 Closes #378 --- docs/source/usage/other.rst | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/source/usage/other.rst b/docs/source/usage/other.rst index d312494c..06b39472 100644 --- a/docs/source/usage/other.rst +++ b/docs/source/usage/other.rst @@ -25,17 +25,38 @@ directory: set rtp+={repository_root}/powerline/bindings/vim -If you're using Vundle or Pathogen and don't want Powerline functionality in -any other applications, simply add Powerline as a bundle and point the path -above to the Powerline bundle directory, e.g. -``~/.vim/bundle/powerline/powerline/bindings/vim``. For vim-addon-manager it is -even easier since you don’t need to write this big path or install anything by -hand: ``powerline`` is installed and run just like any other plugin using +If you're using pathogen and don't want Powerline functionality in any other +applications, simply add Powerline as a bundle and point the path above to the +Powerline bundle directory, e.g. +``~/.vim/bundle/powerline/powerline/bindings/vim``. + +With Vundle you may instead use + +.. code-block:: vim + + Bundle 'Lokaltog/powerline', {'rtp': 'powerline/bindings/vim/'} + +(replace ``Bundle`` with ``NeoBundle`` for NeoBundle). + +For vim-addon-manager it is even easier since you don’t need to write this big +path or install anything by hand: ``powerline`` is installed and run just like +any other plugin using .. code-block:: vim call vam#ActivateAddons(['powerline']) +.. warning:: + *Never* install powerline with pathogen/VAM/Vundle/NeoBundle *and* with pip. + If you want powerline functionality in vim and other applications use + system-wide installation if your system has powerline package, pip-only or + ``pip install --editable`` kind of installation performed on the repository + installed by Vim plugin manager. + + If you have installed powerline with pip and with some of Vim package + managers do never report any errors to powerline bug tracker, especially + errors occurring after updates. + .. note:: If you use supplied :file:`powerline.vim` file to load powerline there are additional configuration variables available: ``g:powerline_pycmd`` and From 5173246a939f1a665d1908c536be6f04e6717ef1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 03:04:47 +0400 Subject: [PATCH 1130/1472] Add note about automatic vimrc resourcing Ref #297 Ref #296 Closes #213 --- docs/source/troubleshooting.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 136c2d67..5174b2d3 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -150,3 +150,28 @@ Prompt is spoiled after completing files in ksh This is exactly why powerline has official mksh support, but not official ksh support. If you know the solution feel free to share it in `powerline bug tracker`_. + +Powerline loses color after editing vimrc +----------------------------------------- + +If your vimrc has something like + +.. code-block:: vim + + autocmd! BufWritePost vimrc :source ~/.vimrc + +to automatically source vimrc after saving it you must then add ``nested`` after +pattern (``vimrc`` in this case): + +.. code-block:: vim + + autocmd! BufWritePost vimrc nested :source ~/.vimrc + +. Alternatively move ``:colorscheme`` command out of the vimrc to the file which +will not be automatically resourced. Observed problem is that when you use +``:colorscheme`` command existing highlighting groups are usually cleared, +including those defined by powerline. To workaround this issue powerline hooks +``Colorscheme`` event, but when you source vimrc with ``BufWritePost`` event, +but without ``nested`` this event is not launched. See also `autocmd-nested +`_ +Vim documentation. From d5081acf386bd107c1126ebc387582f7e7a20e04 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 03:21:39 +0400 Subject: [PATCH 1131/1472] Add note about minibufexpl Closes #230 --- docs/source/troubleshooting.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 5174b2d3..82b2cf9a 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -175,3 +175,31 @@ including those defined by powerline. To workaround this issue powerline hooks but without ``nested`` this event is not launched. See also `autocmd-nested `_ Vim documentation. + +Powerline loses color after saving any file +------------------------------------------- + +It may be one of the incarnations of the above issue: specifically minibufexpl +is known to trigger it. If you are using minibufexplorer you should set + +.. code-block:: vim + + let g:miniBufExplForceSyntaxEnable = 1 + +variable so that this issue is not triggered. Complete explanation: + +#. When MBE autocommand is executed it launches ``:syntax enable`` Vim command… +#. … which makes Vim source :file:`syntax/syntax.vim` file … +#. … which in turn sources :file:`syntax/synload.vim` … +#. … which executes ``:colorscheme`` command. Normally this command triggers + ``Colorscheme`` event, but in the first point minibufexplorer did set up + autocommands that miss ``nested`` attribute meaning that no events will be + triggered when processing MBE events. + +.. note:: + This setting was introduced in version 6.3.1 of `minibufexpl + `_ and removed in + version 6.5.0 of its successor `minibufexplorer + `_. It is highly + advised to use the latter because `minibufexpl`_ was last updated late in + 2004. From f615d85172fba1efd334e11149df6f794edc0aed Mon Sep 17 00:00:00 2001 From: Collin Grady Date: Sun, 3 Aug 2014 03:34:00 +0000 Subject: [PATCH 1132/1472] Added line_count segment for vim. Returns total number of lines in current buffer. Addresses #556 --- powerline/config_files/colorschemes/vim/__main__.json | 1 + powerline/segments/vim.py | 6 ++++++ tests/test_segments.py | 11 +++++++++++ 3 files changed, 18 insertions(+) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 6bfc59dc..16c5ac71 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -10,6 +10,7 @@ "file_directory": "information:additional", "file_name_empty": "file_directory", "line_percent": "information:additional", + "line_count": "line_current", "position": "information:additional" } } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 62f4fe25..5f55bb7b 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -334,6 +334,12 @@ def line_current(pl, segment_info): return str(segment_info['window'].cursor[0]) +@requires_segment_info +def line_count(pl, segment_info): + '''Return the line count of the current buffer.''' + return str(len(segment_info['buffer'])) + + @requires_segment_info def col_current(pl, segment_info): '''Return the current cursor column. diff --git a/tests/test_segments.py b/tests/test_segments.py index de467728..03ccba24 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -687,6 +687,17 @@ class TestVim(TestCase): finally: vim_module._bw(segment_info['bufnr']) + def test_line_count(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + segment_info['buffer'][0:-1] = [str(i) for i in range(99)] + try: + self.assertEqual(vim.line_count(pl=pl, segment_info=segment_info), '100') + vim_module._set_cursor(50, 0) + self.assertEqual(vim.line_count(pl=pl, segment_info=segment_info), '100') + finally: + vim_module._bw(segment_info['bufnr']) + def test_position(self): pl = Pl() segment_info = vim_module._get_segment_info() From 23ec5efb4149dca0fbd4ca26aa3787b7234298f4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 04:48:27 +0400 Subject: [PATCH 1133/1472] Fix required keys handling: do not require more keys then required --- powerline/lint/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 1976e876..ef29b4db 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -683,11 +683,10 @@ type_keys = { 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), } required_keys = { - 'function': set(), + 'function': set(('name',)), 'string': set(('contents',)), 'filler': set(), } -function_keys = set(('args', 'module')) highlight_keys = set(('highlight_group', 'name')) @@ -712,7 +711,7 @@ def check_key_compatibility(segment, data, context, echoerr): problem_mark=list(unknown_keys)[0].mark) hadproblem = True - if not (keys > required_keys[segment_type]): + if not (keys >= required_keys[segment_type]): missing_keys = required_keys[segment_type] - keys echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), context_mark=context[-1][1].mark, From 0ecd10ffb2600ad8e8a41b5979ac8ff3c0305db3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 04:49:01 +0400 Subject: [PATCH 1134/1472] Add ability to copy recursive specifications --- powerline/lint/__init__.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index ef29b4db..7882497f 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -93,15 +93,21 @@ class Spec(object): self.did_type = True return self - def copy(self): - return self.__class__()._update(self.__dict__) + def copy(self, copied=None): + copied = copied or {} + try: + return copied[id(self)] + except KeyError: + instance = self.__class__() + copied[id(self)] = instance + return self.__class__()._update(self.__dict__, copied) - def _update(self, d): + def _update(self, d, copied): self.__dict__.update(d) self.keys = copy(self.keys) self.checks = copy(self.checks) self.uspecs = copy(self.uspecs) - self.specs = [spec.copy() for spec in self.specs] + self.specs = [spec.copy(copied) for spec in self.specs] return self def unknown_spec(self, keyfunc, spec): From 70ae12b5121633e7e63ff6811fdd2abbb5b886f6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 07:45:03 +0400 Subject: [PATCH 1135/1472] Added tabline support No tests for it yet --- docs/source/configuration/reference.rst | 11 +- powerline/config_files/config.json | 2 + .../config_files/themes/vim/tabline.json | 32 ++++++ powerline/lint/__init__.py | 61 +++++----- powerline/renderer.py | 2 +- powerline/renderers/vim.py | 56 +++++---- powerline/segment.py | 108 +++++++++++++++++- powerline/segments/vim.py | 75 ++++++++++++ powerline/theme.py | 50 +------- powerline/vim.py | 7 +- 10 files changed, 301 insertions(+), 103 deletions(-) create mode 100644 powerline/config_files/themes/vim/tabline.json diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 1c3ee800..28c11863 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -272,6 +272,12 @@ Themes highlighting group is defined in the :ref:`highlight_group option `. + ``segments_list`` + Sub-list of segments. This list only allows :ref:`name + `, :ref:`segments + ` and :ref:`args + ` options. + ``module`` .. _config-themes-seg-module: @@ -282,7 +288,7 @@ Themes ``name`` .. _config-themes-seg-name: - Function name, only required for function segments. + Function name, only required for function and list segments. ``highlight_group`` .. _config-themes-seg-highlight_group: @@ -366,3 +372,6 @@ Themes Boolean. If false disables displaying of the segment. Defaults to ``True``. + + ``segments`` + A list of subsegments. diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index b918838d..f64bdfc1 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -39,6 +39,8 @@ "colorscheme": "default", "theme": "default", "local_themes": { + "__tabline__": "tabline", + "cmdwin": "cmdwin", "help": "help", "quickfix": "quickfix", diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json new file mode 100644 index 00000000..be07eaec --- /dev/null +++ b/powerline/config_files/themes/vim/tabline.json @@ -0,0 +1,32 @@ +{ + "default_module": "powerline.segments.vim", + "segments": { + "left": [ + { + "type": "segment_list", + "name": "tablister", + "segments": [ + { + "name": "file_directory", + "draw_soft_divider": false, + "priority": 40 + }, + { + "name": "file_name", + "args": { + "display_no_file": true + }, + "priority": 10 + } + ] + }, + { + "type": "string", + "highlight_group": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 7882497f..2d5dc292 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -385,6 +385,9 @@ class WithPath(object): def check_matcher_func(ext, match_name, data, context, echoerr): import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])] + if match_name == '__tabline__': + return True, False + match_module, separator, match_function = match_name.rpartition('.') if not separator: match_module = 'powerline.matchers.{0}'.format(ext) @@ -687,11 +690,13 @@ type_keys = { 'function': set(('args', 'module', 'draw_inner_divider')), 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), + 'segment_list': set(('segments', 'module', 'args', 'type')), } required_keys = { 'function': set(('name',)), 'string': set(('contents',)), 'filler': set(), + 'segment_list': set(('name', 'segments',)), } highlight_keys = set(('highlight_group', 'name')) @@ -867,7 +872,7 @@ def check_segment_name(name, data, context, echoerr): hadproblem = True return True, False, hadproblem - else: + elif context[-2][1].get('type') != 'segment_list': if name not in context[0][1].get('segment_data', {}): top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) if data['theme'] == top_theme_name: @@ -1075,32 +1080,34 @@ args_spec = Spec( ).unknown_spec(Spec(), Spec()).optional().copy highlight_group_spec = Spec().type(unicode).copy segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy -segments_spec = Spec().optional().list( - Spec( - type=Spec().oneof(type_keys).optional(), - name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(), - exclude_modes=Spec().list(vim_mode_spec()).optional(), - include_modes=Spec().list(vim_mode_spec()).optional(), - draw_hard_divider=Spec().type(bool).optional(), - draw_soft_divider=Spec().type(bool).optional(), - draw_inner_divider=Spec().type(bool).optional(), - display=Spec().type(bool).optional(), - module=segment_module_spec(), - priority=Spec().type(int, float, type(None)).optional(), - after=Spec().type(unicode).optional(), - before=Spec().type(unicode).optional(), - width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), - align=Spec().oneof(set('lr')).optional(), - args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)), - contents=Spec().type(unicode).optional(), - highlight_group=Spec().list( - highlight_group_spec().re('^(?:(?!:divider$).)+$', - lambda value: 'it is recommended that only divider highlight group names end with ":divider"') - ).func(check_highlight_groups).optional(), - divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$', - lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(), - ).func(check_full_segment_data), -).copy +sub_segments_spec = Spec() +segment_spec = Spec( + type=Spec().oneof(type_keys).optional(), + name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(), + exclude_modes=Spec().list(vim_mode_spec()).optional(), + include_modes=Spec().list(vim_mode_spec()).optional(), + draw_hard_divider=Spec().type(bool).optional(), + draw_soft_divider=Spec().type(bool).optional(), + draw_inner_divider=Spec().type(bool).optional(), + display=Spec().type(bool).optional(), + module=segment_module_spec(), + priority=Spec().type(int, float, type(None)).optional(), + after=Spec().type(unicode).optional(), + before=Spec().type(unicode).optional(), + width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), + align=Spec().oneof(set('lr')).optional(), + args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)), + contents=Spec().type(unicode).optional(), + highlight_group=Spec().list( + highlight_group_spec().re('^(?:(?!:divider$).)+$', + lambda value: 'it is recommended that only divider highlight group names end with ":divider"') + ).func(check_highlight_groups).optional(), + divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$', + lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(), + segments=sub_segments_spec, +).func(check_full_segment_data) +sub_segments_spec.optional().list(segment_spec) +segments_spec = Spec().optional().list(segment_spec).copy segdict_spec=Spec( left=segments_spec().context_message('Error while loading segments from left side (key {key})'), right=segments_spec().context_message('Error while loading segments from right side (key {key})'), diff --git a/powerline/renderer.py b/powerline/renderer.py index 002f777c..2def0725 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -222,7 +222,7 @@ class Renderer(object): segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode - segments = [self._get_highlighting(segment, mode) for segment in segments + segments = [self._get_highlighting(segment, segment['mode'] or mode) for segment in segments if mode not in segment['exclude_modes'] and (not segment['include_modes'] or mode in segment['include_modes'])] segments = [segment for segment in self._render_segments(theme, segments)] diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index b948dc0e..e788bdd2 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -53,15 +53,19 @@ class VimRenderer(Renderer): raise KeyError('There is already a local theme with given matcher') self.local_themes[matcher] = theme + def get_matched_theme(self, match): + try: + return match['theme'] + except KeyError: + match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) + return match['theme'] + def get_theme(self, matcher_info): + if matcher_info is None: + return self.get_matched_theme(self.local_themes[None]) for matcher in self.local_themes.keys(): - if matcher(matcher_info): - match = self.local_themes[matcher] - try: - return match['theme'] - except KeyError: - match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) - return match['theme'] + if matcher and matcher(matcher_info): + return self.get_matched_theme(self.local_themes[matcher]) else: return self.theme @@ -80,28 +84,34 @@ class VimRenderer(Renderer): def get_segment_info(self, segment_info, mode): return segment_info or self.segment_info - def render(self, window, window_id, winnr): + def render(self, window=None, window_id=None, winnr=None): '''Render all segments.''' - if window is vim.current.window: - mode = vim_mode(1) - mode = mode_translations.get(mode, mode) - else: - mode = 'nc' segment_info = self.segment_info.copy() - segment_info.update({ - 'window': window, - 'mode': mode, - 'window_id': window_id, - 'winnr': winnr, - }) - segment_info['buffer'] = segment_info['window'].buffer - segment_info['bufnr'] = segment_info['buffer'].number - winwidth = segment_info['window'].width + if window is not None: + if window is vim.current.window: + mode = vim_mode(1) + mode = mode_translations.get(mode, mode) + else: + mode = 'nc' + segment_info.update( + window=window, + mode=mode, + window_id=window_id, + winnr=winnr, + buffer=window.buffer, + ) + segment_info['bufnr'] = segment_info['buffer'].number + winwidth = segment_info['window'].width + matcher_info = segment_info + else: + mode = None + winwidth = int(vim.eval('&columns')) + matcher_info = None statusline = super(VimRenderer, self).render( mode=mode, width=winwidth, segment_info=segment_info, - matcher_info=segment_info, + matcher_info=matcher_info, ) return statusline diff --git a/powerline/segment.py b/powerline/segment.py index 836a5739..de85a7e4 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -44,6 +44,7 @@ segment_getters = { "function": get_function, "string": get_string, "filler": get_filler, + "segment_list": get_function, } @@ -59,6 +60,69 @@ def get_attr_func(contents_func, key, args): return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) +def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subsegments, patcher_args): + for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): + for subsegment in subsegments: + if subsegment_update: + subsegment = subsegment.copy() + subsegment.update(subsegment_update) + if subsegment_update['priority_multiplier'] and subsegment['priority']: + subsegment['priority'] *= subsegment_update['priority_multiplier'] + process_segment(pl, side, subsegment_info, parsed_segments, subsegment) + return None + + +def process_segment(pl, side, segment_info, parsed_segments, segment): + if segment['type'] in ('function', 'segment_list'): + pl.prefix = segment['name'] + try: + if segment['type'] == 'function': + contents = segment['contents_func'](pl, segment_info) + else: + contents = segment['contents_func'](pl, segment_info, parsed_segments, side) + except Exception as e: + pl.exception('Exception while computing segment: {0}', str(e)) + return + + if contents is None: + return + if isinstance(contents, list): + segment_base = segment.copy() + if contents: + draw_divider_position = -1 if side == 'left' else 0 + for key, i, newval in ( + ('before', 0, ''), + ('after', -1, ''), + ('draw_soft_divider', draw_divider_position, True), + ('draw_hard_divider', draw_divider_position, True), + ): + try: + contents[i][key] = segment_base.pop(key) + segment_base[key] = newval + except KeyError: + pass + + draw_inner_divider = None + if side == 'right': + append = parsed_segments.append + else: + pslen = len(parsed_segments) + append = lambda item: parsed_segments.insert(pslen, item) + + for subsegment in (contents if side == 'right' else reversed(contents)): + segment_copy = segment_base.copy() + segment_copy.update(subsegment) + if draw_inner_divider is not None: + segment_copy['draw_soft_divider'] = draw_inner_divider + draw_inner_divider = segment_copy.pop('draw_inner_divider', None) + append(segment_copy) + else: + segment['contents'] = contents + parsed_segments.append(segment) + elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): + parsed_segments.append(segment) + + def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=None): data = { 'default_module': default_module or 'powerline.segments.' + ext, @@ -90,8 +154,48 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non else: highlight_group = segment.get('highlight_group') or segment.get('name') - if segment_type == 'function': + if segment_type in ('function', 'segment_list'): args = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) + + if segment_type == 'segment_list': + # Handle startup and shutdown of _contents_func? + subsegments = [ + get(subsegment, side) + for subsegment in segment['segments'] + ] + return { + 'name': segment.get('name'), + 'type': segment_type, + 'highlight_group': None, + 'divider_highlight_group': None, + 'before': None, + 'after': None, + 'contents_func': lambda pl, segment_info, parsed_segments, side: process_segment_lister( + pl, segment_info, parsed_segments, side, + patcher_args=args, + subsegments=subsegments, + lister=_contents_func, + ), + 'contents': None, + 'priority': None, + 'draw_soft_divider': None, + 'draw_hard_divider': None, + 'side': side, + 'exclude_modes': segment.get('exclude_modes', []), + 'include_modes': segment.get('include_modes', []), + 'width': None, + 'align': None, + 'startup': None, + 'shutdown': None, + 'mode': None, + '_rendered_raw': '', + '_rendered_hl': '', + '_len': 0, + '_space_left': 0, + '_space_right': 0, + } + + if segment_type == 'function': startup_func = get_attr_func(_contents_func, 'startup', args) shutdown_func = get_attr_func(_contents_func, 'shutdown', None) @@ -117,7 +221,6 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'after': get_key(segment, module, 'after', ''), 'contents_func': contents_func, 'contents': contents, - 'args': args if segment_type == 'function' else {}, 'priority': segment.get('priority', None), 'draw_hard_divider': segment.get('draw_hard_divider', True), 'draw_soft_divider': segment.get('draw_soft_divider', True), @@ -129,6 +232,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'align': segment.get('align', 'l'), 'startup': startup_func, 'shutdown': shutdown_func, + 'mode': None, '_rendered_raw': '', '_rendered_hl': '', '_len': 0, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 5f55bb7b..03e0f3b0 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -485,3 +485,78 @@ def trailing_whitespace(pl, segment_info): ret = None trailing_whitespace_cache[bufnr] = (changedtick, ret) return ret + + +if hasattr(vim, 'vvars') and vim.vvars['version'] >= 704: + def updated_segment_info(segment_info, tabpage): + segment_info = segment_info.copy() + window = tabpage.window + buffer = window.buffer + segment_info.update( + tabpage=tabpage, + tabnr=tabpage.number, + window=window, + winnr=window.number, + window_id=window.vars.get('powerline_window_id'), + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info + + list_tabpages = lambda: vim.tabpages + current_tabpage = lambda: vim.current.tabpage + tabpage_nr = lambda tabpage: tabpage.number +else: + def updated_segment_info(segment_info, tabnr): # NOQA + segment_info = segment_info.copy() + winnr = int(vim.eval('tabpagewinnr({0})'.format(tabnr))) + bufnr = int(vim.eval('tabpagebuflist({0})[{1}]'.format(tabnr, winnr - 1))) + buffer = None + for buffer in vim.buffers: + if buffer.number == bufnr: + break + window_id = vim.eval('gettabwinvar({0}, {1}, "powerline_window_id")'.format(tabnr, winnr)) + window_id = int(window_id) if window_id else None + segment_info.update( + tabpage=None, + tabnr=tabnr, + window=None, + winnr=winnr, + window_id=window_id, + buffer=buffer, + bufnr=bufnr, + ) + + list_tabpages = lambda: range(1, int(vim.eval('tabpagenr("$")')) + 1) # NOQA + current_tabpage = lambda: int(vim.eval('tabpagenr()')) # NOQA + tabpage_nr = lambda tabnr: tabnr # NOQA + + +@requires_segment_info +def tablister(pl, segment_info): + '''List all tab pages in segment_info format + + Specifically generates a list of segment info dictionaries with ``window``, + ``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local + ones and additional ``tabpage`` and ``tabnr`` keys. + + Sets segment ``mode`` to either ``tab`` (for current tab page) or ``nc`` + (for all other tab pages). + + Works best with vim-7.4 or later: earlier versions miss tabpage object and + thus window objects are not available as well. + ''' + cur_tabpage = current_tabpage() + cur_tabnr = tabpage_nr(cur_tabpage) + + def add_multiplier(tabpage, dct): + dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage_nr(tabpage) - cur_tabnr)) + return dct + + return [ + ( + updated_segment_info(segment_info, tabpage), + add_multiplier(tabpage, {'mode': ('tab' if tabpage == cur_tabpage else 'nc')}) + ) + for tabpage in list_tabpages() + ] diff --git a/powerline/theme.py b/powerline/theme.py index 9de033a9..2d382d64 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from powerline.segment import gen_segment_getter +from powerline.segment import gen_segment_getter, process_segment from powerline.lib.unicode import u import itertools @@ -91,53 +91,7 @@ class Theme(object): for side in [side] if side else ['left', 'right']: parsed_segments = [] for segment in self.segments[line][side]: - if segment['type'] == 'function': - self.pl.prefix = segment['name'] - try: - contents = segment['contents_func'](self.pl, segment_info) - except Exception as e: - self.pl.exception('Exception while computing segment: {0}', str(e)) - continue - - if contents is None: - continue - if isinstance(contents, list): - segment_base = segment.copy() - if contents: - draw_divider_position = -1 if side == 'left' else 0 - for key, i, newval in ( - ('before', 0, ''), - ('after', -1, ''), - ('draw_soft_divider', draw_divider_position, True), - ('draw_hard_divider', draw_divider_position, True), - ): - try: - contents[i][key] = segment_base.pop(key) - segment_base[key] = newval - except KeyError: - pass - - draw_inner_divider = None - if side == 'right': - append = parsed_segments.append - else: - pslen = len(parsed_segments) - append = lambda item: parsed_segments.insert(pslen, item) - - for subsegment in (contents if side == 'right' else reversed(contents)): - segment_copy = segment_base.copy() - segment_copy.update(subsegment) - if draw_inner_divider is not None: - segment_copy['draw_soft_divider'] = draw_inner_divider - draw_inner_divider = segment_copy.pop('draw_inner_divider', None) - append(segment_copy) - else: - segment['contents'] = contents - parsed_segments.append(segment) - elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): - parsed_segments.append(segment) - else: - continue + process_segment(self.pl, side, segment_info, parsed_segments, segment) for segment in parsed_segments: segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents diff --git a/powerline/vim.py b/powerline/vim.py index 05d42de4..8afae555 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -72,7 +72,8 @@ class VimPowerline(Powerline): return {} self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) - return dict(((self.get_matcher(key), {'config': self.load_theme_config(val)}) + return dict(((None if key == '__tabline__' else self.get_matcher(key), + {'config': self.load_theme_config(val)}) for key, val in local_themes.items())) def get_config_paths(self): @@ -141,6 +142,9 @@ class VimPowerline(Powerline): return 'No window {0}'.format(window_id) return self.render(window, window_id, winnr) + def tabline(self): + return self.render() + def new_window(self): window, window_id, winnr = self.win_idx(None) return self.render(window, window_id, winnr) @@ -202,3 +206,4 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): # Is immediately changed after new_window function is run. Good for global # value. vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format(pyeval=pyeval)) + vim.command('set tabline=%!{pyeval}(\'powerline.tabline()\')'.format(pyeval=pyeval)) From ff91ff7f92a74bfecc219860d935791d8471f0b5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 09:07:31 +0400 Subject: [PATCH 1136/1472] Update tests to work with new functionality Still no tests for new functionality. --- powerline/segments/vim.py | 1 + tests/test_provided_config_files.py | 4 +- tests/vim.py | 257 ++++++++++++++++------------ 3 files changed, 153 insertions(+), 109 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 03e0f3b0..5a8d53f0 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -526,6 +526,7 @@ else: buffer=buffer, bufnr=bufnr, ) + return segment_info list_tabpages = lambda: range(1, int(vim.eval('tabpagenr("$")')) + 1) # NOQA current_tabpage = lambda: int(vim.eval('tabpagenr()')) # NOQA diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 64e9d6a6..a6371588 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -32,7 +32,7 @@ class TestConfig(TestCase): local_themes_raw = json.load(f)['ext']['vim']['local_themes'] # Don't run tests on external/plugin segments local_themes = dict((k, v) for (k, v) in local_themes_raw.items()) - self.assertEqual(len(buffers), len(local_themes)) + self.assertEqual(len(buffers), len(local_themes) - 1) outputs = {} i = 0 @@ -53,6 +53,8 @@ class TestConfig(TestCase): outputs[out] = (i, (args, kwargs), mode) with vim_module._with('bufname', '/tmp/foo.txt'): + out = powerline.render() + outputs[out] = (-1, (None, None), 'tab') with vim_module._with('globals', powerline_config_path=cfg_path): exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) try: diff --git a/tests/vim.py b/tests/vim.py index 0c0cefaa..a939cab5 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -2,12 +2,13 @@ _log = [] vars = {} vvars = {'version': 703} -_window = 0 +_tabpage = 0 _mode = 'n' _buf_purge_events = set() options = { 'paste': 0, 'ambiwidth': 'single', + 'columns': 80, } _last_bufnr = 0 _highlights = {} @@ -29,6 +30,12 @@ def _set_thread_id(): _set_thread_id() +def _print_log(): + for item in _log: + print (item) + _log[:] = () + + def _vim(func): from functools import wraps from threading import current_thread @@ -67,6 +74,10 @@ class _Buffers(object): def __init__(self): self.d = {} + @_vim + def __len__(self): + return len(self.d) + @_vim def __getitem__(self, item): return self.d[item] @@ -75,6 +86,10 @@ class _Buffers(object): def __setitem__(self, item, value): self.d[item] = value + @_vim + def __iter__(self): + return iter(self.d.values()) + @_vim def __contains__(self, item): return item in self.d @@ -84,29 +99,26 @@ class _Buffers(object): return bool(self.d) @_vim - def keys(self): + def _keys(self): return self.d.keys() @_vim - def pop(self, *args, **kwargs): + def _pop(self, *args, **kwargs): return self.d.pop(*args, **kwargs) buffers = _Buffers() -class _Windows(object): +class _ObjList(object): @_vim - def __init__(self): + def __init__(self, objtype): self.l = [] + self.objtype = objtype @_vim def __getitem__(self, item): - return self.l[item] - - @_vim - def __setitem__(self, item, value): - self.l[item] = value + return self.l[item - int(item > 0)] @_vim def __len__(self): @@ -128,13 +140,12 @@ class _Windows(object): def _append(self, *args, **kwargs): return self.l.append(*args, **kwargs) - -windows = _Windows() - - -@_vim -def _buffer(): - return windows[_window - 1].buffer.number + @_vim + def _new(self, *args, **kwargs): + number = len(self) + 1 + new_obj = self.objtype(number, *args, **kwargs) + self._append(new_obj) + return new_obj def _construct_result(r): @@ -238,12 +249,32 @@ def eval(expr): raise NotImplementedError winnr = int(match.group(1)) varname = match.group(2) - return 0 + (varname in windows[winnr - 1].vars) + return 0 + (varname in current.tabpage.windows[winnr].vars) elif expr == 'getbufvar("%", "NERDTreeRoot").path.str()': import os - assert os.path.basename(buffers[_buffer()].name).startswith('NERD_tree_') + assert os.path.basename(current.buffer.name).startswith('NERD_tree_') return '/usr/include' - raise NotImplementedError + elif expr == 'tabpagenr()': + return current.tabpage.number + elif expr == 'tabpagenr("$")': + return len(tabpages) + elif expr.startswith('tabpagewinnr('): + tabnr = int(expr[len('tabpagewinnr('):-1]) + return tabpages[tabnr].window.number + elif expr.startswith('tabpagebuflist('): + import re + match = re.match(r'tabpagebuflist\((\d+)\)\[(\d+)\]', expr) + tabnr = int(match.group(1)) + winnr = int(match.group(2)) + 1 + return tabpages[tabnr].windows[winnr].buffer.number + elif expr.startswith('gettabwinvar('): + import re + match = re.match(r'gettabwinvar\((\d+), (\d+), "(\w+)"\)', expr) + tabnr = int(match.group(1)) + winnr = int(match.group(2)) + varname = match.group(3) + return tabpages[tabnr].windows[winnr].vars[varname] + raise NotImplementedError(expr) @_vim @@ -277,7 +308,7 @@ def _emul_getbufvar(bufnr, varname): import re if varname[0] == '&': if bufnr == '%': - bufnr = buffers[_buffer()].number + bufnr = current.buffer.number if bufnr not in buffers: return '' try: @@ -289,7 +320,7 @@ def _emul_getbufvar(bufnr, varname): return '' elif re.match('^[a-zA-Z_]+$', varname): if bufnr == '%': - bufnr = buffers[_buffer()].number + bufnr = current.buffer.number if bufnr not in buffers: return '' return buffers[bufnr].vars[varname] @@ -299,25 +330,25 @@ def _emul_getbufvar(bufnr, varname): @_vim @_str_func def _emul_getwinvar(winnr, varname): - return windows[winnr - 1].vars.get(varname, '') + return current.tabpage.windows[winnr].vars.get(varname, '') @_vim def _emul_setwinvar(winnr, varname, value): - windows[winnr - 1].vars[varname] = value + current.tabpage.windows[winnr].vars[varname] = value @_vim def _emul_virtcol(expr): if expr == '.' or isinstance(expr, list): - return windows[_window - 1].cursor[1] + 1 + return current.window.cursor[1] + 1 raise NotImplementedError @_vim def _emul_getpos(expr): if expr == '.' or expr == 'v': - return [0, windows[_window - 1].cursor[0] + 1, windows[_window - 1].cursor[1] + 1, 0] + return [0, current.window.cursor[0] + 1, current.window.cursor[1] + 1, 0] raise NotImplementedError @@ -342,7 +373,7 @@ def _emul_fnamemodify(path, modstring): def _emul_expand(expr): global _abuf if expr == '': - return _abuf or _buffer() + return _abuf or current.buffer.number raise NotImplementedError @@ -362,7 +393,7 @@ def _emul_exists(varname): @_vim def _emul_line2byte(line): - buflines = _buf_lines[_buffer()] + buflines = current.buffer._buf_lines if line == len(buflines) + 1: return sum((len(s) for s in buflines)) + 1 raise NotImplementedError @@ -370,8 +401,8 @@ def _emul_line2byte(line): @_vim def _emul_line(expr): - cursorline = windows[_window - 1].cursor[0] + 1 - numlines = len(_buf_lines[_buffer()]) + cursorline = current.window.cursor[0] + 1 + numlines = len(current.buffer._buf_lines) if expr == 'w0': return max(cursorline - 5, 1) if expr == 'w$': @@ -395,16 +426,15 @@ def _emul_bufname(bufnr): return b'' -_window_ids = [None] _window_id = 0 class _Window(object): - def __init__(self, buffer=None, cursor=(1, 0), width=80): + def __init__(self, number, buffer=None, cursor=(1, 0), width=80): global _window_id self.cursor = cursor self.width = width - self.number = len(windows) + 1 + self.number = number if buffer: if type(buffer) is _Buffer: self.buffer = buffer @@ -412,19 +442,39 @@ class _Window(object): self.buffer = _Buffer(**buffer) else: self.buffer = _Buffer() - windows._append(self) _window_id += 1 - _window_ids.append(_window_id) + self._window_id = _window_id self.options = {} - self.vars = {} + self.vars = { + 'powerline_window_id': self._window_id, + } def __repr__(self): return '' -_buf_lines = {} -_undostate = {} -_undo_written = {} +class _Tabpage(object): + def __init__(self, number): + self.windows = _ObjList(_Window) + self.number = number + + def _new_window(self, **kwargs): + self.window = self.windows._new(**kwargs) + return self.window + + def _close_window(self, winnr): + curwinnr = self.window.number + win = self.windows._pop(winnr - 1) + if self.windows and winnr == curwinnr: + self.window = self.windows[-1] + else: + current.tabpage._new_window() + return win + + +tabpages = _ObjList(_Tabpage) + + _abuf = None @@ -446,10 +496,9 @@ class _Buffer(object): 'fileencoding': 'utf-8', 'textwidth': 80, } - _buf_lines[bufnr] = [''] - from copy import copy - _undostate[bufnr] = [copy(_buf_lines[bufnr])] - _undo_written[bufnr] = len(_undostate[bufnr]) + self._buf_lines = [''] + self._undostate = [self._buf_lines[:]] + self._undo_written = len(self._undostate) buffers[bufnr] = self @property @@ -471,27 +520,27 @@ class _Buffer(object): self._name = os.path.abspath(name) def __getitem__(self, line): - return _buf_lines[self.number][line] + return self._buf_lines[line] def __setitem__(self, line, value): self.options['modified'] = 1 self.vars['changedtick'] += 1 - _buf_lines[self.number][line] = value + self._buf_lines[line] = value from copy import copy - _undostate[self.number].append(copy(_buf_lines[self.number])) + self._undostate.append(copy(self._buf_lines)) def __setslice__(self, *args): self.options['modified'] = 1 self.vars['changedtick'] += 1 - _buf_lines[self.number].__setslice__(*args) + self._buf_lines.__setslice__(*args) from copy import copy - _undostate[self.number].append(copy(_buf_lines[self.number])) + self._undostate.append(copy(self._buf_lines)) def __getslice__(self, *args): - return _buf_lines[self.number].__getslice__(*args) + return self._buf_lines.__getslice__(*args) def __len__(self): - return len(_buf_lines[self.number]) + return len(self._buf_lines) def __repr__(self): return '' @@ -514,22 +563,20 @@ class _Buffer(object): exec(event, __main__.__dict__) finally: _abuf = None - if _buf_lines: - _buf_lines.pop(bufnr) - if _undostate: - _undostate.pop(bufnr) - if _undo_written: - _undo_written.pop(bufnr) class _Current(object): @property def buffer(self): - return buffers[_buffer()] + return self.window.buffer @property def window(self): - return windows[_window - 1] + return self.tabpage.window + + @property + def tabpage(self): + return tabpages[_tabpage - 1] current = _Current() @@ -549,7 +596,7 @@ def _init(): for varname, value in globals().items(): if varname[0] != '_': _dict[varname] = value - _new() + _tabnew() return _dict @@ -561,12 +608,14 @@ def _get_segment_info(): } mode = _mode mode = mode_translations.get(mode, mode) + window = current.window + buffer = current.buffer return { - 'window': windows[_window - 1], - 'winnr': _window, - 'buffer': buffers[_buffer()], - 'bufnr': _buffer(), - 'window_id': _window_ids[_window], + 'window': window, + 'winnr': window.number, + 'buffer': buffer, + 'bufnr': buffer.number, + 'window_id': window._window_id, 'mode': mode, } @@ -588,85 +637,77 @@ def _start_mode(mode): @_vim def _undo(): - if len(_undostate[_buffer()]) == 1: + if len(current.buffer._undostate) == 1: return - _undostate[_buffer()].pop(-1) - _buf_lines[_buffer()] = _undostate[_buffer()][-1] - buf = current.buffer - if _undo_written[_buffer()] == len(_undostate[_buffer()]): - buf.options['modified'] = 0 + buffer = current.buffer + buffer._undostate.pop(-1) + buffer._buf_lines = buffer._undostate[-1] + if buffer._undo_written == len(buffer._undostate): + buffer.options['modified'] = 0 @_vim def _edit(name=None): - global _last_bufnr - if _buffer() and buffers[_buffer()].name is None: - buf = buffers[_buffer()] - buf.name = name + if current.buffer.name is None: + buffer = current.buffer + buffer.name = name else: - buf = _Buffer(name) - windows[_window - 1].buffer = buf + buffer = _Buffer(name) + current.window.buffer = buffer + + +@_vim +def _tabnew(name=None): + global windows + tabpage = tabpages._new() + windows = tabpage.windows + _tabpage = len(tabpages) + _new(name) @_vim def _new(name=None): - global _window - _Window(buffer={'name': name}) - _window = len(windows) + current.tabpage._new_window(buffer={'name': name}) @_vim def _split(): - global _window - _Window(buffer=buffers[_buffer()]) - _window = len(windows) - - -@_vim -def _del_window(winnr): - win = windows._pop(winnr - 1) - _window_ids.pop(winnr) - return win + current.tabpage._new_window(buffer=current.buffer) @_vim def _close(winnr, wipe=True): - global _window - win = _del_window(winnr) - if _window == winnr: - _window = len(windows) + win = current.tabpage._close_window(winnr) if wipe: - for w in windows: + for w in current.tabpage.windows: if w.buffer.number == win.buffer.number: break else: _bw(win.buffer.number) - if not windows: - _Window() @_vim def _bw(bufnr=None): - bufnr = bufnr or _buffer() + bufnr = bufnr or current.buffer.number winnr = 1 - for win in windows: + for win in current.tabpage.windows: if win.buffer.number == bufnr: _close(winnr, wipe=False) winnr += 1 - buffers.pop(bufnr) + buffers._pop(bufnr) if not buffers: _Buffer() - _b(max(buffers.keys())) + _b(max(buffers._keys())) @_vim def _b(bufnr): - windows[_window - 1].buffer = buffers[bufnr] + current.window.buffer = buffers[bufnr] @_vim def _set_cursor(line, col): - windows[_window - 1].cursor = (line, col) + current.window.cursor = (line, col) if _mode == 'n': _launch_event('CursorMoved') elif _mode == 'i': @@ -675,12 +716,12 @@ def _set_cursor(line, col): @_vim def _get_buffer(): - return buffers[_buffer()] + return current.buffer @_vim def _set_bufoption(option, value, bufnr=None): - buffers[bufnr or _buffer()].options[option] = value + buffers[bufnr or current.buffer.number].options[option] = value if option == 'filetype': _launch_event('FileType') @@ -691,7 +732,7 @@ class _WithNewBuffer(object): def __enter__(self): self.call() - self.bufnr = _buffer() + self.bufnr = current.buffer.number return _get_segment_info() def __exit__(self, *args): @@ -720,7 +761,7 @@ class _WithBufOption(object): self.new = new def __enter__(self): - self.buffer = buffers[_buffer()] + self.buffer = current.buffer self.old = _set_dict(self.buffer.options, self.new, _set_bufoption)[0] def __exit__(self, *args): @@ -768,7 +809,7 @@ class _WithBufName(object): def __enter__(self): import os - buffer = buffers[_buffer()] + buffer = current.buffer self.buffer = buffer self.old = buffer.name buffer.name = self.new @@ -795,7 +836,7 @@ def _with(key, *args, **kwargs): elif key == 'globals': return _WithDict(vars, **kwargs) elif key == 'wvars': - return _WithDict(windows[_window - 1].vars, **kwargs) + return _WithDict(current.window.vars, **kwargs) elif key == 'environ': return _WithDict(_environ, **kwargs) elif key == 'split': From 07c533d1b2972c2921693328723249f7cef53988 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 09:16:30 +0400 Subject: [PATCH 1137/1472] Test tabline in test_tabline.vim file --- tests/test_plugin_file.vim | 4 +++- tests/test_tabline.vim | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100755 tests/test_tabline.vim diff --git a/tests/test_plugin_file.vim b/tests/test_plugin_file.vim index 02d916fd..76e08bd8 100755 --- a/tests/test_plugin_file.vim +++ b/tests/test_plugin_file.vim @@ -1,5 +1,7 @@ #!/usr/bin/vim -S set nocompatible +tabedit abc +tabedit def try source powerline/bindings/vim/plugin/powerline.vim catch @@ -16,4 +18,4 @@ if len(mess)>1 call writefile(mess, 'message.fail') cquit endif -quit! +qall! diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim new file mode 100755 index 00000000..e96624c5 --- /dev/null +++ b/tests/test_tabline.vim @@ -0,0 +1,18 @@ +#!/usr/bin/vim -S +source powerline/bindings/vim/plugin/powerline.vim +edit abc +tabedit def +tabedit ghi +try + let &columns = 80 + let result = eval(&tabline[2:]) +catch + call writefile(['Exception while evaluating &tabline', v:exception], 'message.fail') + cquit +endtry + +if result isnot# '%#Pl_240_5789784_235_2500134_NONE# ./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                       ' + call writefile(['Unexpected result', result], 'message.fail') + cquit +endif +qall! From b99ad3904ee3e139b56e728c99ad20384b60981c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 10:12:54 +0400 Subject: [PATCH 1138/1472] Remove some __nonzero__ methods in tests/vim.py Just __len__ is good enough here --- tests/vim.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index a939cab5..5df98764 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -94,10 +94,6 @@ class _Buffers(object): def __contains__(self, item): return item in self.d - @_vim - def __nonzero__(self): - return bool(self.d) - @_vim def _keys(self): return self.d.keys() @@ -128,10 +124,6 @@ class _ObjList(object): def __iter__(self): return iter(self.l) - @_vim - def __nonzero__(self): - return not not self.l - @_vim def _pop(self, *args, **kwargs): return self.l.pop(*args, **kwargs) From 3148acfef121b83dc995df3595104e5c612a6771 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 10:13:44 +0400 Subject: [PATCH 1139/1472] Set __bool__ to __nonzero__ in DelayedEchoErr Method __nonzero__ was renamed to __bool__ in Python-3 --- powerline/lint/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 2d5dc292..9b3661e7 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -71,6 +71,8 @@ class DelayedEchoErr(EchoErr): def __nonzero__(self): return not not self.errs + __bool__ = __nonzero__ + class Spec(object): def __init__(self, **keys): From 0403f7af1a09e3eb58ea7de9de58e02ff54fd9b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 11:56:41 +0400 Subject: [PATCH 1140/1472] Fix renderer length computation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current sum() of once computed _len’s is completely inappropriate in case removal of the segment caused change in divider lengths: addition or removal of dividers or change of the divider type when dividers have different length. Also contains some optimizations: first of all _render_segments is called only once always, same for strwidth() function for each string. Space is considired to always have length 1. And do not bother computing any length if no width was specified. --- powerline/renderer.py | 154 +++++++++++++++++++++++++++++++----------- 1 file changed, 113 insertions(+), 41 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 2def0725..6ee245aa 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -222,20 +222,44 @@ class Renderer(object): segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode - segments = [self._get_highlighting(segment, segment['mode'] or mode) for segment in segments - if mode not in segment['exclude_modes'] and (not segment['include_modes'] or mode in segment['include_modes'])] - - segments = [segment for segment in self._render_segments(theme, segments)] + segments = [ + self._get_highlighting(segment, segment['mode'] or mode) + for segment in segments + if ( + mode not in segment['exclude_modes'] + and ( + not segment['include_modes'] + or mode in segment['include_modes'] + ) + ) + ] if not width: # No width specified, so we don't need to crop or pad anything - return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) + return construct_returned_value(''.join([ + segment['_rendered_hl'] + for segment in self._render_segments(theme, segments) + ]) + self.hlstyle(), segments, output_raw) + + divider_lengths = { + 'left': { + 'hard': self.strwidth(theme.get_divider('left', 'hard')), + 'soft': self.strwidth(theme.get_divider('left', 'soft')), + }, + 'right': { + 'hard': self.strwidth(theme.get_divider('right', 'hard')), + 'soft': self.strwidth(theme.get_divider('right', 'soft')), + }, + } + + length = self._render_length(theme, segments, divider_lengths) # Create an ordered list of segments that can be dropped segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) - while sum([segment['_len'] for segment in segments]) > width and len(segments_priority): - segments.remove(segments_priority[0]) - segments_priority.pop(0) + for segment in segments_priority: + if self._render_length(theme, segments, divider_lengths) <= width: + break + segments.remove(segment) # Distribute the remaining space on spacer segments segments_spacers = [segment for segment in segments if segment['width'] == 'auto'] @@ -256,6 +280,38 @@ class Renderer(object): return construct_returned_value(rendered_highlighted, segments, output_raw) + def _render_length(self, theme, segments, divider_lengths): + '''Update segments lengths and return them + ''' + segments_len = len(segments) + ret = 0 + divider_spaces = theme.get_spaces() + for index, segment in enumerate(segments): + side = segment['side'] + if segment['_contents_len'] is None: + segment_len = segment['_contents_len'] = self.strwidth(segment['contents']) + else: + segment_len = segment['_contents_len'] + + prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT + next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT + compare_segment = next_segment if side == 'left' else prev_segment + divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' + + outer_padding = int(bool( + (index == 0 and side == 'left') or + (index == segments_len - 1 and side == 'right') + )) + + draw_divider = segment['draw_' + divider_type + '_divider'] + segment_len += segment['_space_left'] + segment['_space_right'] + outer_padding + if draw_divider: + segment_len += divider_lengths[side][divider_type] + divider_spaces + + segment['_len'] = segment_len + ret += segment_len + return ret + def _render_segments(self, theme, segments, render_highlighted=True): '''Internal segment rendering method. @@ -268,19 +324,19 @@ class Renderer(object): statusline if render_highlighted is True. ''' segments_len = len(segments) + divider_spaces = theme.get_spaces() for index, segment in enumerate(segments): - segment['_rendered_raw'] = '' - segment['_rendered_hl'] = '' - + side = segment['side'] prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT - compare_segment = next_segment if segment['side'] == 'left' else prev_segment - outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else '' + compare_segment = next_segment if side == 'left' else prev_segment + outer_padding = int(bool( + (index == 0 and side == 'left') or + (index == segments_len - 1 and side == 'right') + )) * ' ' divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' - divider_raw = theme.get_divider(segment['side'], divider_type) - divider_spaces = theme.get_spaces() divider_highlighted = '' contents_raw = segment['contents'] contents_highlighted = '' @@ -288,48 +344,64 @@ class Renderer(object): # Pad segments first if draw_divider: - if segment['side'] == 'left': - contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + ((divider_spaces + segment['_space_right']) * ' ') + divider_raw = theme.get_divider(side, divider_type).replace(' ', NBSP) + if side == 'left': + contents_raw = ( + outer_padding + (segment['_space_left'] * ' ') + + contents_raw + + ((divider_spaces + segment['_space_right']) * ' ') + ) else: - contents_raw = ((divider_spaces + segment['_space_left']) * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding + contents_raw = ( + ((divider_spaces + segment['_space_left']) * ' ') + + contents_raw + + (segment['_space_right'] * ' ') + outer_padding + ) else: - if segment['side'] == 'left': - contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + if side == 'left': + contents_raw = ( + outer_padding + (segment['_space_left'] * ' ') + + contents_raw + + (segment['_space_right'] * ' ') + ) else: - contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding + contents_raw = ( + (segment['_space_left'] * ' ') + + contents_raw + + (segment['_space_right'] * ' ') + outer_padding + ) # Replace spaces with no-break spaces - divider_raw = divider_raw.replace(' ', NBSP) contents_raw = contents_raw.translate(self.np_character_translations) # Apply highlighting to padded dividers and contents if render_highlighted: - if divider_type == 'soft': - divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' - divider_fg = segment[divider_highlight_group_key]['fg'] - divider_bg = segment[divider_highlight_group_key]['bg'] - else: - divider_fg = segment['highlight']['bg'] - divider_bg = compare_segment['highlight']['bg'] - divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + if draw_divider: + if divider_type == 'soft': + divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' + divider_fg = segment[divider_highlight_group_key]['fg'] + divider_bg = segment[divider_highlight_group_key]['bg'] + else: + divider_fg = segment['highlight']['bg'] + divider_bg = compare_segment['highlight']['bg'] + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) # Append padded raw and highlighted segments to the rendered segment variables if draw_divider: - if segment['side'] == 'left': - segment['_rendered_raw'] += contents_raw + divider_raw - segment['_rendered_hl'] += contents_highlighted + divider_highlighted + if side == 'left': + segment['_rendered_raw'] = contents_raw + divider_raw + segment['_rendered_hl'] = contents_highlighted + divider_highlighted else: - segment['_rendered_raw'] += divider_raw + contents_raw - segment['_rendered_hl'] += divider_highlighted + contents_highlighted + segment['_rendered_raw'] = divider_raw + contents_raw + segment['_rendered_hl'] = divider_highlighted + contents_highlighted else: - if segment['side'] == 'left': - segment['_rendered_raw'] += contents_raw - segment['_rendered_hl'] += contents_highlighted + if side == 'left': + segment['_rendered_raw'] = contents_raw + segment['_rendered_hl'] = contents_highlighted else: - segment['_rendered_raw'] += contents_raw - segment['_rendered_hl'] += contents_highlighted - segment['_len'] = self.strwidth(segment['_rendered_raw']) + segment['_rendered_raw'] = contents_raw + segment['_rendered_hl'] = contents_highlighted yield segment @classmethod From 1907708e98394eccd2741d9def1128e8334f9d6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 12:00:05 +0400 Subject: [PATCH 1141/1472] Add support for has_key(gettabwinvar()) in vim test module --- tests/vim.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index 5df98764..3edf2862 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -230,18 +230,25 @@ def eval(expr): import re match = re.match(r'^getwinvar\((\d+), "(\w+)"\)$', expr) if not match: - raise NotImplementedError + raise NotImplementedError(expr) winnr = int(match.group(1)) varname = match.group(2) return _emul_getwinvar(winnr, varname) elif expr.startswith('has_key('): import re match = re.match(r'^has_key\(getwinvar\((\d+), ""\), "(\w+)"\)$', expr) - if not match: - raise NotImplementedError - winnr = int(match.group(1)) - varname = match.group(2) - return 0 + (varname in current.tabpage.windows[winnr].vars) + if match: + winnr = int(match.group(1)) + varname = match.group(2) + return 0 + (varname in current.tabpage.windows[winnr].vars) + else: + match = re.match(r'^has_key\(gettabwinvar\((\d+), (\d+), ""\), "(\w+)"\)$', expr) + if not match: + raise NotImplementedError(expr) + tabnr = int(match.group(1)) + winnr = int(match.group(2)) + varname = match.group(3) + return 0 + (varname in tabpages[tabnr].windows[winnr].vars) elif expr == 'getbufvar("%", "NERDTreeRoot").path.str()': import os assert os.path.basename(current.buffer.name).startswith('NERD_tree_') From f02399b617da476bfc57fe4e9c2e709c417452e1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 12:03:44 +0400 Subject: [PATCH 1142/1472] Add tabpage and tabnr arguments to segment_info --- powerline/bindings/vim/__init__.py | 79 ++++++++++++++++++++++++++++-- powerline/renderers/vim.py | 4 +- powerline/segment.py | 7 ++- powerline/segments/vim.py | 67 +++++++------------------ 4 files changed, 103 insertions(+), 54 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 6830365c..76c40ffe 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -84,10 +84,10 @@ else: def bufvar_exists(buffer, varname): # NOQA if not buffer or buffer.number == vim.current.buffer.number: - return vim.eval('exists("b:{0}")'.format(varname)) + return int(vim.eval('exists("b:{0}")'.format(varname))) else: - return vim.eval('has_key(getbufvar({0}, ""), {1})' - .format(buffer.number, varname)) + return int(vim.eval('has_key(getbufvar({0}, ""), {1})' + .format(buffer.number, varname))) def vim_getwinvar(segment_info, varname): # NOQA result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname)) @@ -104,6 +104,79 @@ else: return getbufvar(info['bufnr'], '&' + option) +if hasattr(vim, 'tabpages'): + current_tabpage = lambda: vim.current.tabpage + list_tabpages = lambda: vim.tabpages +else: + class FalseObject(object): + @staticmethod + def __nonzero__(): + return False + + __bool__ = __nonzero__ + + def get_buffer(number): + for buffer in vim.buffers: + if buffer.number == number: + return buffer + raise KeyError(number) + + class WindowVars(object): + __slots__ = ('tabnr', 'winnr') + + def __init__(self, window): + self.tabnr = window.tabnr + self.winnr = window.number + + def __getitem__(self, key): + has_key = vim.eval('has_key(gettabwinvar({0}, {1}, ""), "{2}")'.format(self.tabnr, self.winnr, key)) + if has_key == '0': + raise KeyError + return vim.eval('gettabwinvar({0}, {1}, "{2}")'.format(self.tabnr, self.winnr, key)) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + class Window(FalseObject): + __slots__ = ('tabnr', 'number', '_vars') + + def __init__(self, tabnr, number): + self.tabnr = tabnr + self.number = number + self.vars = WindowVars(self) + + @property + def buffer(self): + return get_buffer(int(vim.eval('tabpagebuflist({0})[{1}]'.format(self.tabnr, self.number - 1)))) + + class Tabpage(FalseObject): + __slots__ = ('number',) + + def __init__(self, number): + self.number = number + + def __eq__(self, tabpage): + if not isinstance(tabpage, Tabpage): + raise NotImplementedError + return self.number == tabpage.number + + @property + def window(self): + return Window(self.number, int(vim.eval('tabpagewinnr({0})'.format(self.number)))) + + def _last_tab_nr(): + return int(vim.eval('tabpagenr("$")')) + + def current_tabpage(): # NOQA + return Tabpage(int(vim.eval('tabpagenr()'))) + + def list_tabpages(): # NOQA + return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)] + + if sys.version_info < (3,) or not hasattr(vim, 'bindeval'): getbufvar = vim_get_func('getbufvar') else: diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index e788bdd2..28ff2202 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -2,7 +2,7 @@ from __future__ import absolute_import, unicode_literals -from powerline.bindings.vim import vim_get_func, environ +from powerline.bindings.vim import vim_get_func, environ, current_tabpage from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme @@ -99,7 +99,9 @@ class VimRenderer(Renderer): window_id=window_id, winnr=winnr, buffer=window.buffer, + tabpage=current_tabpage(), ) + segment_info['tabnr'] = segment_info['tabpage'].number segment_info['bufnr'] = segment_info['buffer'].number winwidth = segment_info['window'].width matcher_info = segment_info diff --git a/powerline/segment.py b/powerline/segment.py index de85a7e4..9ae5f0fe 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -180,6 +180,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'priority': None, 'draw_soft_divider': None, 'draw_hard_divider': None, + 'draw_inner_divider': None, 'side': side, 'exclude_modes': segment.get('exclude_modes', []), 'include_modes': segment.get('include_modes', []), @@ -190,7 +191,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'mode': None, '_rendered_raw': '', '_rendered_hl': '', - '_len': 0, + '_len': None, + '_contents_len': None, '_space_left': 0, '_space_right': 0, } @@ -235,7 +237,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'mode': None, '_rendered_raw': '', '_rendered_hl': '', - '_len': 0, + '_len': None, + '_contents_len': None, '_space_left': 0, '_space_right': 0, } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 5a8d53f0..d4dac04b 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -15,7 +15,8 @@ except ImportError: from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar, - register_buffer_cache) + register_buffer_cache, current_tabpage, + list_tabpages) from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -487,50 +488,20 @@ def trailing_whitespace(pl, segment_info): return ret -if hasattr(vim, 'vvars') and vim.vvars['version'] >= 704: - def updated_segment_info(segment_info, tabpage): - segment_info = segment_info.copy() - window = tabpage.window - buffer = window.buffer - segment_info.update( - tabpage=tabpage, - tabnr=tabpage.number, - window=window, - winnr=window.number, - window_id=window.vars.get('powerline_window_id'), - buffer=buffer, - bufnr=buffer.number, - ) - return segment_info - - list_tabpages = lambda: vim.tabpages - current_tabpage = lambda: vim.current.tabpage - tabpage_nr = lambda tabpage: tabpage.number -else: - def updated_segment_info(segment_info, tabnr): # NOQA - segment_info = segment_info.copy() - winnr = int(vim.eval('tabpagewinnr({0})'.format(tabnr))) - bufnr = int(vim.eval('tabpagebuflist({0})[{1}]'.format(tabnr, winnr - 1))) - buffer = None - for buffer in vim.buffers: - if buffer.number == bufnr: - break - window_id = vim.eval('gettabwinvar({0}, {1}, "powerline_window_id")'.format(tabnr, winnr)) - window_id = int(window_id) if window_id else None - segment_info.update( - tabpage=None, - tabnr=tabnr, - window=None, - winnr=winnr, - window_id=window_id, - buffer=buffer, - bufnr=bufnr, - ) - return segment_info - - list_tabpages = lambda: range(1, int(vim.eval('tabpagenr("$")')) + 1) # NOQA - current_tabpage = lambda: int(vim.eval('tabpagenr()')) # NOQA - tabpage_nr = lambda tabnr: tabnr # NOQA +def tabpage_updated_segment_info(segment_info, tabpage): + segment_info = segment_info.copy() + window = tabpage.window + buffer = window.buffer + segment_info.update( + tabpage=tabpage, + tabnr=tabpage.number, + window=window, + winnr=window.number, + window_id=int(window.vars.get('powerline_window_id', -1)), + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info @requires_segment_info @@ -548,15 +519,15 @@ def tablister(pl, segment_info): thus window objects are not available as well. ''' cur_tabpage = current_tabpage() - cur_tabnr = tabpage_nr(cur_tabpage) + cur_tabnr = cur_tabpage.number def add_multiplier(tabpage, dct): - dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage_nr(tabpage) - cur_tabnr)) + dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr)) return dct return [ ( - updated_segment_info(segment_info, tabpage), + tabpage_updated_segment_info(segment_info, tabpage), add_multiplier(tabpage, {'mode': ('tab' if tabpage == cur_tabpage else 'nc')}) ) for tabpage in list_tabpages() From 85f252652ea542599794656570bd90c1d6667906 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 12:05:03 +0400 Subject: [PATCH 1143/1472] Add tabnr segment --- .../config_files/colorschemes/vim/__main__.json | 3 ++- powerline/config_files/themes/vim/tabline.json | 6 ++++++ powerline/segments/vim.py | 16 ++++++++++++++++ tests/test_segments.py | 6 ++++++ tests/test_tabline.vim | 2 +- tests/vim.py | 3 +++ 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 16c5ac71..8105d272 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -11,6 +11,7 @@ "file_name_empty": "file_directory", "line_percent": "information:additional", "line_count": "line_current", - "position": "information:additional" + "position": "information:additional", + "tabnr": "file_directory" } } diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index be07eaec..7fbea655 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -6,6 +6,12 @@ "type": "segment_list", "name": "tablister", "segments": [ + { + "name": "tabnr", + "after": " ", + "draw_soft_divider": false, + "priority": 5 + }, { "name": "file_directory", "draw_soft_divider": false, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index d4dac04b..538a4ac8 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -488,6 +488,22 @@ def trailing_whitespace(pl, segment_info): return ret +@requires_segment_info +def tabnr(pl, segment_info, show_current=False): + '''Show tabpage number + + :param bool show_current: + If False do not show current tabpage number. This is default because + tabnr is by default only present in tabline. + ''' + try: + tabnr = segment_info['tabnr'] + except KeyError: + return None + if show_current or tabnr != current_tabpage().number: + return str(tabnr) + + def tabpage_updated_segment_info(segment_info, tabpage): segment_info = segment_info.copy() window = tabpage.window diff --git a/tests/test_segments.py b/tests/test_segments.py index 03ccba24..07f0fd01 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -787,6 +787,12 @@ class TestVim(TestCase): self.assertEqual(trailing_whitespace(), None) self.assertEqual(trailing_whitespace(), None) + def test_tabnr(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=True), '1') + self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=False), None) + old_cwd = None diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index e96624c5..1f46da2a 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -11,7 +11,7 @@ catch cquit endtry -if result isnot# '%#Pl_240_5789784_235_2500134_NONE# ./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                       ' +if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' call writefile(['Unexpected result', result], 'message.fail') cquit endif diff --git a/tests/vim.py b/tests/vim.py index 3edf2862..08ae5e95 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -609,11 +609,14 @@ def _get_segment_info(): mode = mode_translations.get(mode, mode) window = current.window buffer = current.buffer + tabpage = current.tabpage return { 'window': window, 'winnr': window.number, 'buffer': buffer, 'bufnr': buffer.number, + 'tabpage': tabpage, + 'tabnr': tabpage.number, 'window_id': window._window_id, 'mode': mode, } From 2acefc9ac9aef84babc26580c389e371dc1ac94b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 12:18:52 +0400 Subject: [PATCH 1144/1472] Add tabbuflister and buflister listers --- .../config_files/themes/vim/tabline.json | 3 +- powerline/segments/vim.py | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 7fbea655..28e67c1c 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -4,12 +4,13 @@ "left": [ { "type": "segment_list", - "name": "tablister", + "name": "tabbuflister", "segments": [ { "name": "tabnr", "after": " ", "draw_soft_divider": false, + "exclude_modes": ["tab", "buf"], "priority": 5 }, { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 538a4ac8..5a973ff1 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -548,3 +548,50 @@ def tablister(pl, segment_info): ) for tabpage in list_tabpages() ] + + +def buffer_updated_segment_info(segment_info, buffer): + segment_info = segment_info.copy() + segment_info.update( + window=None, + winnr=None, + window_id=None, + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info + + +@requires_segment_info +def bufferlister(pl, segment_info): + '''List all buffers in segment_info format + + Specifically generates a list of segment info dictionaries with ``buffer`` + and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and + ``window_id`` keys unset. + + Sets segment ``mode`` to either ``buf`` (for current buffer) or ``nc`` + (for all other buffers). + ''' + cur_buffer = vim.current.buffer + cur_bufnr = cur_buffer.number + + def add_multiplier(buffer, dct): + dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr)) + return dct + + return [ + ( + buffer_updated_segment_info(segment_info, buffer), + add_multiplier(buffer, {'mode': ('tab' if buffer == cur_buffer else 'nc')}) + ) + for buffer in vim.buffers + ] + + +@requires_segment_info +def tabbuflister(*args, **kwargs): + if len(list_tabpages()) == 1: + return bufferlister(*args, **kwargs) + else: + return tablister(*args, **kwargs) From 6cf0c485fa63c17a755d8e4dd925a78bfe13f2f2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 12:43:41 +0400 Subject: [PATCH 1145/1472] Add single_tab segment --- .../colorschemes/vim/__main__.json | 2 ++ .../config_files/themes/vim/tabline.json | 5 +++ powerline/segments/vim.py | 26 ++++++++++++++ tests/test_segments.py | 13 +++++++ tests/vim.py | 34 ++++++++++++++++--- 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 8105d272..03f51ca2 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -12,6 +12,8 @@ "line_percent": "information:additional", "line_count": "line_current", "position": "information:additional", + "single_tab": "line_current", + "many_tabs": "line_current", "tabnr": "file_directory" } } diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 28e67c1c..a51da75f 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -34,6 +34,11 @@ "draw_hard_divider": false, "width": "auto" } + ], + "right": [ + { + "name": "single_tab" + } ] } } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 5a973ff1..964f78f1 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -504,6 +504,32 @@ def tabnr(pl, segment_info, show_current=False): return str(tabnr) +def single_tab(pl, single_text='Bufs', multiple_text='Tabs'): + '''Show one text if there is only one tab and another if there are many + + Mostly useful for tabline to indicate what kind of data is shown there. + + :param str single_text: + Text displayed when there is only one tabpage. May be None if you do not + want to display anything. + :param str multiple_text: + Text displayed when there is more then one tabpage. May be None if you + do not want to display anything. + + Highlight groups used: ``single_tab``, ``many_tabs``. + ''' + if len(list_tabpages()) == 1: + return single_text and [{ + 'contents': single_text, + 'highlight_group': ['single_tab'], + }] + else: + return multiple_text and [{ + 'contents': multiple_text, + 'highlight_group': ['many_tabs'], + }] + + def tabpage_updated_segment_info(segment_info, tabpage): segment_info = segment_info.copy() window = tabpage.window diff --git a/tests/test_segments.py b/tests/test_segments.py index 07f0fd01..eb86bf09 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -793,6 +793,19 @@ class TestVim(TestCase): self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=True), '1') self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=False), None) + def test_single_tab(self): + pl = Pl() + single_tab = partial(vim.single_tab, pl=pl) + with vim_module._with('tabpage'): + self.assertEqual(single_tab(), [{'highlight_group': ['many_tabs'], 'contents': 'Tabs'}]) + self.assertEqual(single_tab(single_text='s', multiple_text='m'), [{'highlight_group': ['many_tabs'], 'contents': 'm'}]) + self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['many_tabs'], 'contents': 'm'}]) + self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['many_tabs'], 'contents': 'Tabs'}]) + self.assertEqual(single_tab(), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}]) + self.assertEqual(single_tab(single_text='s', multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 's'}]) + self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}]) + self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['single_tab'], 'contents': 's'}]) + old_cwd = None diff --git a/tests/vim.py b/tests/vim.py index 08ae5e95..a0db754a 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -125,8 +125,11 @@ class _ObjList(object): return iter(self.l) @_vim - def _pop(self, *args, **kwargs): - return self.l.pop(*args, **kwargs) + def _pop(self, idx): + obj = self.l.pop(idx - 1) + for moved_obj in self.l[idx - 1:]: + moved_obj.number -= 1 + return obj @_vim def _append(self, *args, **kwargs): @@ -461,15 +464,21 @@ class _Tabpage(object): self.window = self.windows._new(**kwargs) return self.window - def _close_window(self, winnr): + def _close_window(self, winnr, open_window=True): curwinnr = self.window.number - win = self.windows._pop(winnr - 1) + win = self.windows._pop(winnr) if self.windows and winnr == curwinnr: self.window = self.windows[-1] - else: + elif open_window: current.tabpage._new_window() return win + def _close(self): + while self.windows: + self._close_window(1, False) + tabpages._pop(self.number) + _tabpage = len(tabpages) + tabpages = _ObjList(_Tabpage) @@ -665,6 +674,7 @@ def _tabnew(name=None): windows = tabpage.windows _tabpage = len(tabpages) _new(name) + return tabpage @_vim @@ -823,6 +833,18 @@ class _WithBufName(object): self.buffer.name = self.old +class _WithNewTabPage(object): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + def __enter__(self): + self.tab = _tabnew(*self.args, **self.kwargs) + + def __exit__(self, *args): + self.tab._close() + + @_vim def _with(key, *args, **kwargs): if key == 'buffer': @@ -843,6 +865,8 @@ def _with(key, *args, **kwargs): return _WithDict(_environ, **kwargs) elif key == 'split': return _WithSplit() + elif key == 'tabpage': + return _WithNewTabPage(*args, **kwargs) class error(Exception): From 43acf07ca0362d184e2723174317cbce2baf5d3f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 12:49:18 +0400 Subject: [PATCH 1146/1472] Modify test_tabline to also test bufline --- tests/test_tabline.vim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 1f46da2a..7289c0ce 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -3,6 +3,7 @@ source powerline/bindings/vim/plugin/powerline.vim edit abc tabedit def tabedit ghi + try let &columns = 80 let result = eval(&tabline[2:]) @@ -15,4 +16,19 @@ if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_25 call writefile(['Unexpected result', result], 'message.fail') cquit endif + +tabonly! + +try + let result = eval(&tabline[2:]) +catch + call writefile(['Exception while evaluating &tabline', v:exception], 'message.fail') + cquit +endtry + +if result isnot# '%#Pl_240_5789784_235_2500134_NONE# ./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                               %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' + call writefile(['Unexpected result (2)', result], 'message.fail') + cquit +endif + qall! From 1b8c092ae3a4257ee28a16bd7ca558587b7bd638 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 13:13:49 +0400 Subject: [PATCH 1147/1472] Make dividers unicode Fixes travis problem with old vim --- powerline/theme.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/theme.py b/powerline/theme.py index 2d382d64..25fa7e45 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -32,6 +32,11 @@ class Theme(object): run_once=False, shutdown_event=None): self.dividers = theme_config.get('dividers', common_config['dividers']) + self.dividers = dict(( + (key, dict((k, u(v)) + for k, v in val.items())) + for key, val in self.dividers.items() + )) self.spaces = theme_config.get('spaces', common_config['spaces']) self.segments = [] self.EMPTY_SEGMENT = { From f877516e54d74f04989c271ab5ad6f1fcfa2fd6f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 13:18:56 +0400 Subject: [PATCH 1148/1472] Add bufnr segment Ref #705 --- .../config_files/colorschemes/vim/__main__.json | 1 + powerline/segments/vim.py | 12 ++++++++++++ tests/test_segments.py | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 03f51ca2..9af73a9e 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -14,6 +14,7 @@ "position": "information:additional", "single_tab": "line_current", "many_tabs": "line_current", + "bufnr": "information:unimportant", "tabnr": "file_directory" } } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 964f78f1..6951d0ca 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -504,6 +504,18 @@ def tabnr(pl, segment_info, show_current=False): return str(tabnr) +@requires_segment_info +def bufnr(pl, segment_info, show_current=False): + '''Show buffer number + + :param bool show_current: + If False do not show current window number. + ''' + bufnr = segment_info['bufnr'] + if show_current or bufnr != vim.current.buffer.number: + return str(bufnr) + + def single_tab(pl, single_text='Bufs', multiple_text='Tabs'): '''Show one text if there is only one tab and another if there are many diff --git a/tests/test_segments.py b/tests/test_segments.py index eb86bf09..65ddabc8 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -793,6 +793,12 @@ class TestVim(TestCase): self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=True), '1') self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=False), None) + def test_bufnr(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.bufnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['bufnr'])) + self.assertEqual(vim.bufnr(pl=pl, segment_info=segment_info, show_current=False), None) + def test_single_tab(self): pl = Pl() single_tab = partial(vim.single_tab, pl=pl) From 120a9cc8facf3fab0ff86be04cb80024f7e5fb62 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 13:23:13 +0400 Subject: [PATCH 1149/1472] Add winnr segment Closes #705 --- .../config_files/colorschemes/vim/__main__.json | 1 + powerline/segments/vim.py | 12 ++++++++++++ tests/test_segments.py | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 9af73a9e..0294a5bb 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -15,6 +15,7 @@ "single_tab": "line_current", "many_tabs": "line_current", "bufnr": "information:unimportant", + "winnr": "information:unimportant", "tabnr": "file_directory" } } diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 6951d0ca..ee69b782 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -516,6 +516,18 @@ def bufnr(pl, segment_info, show_current=False): return str(bufnr) +@requires_segment_info +def winnr(pl, segment_info, show_current=False): + '''Show window number + + :param bool show_current: + If False do not show current window number. + ''' + winnr = segment_info['winnr'] + if show_current or winnr != vim.current.window.number: + return str(winnr) + + def single_tab(pl, single_text='Bufs', multiple_text='Tabs'): '''Show one text if there is only one tab and another if there are many diff --git a/tests/test_segments.py b/tests/test_segments.py index 65ddabc8..58b2a52e 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -799,6 +799,12 @@ class TestVim(TestCase): self.assertEqual(vim.bufnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['bufnr'])) self.assertEqual(vim.bufnr(pl=pl, segment_info=segment_info, show_current=False), None) + def test_winnr(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.winnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['winnr'])) + self.assertEqual(vim.winnr(pl=pl, segment_info=segment_info, show_current=False), None) + def test_single_tab(self): pl = Pl() single_tab = partial(vim.single_tab, pl=pl) From ead7e3f0fef84ea28cfda66de781cfcbed282ea3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 18:56:14 +0400 Subject: [PATCH 1150/1472] Add troubleshooting information discovered in #946 --- docs/source/troubleshooting.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 82b2cf9a..a51a4142 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -71,6 +71,31 @@ diagnose this problem you may do the following: it may be a powerline bug, but if locations do not match you should not report the bug until you observe it on configuration where locations do match. +#) If this problem is observed specifically within bash make sure that you clean + ``$POWERLINE_COMMAND`` and ``$PROMPT_COMMAND`` environment variables on + startup or, at least, that it was cleaned after update. While different + ``$POWERLINE_COMMAND`` variable should not cause any troubles most of time + (and when it will cause troubles are rather trivial) spoiled + ``$PROMPT_COMMAND`` may lead to strange error messages or absense of exit + code reporting. + + These are the sources which may keep outdated environment variables: + + * Any command launched from any application inherits its environment unless + callee explicitly requests to use specific environment. So if you did + ``exec bash`` after update it is rather unlikely to fix the problem. + * More interesting: `tmux` is a client-server application, it keeps one + server instance per one user. You probably already knew that, but there is + an interesting consequence: once `tmux` server was started it inherits its + environment from the callee and keeps it *forever* (i.e. until server is + killed). This environment is then inherited by applications you start with + ``tmux new-session``. Easiest solution is to kill tmux with ``tmux + kill-server``, but you may also use ``tmux set-environment -u`` to unset + offending variables. + * Also check `When using z powerline shows wrong number of jobs`_: though + this problem should not be seen after update only, it contains another + example of ``$PROMPT_COMMAND`` spoiling results. + #) If this problem is observed within the vim instance you should check out the output of the following Ex mode commands From 780c919d88864790445cfd38921c11d50ecf58c4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 22:04:09 +0400 Subject: [PATCH 1151/1472] Add documentation for `powerline --help` --- powerline/shell.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/powerline/shell.py b/powerline/shell.py index 8cfb2de6..8ab81ae8 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -52,17 +52,18 @@ def get_argparser(parser=None, *args, **kwargs): import argparse parser = argparse.ArgumentParser p = parser(*args, **kwargs) - p.add_argument('ext', nargs=1) - p.add_argument('side', nargs='?', choices=('left', 'right', 'above', 'aboveleft')) - p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) - p.add_argument('-w', '--width', type=int) - p.add_argument('--last_exit_code', metavar='INT', type=int) - p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()]) - p.add_argument('--jobnum', metavar='INT', type=int) - p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append') - p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append') - p.add_argument('-p', '--config_path', metavar='PATH') - p.add_argument('-R', '--renderer_arg', metavar='KEY=VAL', action='append') + p.add_argument('ext', nargs=1, help='Extension: application for which powerline command is launched (usually `shell\' or `tmux\')') + p.add_argument('side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'), help='Side: `left\' and `right\' represent left and right side respectively, `above\' emits lines that are supposed to be printed just above the prompt and `aboveleft\' is like concatenating `above\' with `left\' with the exception that only one Python instance is used in this case.') + p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str, + help='Renderer module. Usually something like `bash_prompt\' or `zsh_prompt\', is supposed to be set only in shell-specific bindings file.') + p.add_argument('-w', '--width', type=int, help='Maximum prompt with. Triggers truncation of some segments') + p.add_argument('--last_exit_code', metavar='INT', type=int, help='Last exit code') + p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()], help='Like above, but is supposed to contain space-separated array of statuses, representing exit statuses of commands in one pipe.') + p.add_argument('--jobnum', metavar='INT', type=int, help='Number of jobs.') + p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append', help='Configuration overrides for `config.json\'. Is translated to a dictionary and merged with the dictionary obtained from actual JSON configuration: KEY.KEY=VALUE is translated to `{"KEY": {"KEY": VALUE}}\' and then merged recursively. VALUE may be any JSON value, values that are not `null\', `true\', `false\', start with digit, `{\', `[\' are treated like strings. If VALUE is omitted then corresponding key is removed.') + p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append', help='Like above, but theme-specific. THEME should point to an existing and used theme to have any effect, but it is fine to use any theme here.') + p.add_argument('-R', '--renderer_arg', metavar='KEY=VAL', action='append', help='Like above, but provides argument for renderer. Is supposed to be used only by shell bindings to provide various data like last_exit_code or last_pipe_status (they are not using --renderer_arg for historical resons: renderer_arg was added later).') + p.add_argument('-p', '--config_path', metavar='PATH', help='Path to configuration directory. If it is present then configuration files will only be seeked in the provided path.') return p From 3d1f9bfbbd8d20231ff4a106efa8bde458cf3b17 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 22:20:01 +0400 Subject: [PATCH 1152/1472] Allow multiple arguments to `powerline[-lint] --config_path` --- powerline/lint/__init__.py | 4 ++-- powerline/shell.py | 7 ++----- scripts/powerline-lint | 2 +- tests/test_cmdline.py | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 9b3661e7..ffe58420 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1133,8 +1133,8 @@ theme_spec = (Spec( ).context_message('Error while loading theme')) -def check(path=None, debug=False): - search_paths = [path] if path else get_config_paths() +def check(paths=None, debug=False): + search_paths = paths or get_config_paths() find_config_file = generate_config_finder(lambda: search_paths) logger = logging.getLogger('powerline-lint') diff --git a/powerline/shell.py b/powerline/shell.py index 8ab81ae8..baf0973c 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -34,10 +34,7 @@ class ShellPowerline(Powerline): return r def get_config_paths(self): - if self.args.config_path: - return [self.args.config_path] - else: - return super(ShellPowerline, self).get_config_paths() + return self.args.config_path or super(ShellPowerline, self).get_config_paths() def get_local_themes(self, local_themes): if not local_themes: @@ -63,7 +60,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-c', '--config', metavar='KEY.KEY=VALUE', action='append', help='Configuration overrides for `config.json\'. Is translated to a dictionary and merged with the dictionary obtained from actual JSON configuration: KEY.KEY=VALUE is translated to `{"KEY": {"KEY": VALUE}}\' and then merged recursively. VALUE may be any JSON value, values that are not `null\', `true\', `false\', start with digit, `{\', `[\' are treated like strings. If VALUE is omitted then corresponding key is removed.') p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append', help='Like above, but theme-specific. THEME should point to an existing and used theme to have any effect, but it is fine to use any theme here.') p.add_argument('-R', '--renderer_arg', metavar='KEY=VAL', action='append', help='Like above, but provides argument for renderer. Is supposed to be used only by shell bindings to provide various data like last_exit_code or last_pipe_status (they are not using --renderer_arg for historical resons: renderer_arg was added later).') - p.add_argument('-p', '--config_path', metavar='PATH', help='Path to configuration directory. If it is present then configuration files will only be seeked in the provided path.') + p.add_argument('-p', '--config_path', action='append', metavar='PATH', help='Path to configuration directory. If it is present then configuration files will only be seeked in the provided path. May be provided multiple times to search in a list of directories.') return p diff --git a/scripts/powerline-lint b/scripts/powerline-lint index d8a05ca3..5a9b1285 100755 --- a/scripts/powerline-lint +++ b/scripts/powerline-lint @@ -7,7 +7,7 @@ import sys parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('-p', '--config_path', metavar='PATH') +parser.add_argument('-p', '--config_path', action='append', metavar='PATH') parser.add_argument('-d', '--debug', action='store_const', const=True) if __name__ == '__main__': diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 91f27adb..9895709f 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -90,7 +90,7 @@ class TestParser(TestCase): } } }, - 'config_path': '.', + 'config_path': ['.'], 'renderer_arg': {'smth': {'abc': 'def'}}, }), (['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}), From 74a3c9a0ca90cd55ce4d8f5297838a575bdb6d52 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Aug 2014 23:58:34 +0400 Subject: [PATCH 1153/1472] Add file_scheme segment Fixes #207 --- .../colorschemes/vim/__main__.json | 1 + .../config_files/themes/vim/default.json | 4 ++ powerline/segments/vim.py | 72 +++++++++++++++---- tests/test_segments.py | 17 +++++ tests/vim.py | 5 +- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 0294a5bb..ea9ba29e 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -7,6 +7,7 @@ "file_encoding": "file_format", "file_type": "file_format", "branch": "information:additional", + "file_scheme": "file_name", "file_directory": "information:additional", "file_name_empty": "file_directory", "line_percent": "information:additional", diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 05204943..d80010df 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -40,6 +40,10 @@ "draw_soft_divider": false, "after": " " }, + { + "name": "file_scheme", + "priority": 20 + }, { "name": "file_directory", "priority": 40, diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index ee69b782..d622a601 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals, absolute_import, division import os +import re try: import vim except ImportError: @@ -153,28 +154,71 @@ def readonly_indicator(pl, segment_info, text=''): return text if int(vim_getbufoption(segment_info, 'readonly')) else None +SCHEME_RE = re.compile(b'^\\w[\\w\\d+\\-.]*(?=:)') + + @requires_segment_info -def file_directory(pl, segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False): - '''Return file directory (head component of the file path). +def file_scheme(pl, segment_info): + '''Return the protocol part of the file. - :param bool shorten_user: - shorten ``$HOME`` directory to :file:`~/` + Protocol is the part of the full filename just before the colon which + starts with a latin letter and contains only latin letters, digits, plus, + period or hyphen (refer to `RFC3986 + `_ for the description of + URI scheme). If there is no such a thing ``None`` is returned, effectively + removing segment. - :param bool shorten_cwd: - shorten current directory to :file:`./` - - :param bool shorten_home: - shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. + .. note:: + Segment will not check whether there is ``//`` just after the + colon or if there is at least one slash after the scheme. Reason: it is + not always present. E.g. when opening file inside a zip archive file + name will look like :file:`zipfile:/path/to/archive.zip::file.txt`. + ``file_scheme`` segment will catch ``zipfile`` part here. ''' name = buffer_name(segment_info['buffer']) if not name: return None - file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') - + (':.' if shorten_cwd else '') + ':h') - if not file_directory: + match = SCHEME_RE.match(name) + if match: + return unicode(match.group(0)) + + +@requires_segment_info +def file_directory(pl, segment_info, remove_scheme=True, shorten_user=True, shorten_cwd=True, shorten_home=False): + '''Return file directory (head component of the file path). + + :param bool remove_scheme: + Remove scheme part from the segment name, if present. See documentation + of file_scheme segment for the description of what scheme is. Also + removes the colon. + + :param bool shorten_user: + Shorten ``$HOME`` directory to :file:`~/`. Does not work for files with + scheme. + + :param bool shorten_cwd: + Shorten current directory to :file:`./`. Does not work for files with + scheme present. + + :param bool shorten_home: + Shorten all directories in :file:`/home/` to :file:`~user/` instead of + :file:`/home/user/`. Does not work for files with scheme present. + ''' + name = buffer_name(segment_info['buffer']) + if not name: return None - if shorten_home and file_directory.startswith('/home/'): - file_directory = b'~' + file_directory[6:] + match = SCHEME_RE.match(name) + if match: + if remove_scheme: + name = name[len(match.group(0)) + 1:] # Remove scheme and colon + file_directory = vim_funcs['fnamemodify'](name, ':h') + else: + file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') + + (':.' if shorten_cwd else '') + ':h') + if not file_directory: + return None + if shorten_home and file_directory.startswith('/home/'): + file_directory = b'~' + file_directory[6:] file_directory = file_directory.decode('utf-8', 'powerline_vim_strtrans_error') return file_directory + os.sep diff --git a/tests/test_segments.py b/tests/test_segments.py index 58b2a52e..fc729858 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -619,6 +619,15 @@ class TestVim(TestCase): self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), '') self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info, text='L'), 'L') + def test_file_scheme(self): + pl = Pl() + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.file_scheme(pl=pl, segment_info=segment_info), None) + with vim_module._with('buffer', '/tmp/’’/abc') as segment_info: + self.assertEqual(vim.file_scheme(pl=pl, segment_info=segment_info), None) + with vim_module._with('buffer', 'zipfile:/tmp/abc.zip::abc/abc.vim') as segment_info: + self.assertEqual(vim.file_scheme(pl=pl, segment_info=segment_info), 'zipfile') + def test_file_directory(self): pl = Pl() segment_info = vim_module._get_segment_info() @@ -632,6 +641,14 @@ class TestVim(TestCase): self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/') os.environ['HOME'] = '/tmp' self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '~/') + with vim_module._with('buffer', 'zipfile:/tmp/abc.zip::abc/abc.vim') as segment_info: + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=False), 'zipfile:/tmp/abc.zip::abc/') + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=True), '/tmp/abc.zip::abc/') + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/abc.zip::abc/') + os.environ['HOME'] = '/tmp' + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=False), 'zipfile:/tmp/abc.zip::abc/') + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=True), '/tmp/abc.zip::abc/') + self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/abc.zip::abc/') def test_file_name(self): pl = Pl() diff --git a/tests/vim.py b/tests/vim.py index a0db754a..77995cf7 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -525,7 +525,10 @@ class _Buffer(object): import os if type(name) is not bytes: name = name.encode('utf-8') - self._name = os.path.abspath(name) + if ':/' in name: + self._name = name + else: + self._name = os.path.abspath(name) def __getitem__(self, line): return self._buf_lines[line] From 50703f1cef37f49a37006fbfc347793dc76a13d2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Aug 2014 00:07:30 +0400 Subject: [PATCH 1154/1472] Fix unicode/bytes handling for python-3 --- powerline/segments/vim.py | 2 +- tests/vim.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index d622a601..077bd67e 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -180,7 +180,7 @@ def file_scheme(pl, segment_info): return None match = SCHEME_RE.match(name) if match: - return unicode(match.group(0)) + return match.group(0).decode('ascii') @requires_segment_info diff --git a/tests/vim.py b/tests/vim.py index 77995cf7..c7db4230 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -525,7 +525,7 @@ class _Buffer(object): import os if type(name) is not bytes: name = name.encode('utf-8') - if ':/' in name: + if b':/' in name: self._name = name else: self._name = os.path.abspath(name) From cfb107724f693cd3635c37f7ece0052bd3d63ce4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Aug 2014 00:17:41 +0400 Subject: [PATCH 1155/1472] Exclude renderer_arg from daemon powerline objects cache It may easily change between runs from one client and only affects segment_info which is regenerated always. --- scripts/powerline-daemon | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index bc054680..92915a3d 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -83,7 +83,6 @@ def render(args): args.renderer_module, tuple(args.config) if args.config else None, tuple(args.theme_option) if args.theme_option else None, - tuple(args.renderer_arg) if args.renderer_arg else None, ) finish_args(args) powerline = None From a5037a817b1765a47d3295365373eb985301934e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Aug 2014 02:11:19 +0400 Subject: [PATCH 1156/1472] Fix powerline.c styling Also makes it compile with -std=c89 (except for snprintf) or just -std=c99, -Wall, -pedantic. --- client/powerline.c | 94 +++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/client/powerline.c b/client/powerline.c index a60743a7..6e5592e9 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -8,72 +8,82 @@ #include #include #include +#include -#define handle_error(msg) \ - do { perror(msg); exit(EXIT_FAILURE); } while (0) +#define HANDLE_ERROR(msg) \ + do { \ + perror(msg); exit(EXIT_FAILURE); \ + } while (0) -#ifndef TEMP_FAILURE_RETRY -#define TEMP_FAILURE_RETRY(expression) \ - ( \ - ({ long int __result; \ - do __result = (long int) (expression); \ - while (__result == -1L && errno == EINTR); \ - __result; })) -#endif +#define TEMP_FAILURE_RETRY(var, expression) \ + do { \ + long int __result; \ + do { \ + __result = (long int) (expression); \ + } while (__result == -1L && errno == EINTR); \ + var = __result; \ + } while (0) extern char **environ; void do_write(int sd, const char *raw, int len) { - int written = 0, n = -1; + int written = 0; + int n = -1; while (written < len) { - n = TEMP_FAILURE_RETRY(write(sd, raw+written, len-written)); + TEMP_FAILURE_RETRY(n, write(sd, raw + written, len - written)); if (n == -1) { close(sd); - handle_error("write() failed"); + HANDLE_ERROR("write() failed"); } written += n; } } +#ifdef __APPLE__ +# define ADDRESS_TEMPLATE "/tmp/powerline-ipc-%d" +# define A +#else +# define ADDRESS_TEMPLATE "powerline-ipc-%d" +# define A +1 +#endif + +#define ADDRESS_SIZE sizeof(ADDRESS_TEMPLATE) + (sizeof(uid_t) * 4) +#define NUM_ARGS_SIZE (sizeof(int) * 2) +#define BUF_SIZE 4096 +#define NEW_ARGV_SIZE 200 + int main(int argc, char *argv[]) { - int sd = -1, i; + int sd = -1; + int i; struct sockaddr_un server; - char address[50] = {}; + char address[ADDRESS_SIZE]; const char eof[2] = "\0\0"; - char buf[4096] = {}; - char *newargv[200] = {}; + char buf[BUF_SIZE]; + char *newargv[NEW_ARGV_SIZE]; char *wd = NULL; char **envp; - if (argc < 2) { printf("Must provide at least one argument.\n"); return EXIT_FAILURE; } + if (argc < 2) { + printf("Must provide at least one argument.\n"); return EXIT_FAILURE; + } -#ifdef __APPLE__ - snprintf(address, 50, "/tmp/powerline-ipc-%d", getuid()); -#else - snprintf(address, 50, "powerline-ipc-%d", getuid()); -#endif + snprintf(address, ADDRESS_SIZE, ADDRESS_TEMPLATE, getuid()); sd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sd == -1) handle_error("socket() failed"); + if (sd == -1) + HANDLE_ERROR("socket() failed"); - memset(&server, 0, sizeof(struct sockaddr_un)); // Clear + memset(&server, 0, sizeof(struct sockaddr_un)); server.sun_family = AF_UNIX; -#ifdef __APPLE__ - strncpy(server.sun_path, address, strlen(address)); -#else - strncpy(server.sun_path+1, address, strlen(address)); -#endif + strncpy(server.sun_path A, address, strlen(address)); -#ifdef __APPLE__ - if (connect(sd, (struct sockaddr *) &server, sizeof(server.sun_family) + strlen(address)) < 0) { -#else - if (connect(sd, (struct sockaddr *) &server, sizeof(server.sun_family) + strlen(address)+1) < 0) { -#endif + if (connect(sd, (struct sockaddr *) &server, sizeof(server.sun_family) + strlen(address) A) < 0) { close(sd); - // We failed to connect to the daemon, execute powerline instead - argc = (argc < 199) ? argc : 199; - for (i=1; i < argc; i++) newargv[i] = argv[i]; + /* We failed to connect to the daemon, execute powerline instead */ + argc = (argc < NEW_ARGV_SIZE - 1) ? argc : NEW_ARGV_SIZE - 1; + for (i = 1; i < argc; i++) + newargv[i] = argv[i]; newargv[0] = "powerline-render"; newargv[argc] = NULL; execvp("powerline-render", newargv); @@ -101,13 +111,13 @@ int main(int argc, char *argv[]) { i = -1; while (i != 0) { - i = TEMP_FAILURE_RETRY(read(sd, buf, 4096)); + TEMP_FAILURE_RETRY(i, read(sd, buf, BUF_SIZE)); if (i == -1) { close(sd); - handle_error("read() failed"); + HANDLE_ERROR("read() failed"); + } else if (i > 0) { + (void) write(STDOUT_FILENO, buf, i); } - if (i > 0) - write(STDOUT_FILENO, buf, i) || 0; } close(sd); From da45adb9e63b65b6a532f62910e08c2ab9c398ce Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Aug 2014 02:21:44 +0400 Subject: [PATCH 1157/1472] Make it compile without warnings with -Wconversion --- client/powerline.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/client/powerline.c b/client/powerline.c index 6e5592e9..de14ac26 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -26,9 +27,9 @@ extern char **environ; -void do_write(int sd, const char *raw, int len) { - int written = 0; - int n = -1; +void do_write(int sd, const char *raw, size_t len) { + size_t written = 0; + ptrdiff_t n = -1; while (written < len) { TEMP_FAILURE_RETRY(n, write(sd, raw + written, len - written)); @@ -36,7 +37,7 @@ void do_write(int sd, const char *raw, int len) { close(sd); HANDLE_ERROR("write() failed"); } - written += n; + written += (size_t) n; } } @@ -55,7 +56,7 @@ void do_write(int sd, const char *raw, int len) { int main(int argc, char *argv[]) { int sd = -1; - int i; + ptrdiff_t i; struct sockaddr_un server; char address[ADDRESS_SIZE]; const char eof[2] = "\0\0"; @@ -78,7 +79,7 @@ int main(int argc, char *argv[]) { server.sun_family = AF_UNIX; strncpy(server.sun_path A, address, strlen(address)); - if (connect(sd, (struct sockaddr *) &server, sizeof(server.sun_family) + strlen(address) A) < 0) { + if (connect(sd, (struct sockaddr *) &server, (socklen_t) (sizeof(server.sun_family) + strlen(address) A)) < 0) { close(sd); /* We failed to connect to the daemon, execute powerline instead */ argc = (argc < NEW_ARGV_SIZE - 1) ? argc : NEW_ARGV_SIZE - 1; @@ -116,7 +117,7 @@ int main(int argc, char *argv[]) { close(sd); HANDLE_ERROR("read() failed"); } else if (i > 0) { - (void) write(STDOUT_FILENO, buf, i); + (void) write(STDOUT_FILENO, buf, (size_t) i); } } From 203a374b54c202d6025828d57d47ad63f571c92f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 4 Aug 2014 02:59:02 +0400 Subject: [PATCH 1158/1472] Do not use argparge for parsing --env and --cwd It took 2/3 of do_render call according to profiler. New variant takes only 1/6. WARNING: This commit will break all powerline installations with running daemon because it changes communication protocol. You must kill and restart all your powerline daemons for powerline to function properly. Highlight @kovidgoyal --- client/powerline.c | 17 ++++++++++------- client/powerline.py | 11 ++++++----- client/powerline.sh | 5 +++-- scripts/powerline-daemon | 20 ++++++++++++-------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/client/powerline.c b/client/powerline.c index de14ac26..fd08c066 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -60,6 +60,7 @@ int main(int argc, char *argv[]) { struct sockaddr_un server; char address[ADDRESS_SIZE]; const char eof[2] = "\0\0"; + char num_args[NUM_ARGS_SIZE]; char buf[BUF_SIZE]; char *newargv[NEW_ARGV_SIZE]; char *wd = NULL; @@ -90,24 +91,26 @@ int main(int argc, char *argv[]) { execvp("powerline-render", newargv); } + snprintf(num_args, NUM_ARGS_SIZE, "%x", argc - 1); + do_write(sd, num_args, strlen(num_args)); + do_write(sd, eof, 1); + for (i = 1; i < argc; i++) { do_write(sd, argv[i], strlen(argv[i])); do_write(sd, eof, 1); } - for(envp=environ; *envp; envp++) { - do_write(sd, "--env=", 6); - do_write(sd, *envp, strlen(*envp)); - do_write(sd, eof, 1); - } - wd = getcwd(NULL, 0); if (wd != NULL) { - do_write(sd, "--cwd=", 6); do_write(sd, wd, strlen(wd)); free(wd); wd = NULL; } + for(envp=environ; *envp; envp++) { + do_write(sd, *envp, strlen(*envp)); + do_write(sd, eof, 1); + } + do_write(sd, eof, 2); i = -1; diff --git a/client/powerline.py b/client/powerline.py index 89e56f8e..5cec13e8 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -42,7 +42,8 @@ fenc = sys.getfilesystemencoding() or 'utf-8' if fenc == 'ascii': fenc = 'utf-8' -args = [x.encode(fenc) if isinstance(x, type('')) else x for x in sys.argv[1:]] +args = [bytes('%x' % (len(sys.argv) - 1))] +args.extend((x.encode(fenc) if isinstance(x, type('')) else x for x in sys.argv[1:])) try: cwd = os.getcwd() @@ -51,17 +52,17 @@ except EnvironmentError: else: if isinstance(cwd, type('')): cwd = cwd.encode(fenc) - args.append(b'--cwd=' + cwd) + args.append(cwd) env = (k + b'=' + v for k, v in os.environ.items()) env = (x if isinstance(x, bytes) else x.encode(fenc, 'replace') for x in env) -args.extend((b'--env=' + x for x in env)) +args.extend(env) EOF = b'\0\0' for a in args: - eintr_retry_call(sock.sendall, a + EOF[0]) + eintr_retry_call(sock.sendall, a + b'\0') eintr_retry_call(sock.sendall, EOF) @@ -74,4 +75,4 @@ while True: sock.close() -print (b''.join(received)) +sys.stdout.write(b''.join(received)) diff --git a/client/powerline.sh b/client/powerline.sh index a56bc296..5860b7f8 100755 --- a/client/powerline.sh +++ b/client/powerline.sh @@ -5,11 +5,12 @@ ADDRESS="powerline-ipc-${UID:-`id -u`}" # Warning: env -0 does not work in busybox. Consider switching to parsing # `set` output in this case ( + printf '%x\0' "$#" for argv in "$@" ; do printf '%s\0' "$argv" done - env -0 | sed 's/\(\x00\)\([^\x00]\)\|^/\1--env=\2/g' - printf -- '--cwd=%s\0' "$PWD" + printf '%s\0' "$PWD" + env -0 ) | socat -lf/dev/null -t 10 - abstract-client:"$ADDRESS" if test $? -ne 0 ; then diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 92915a3d..20794575 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -48,8 +48,6 @@ class NonInteractiveArgParser(ArgumentParser): parser = get_argparser(parser=NonInteractiveArgParser, description='powerline daemon') -parser.add_argument('--cwd', metavar='PATH') -parser.add_argument('--env', action='append') EOF = b'EOF\0\0' @@ -67,11 +65,10 @@ class PowerlineDaemon(ShellPowerline): return super(PowerlineDaemon, self).get_log_handler() -def render(args): +def render(args, environ, cwd): global logger global config_loader - environ = dict(((k, v) for k, v in (x.partition('=')[0::2] for x in args.env))) - cwd = environ.get('PWD', args.cwd or '/') + cwd = cwd or environ.get('PWD', '/') segment_info = { 'getcwd': lambda: cwd, 'home': environ.get('HOME', home), @@ -175,11 +172,18 @@ def safe_bytes(o, encoding=encoding): return safe_bytes(str(e), encoding) +def parse_args(req): + args = [x.decode(encoding) for x in req.split(b'\0') if x] + numargs = int(args[0], 16) + shell_args = parser.parse_args(args[1:numargs + 1]) + cwd = args[numargs + 1] + environ = dict(((k, v) for k, v in (x.partition('=')[0::2] for x in args[numargs + 2:]))) + return shell_args, environ, cwd + + def do_render(req): try: - args = [x.decode(encoding) for x in req.split(b'\0') if x] - args = parser.parse_args(args) - return safe_bytes(render(args)) + return safe_bytes(render(*parse_args(req))) except Exception as e: return safe_bytes(str(e)) From 4bb217a05c264989be4d55b0080c2c4a0a86abd4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 00:07:25 +0400 Subject: [PATCH 1159/1472] Run refresh-client in tmux not depending on POWERLINE_COMMAND Ref #942 --- powerline/bindings/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 4dd79cc2..4f8d9402 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -115,7 +115,7 @@ def source_tmux_files(pl, args): cmd = deduce_command() if cmd: run_tmux_command('set-environment', '-g', 'POWERLINE_COMMAND', deduce_command()) - run_tmux_command('refresh-client') + run_tmux_command('refresh-client') def create_powerline_logger(args): From 0c3e380fe0388e34a9d5445c8b0d5896449e0d7b Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 00:17:58 +0400 Subject: [PATCH 1160/1472] Set all options quietly Requires tmux 1.7 or later, so `-q` is not used in 1.8 and earlier configuration file. Closes #942 --- powerline/bindings/tmux/powerline_tmux_1.8.conf | 2 +- powerline/bindings/tmux/powerline_tmux_1.8_plus.conf | 2 +- powerline/bindings/tmux/powerline_tmux_1.9_plus.conf | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/tmux/powerline_tmux_1.8.conf b/powerline/bindings/tmux/powerline_tmux_1.8.conf index d94daf39..720206be 100644 --- a/powerline/bindings/tmux/powerline_tmux_1.8.conf +++ b/powerline/bindings/tmux/powerline_tmux_1.8.conf @@ -1,5 +1,5 @@ # powerline_tmux_1.8.conf # tmux Version 1.8 introduces window-status-last-{attr,bg,fg}, which is # deprecated for versions 1.9+, thus only applicable to version 1.8. -set -g window-status-last-fg colour31 +set -qg window-status-last-fg colour31 # vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf b/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf index a31bacfe..1ad9cdb3 100644 --- a/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf +++ b/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf @@ -1,5 +1,5 @@ # powerline_tmux_1.8_plus.conf # tmux Version 1.8 introduces the 'client_prefix' format variable, applicable # for versions 1.8+ -set -g status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)' +set -qg status-left '#{?client_prefix,#[fg=colour254]#[bg=colour31]#[bold],#[fg=colour16]#[bg=colour254]#[bold]} #S #{?client_prefix,#[fg=colour31]#[bg=colour234]#[nobold],#[fg=colour254]#[bg=colour234]#[nobold]}#(eval $POWERLINE_COMMAND tmux left)' # vim: ft=tmux diff --git a/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf b/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf index 619b26d5..b053b6ee 100644 --- a/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf +++ b/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf @@ -1,8 +1,8 @@ # powerline_tmux_1.9_plus.conf # Version 1.9 introduces the foo-style options, applicable to version 1.9+ -set -g status-style fg=colour231,bg=colour234 -set -g window-status-last-style fg=colour31 -set-window-option -g window-status-style fg=colour249 -set-window-option -g window-status-activity-style fg=yellow,none -set-window-option -g window-status-bell-style fg=red,none +set -qg status-style fg=colour231,bg=colour234 +set -qg window-status-last-style fg=colour31 +set-window-option -qg window-status-style fg=colour249 +set-window-option -qg window-status-activity-style fg=yellow,none +set-window-option -qg window-status-bell-style fg=red,none # vim: ft=tmux From 22962900377b97bd7370ec1fcb9d76f3bf766bd1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 23:21:16 +0400 Subject: [PATCH 1161/1472] Add MarkedList type --- powerline/lint/markedjson/markedvalue.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 397be680..ce23c509 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -16,6 +16,12 @@ def gen_new(cls): return __new__ +def gen_init(cls): + def __init__(self, value, mark): + return cls.__init__(self, value) + return __init__ + + class MarkedUnicode(unicode): __new__ = gen_new(unicode) @@ -49,14 +55,17 @@ class MarkedFloat(float): class MarkedDict(dict): __new__ = gen_new(dict) - - def __init__(self, value, mark): - super(MarkedDict, self).__init__(value) + __init__ = gen_init(dict) def copy(self): return MarkedDict(super(MarkedDict, self).copy(), self.mark) +class MarkedList(list): + __new__ = gen_new(list) + __init__ = gen_init(list) + + class MarkedValue: def __init__(self, value, mark): self.mark = mark @@ -68,6 +77,7 @@ specialclasses = { int: MarkedInt, float: MarkedFloat, dict: MarkedDict, + list: MarkedList, } classcache = {} From b043749daafecc08d059e15c0e4a5d74fa683505 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 12 Jul 2014 23:22:28 +0400 Subject: [PATCH 1162/1472] Add support for pickle --- powerline/lint/markedjson/markedvalue.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index ce23c509..457b66f6 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -22,8 +22,15 @@ def gen_init(cls): return __init__ +def gen_getnewargs(cls): + def __getnewargs__(self): + return (self.value, self.mark) + return __getnewargs__ + + class MarkedUnicode(unicode): __new__ = gen_new(unicode) + __getnewargs__ = gen_getnewargs(unicode) def _proc_partition(self, part_result): pointdiff = 1 @@ -47,15 +54,18 @@ class MarkedUnicode(unicode): class MarkedInt(int): __new__ = gen_new(int) + __getnewargs__ = gen_getnewargs(int) class MarkedFloat(float): __new__ = gen_new(float) + __getnewargs__ = gen_getnewargs(float) class MarkedDict(dict): __new__ = gen_new(dict) __init__ = gen_init(dict) + __getnewargs__ = gen_getnewargs(dict) def copy(self): return MarkedDict(super(MarkedDict, self).copy(), self.mark) @@ -64,6 +74,7 @@ class MarkedDict(dict): class MarkedList(list): __new__ = gen_new(list) __init__ = gen_init(list) + __getnewargs__ = gen_getnewargs(list) class MarkedValue: @@ -71,6 +82,8 @@ class MarkedValue: self.mark = mark self.value = value + __getinitargs__ = gen_getnewargs(None) + specialclasses = { unicode: MarkedUnicode, From 3d77306c35e7f2a3ede1fd53ab9b0d70a061920e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 13 Jul 2014 00:01:38 +0400 Subject: [PATCH 1163/1472] Some style fixes in powerline.lint --- powerline/lint/__init__.py | 367 +++++++++++++++++++++++++------------ 1 file changed, 253 insertions(+), 114 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index ffe58420..cae49b79 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -159,7 +159,13 @@ class Spec(object): for item in value: if isinstance(item_func, int): spec = self.specs[item_func] - proceed, fhadproblem = spec.match(item, value.mark, data, context + (('list item ' + unicode(i), item),), echoerr) + proceed, fhadproblem = spec.match( + item, + value.mark, + data, + context + (('list item ' + unicode(i), item),), + echoerr + ) else: proceed, echo, fhadproblem = item_func(item, data, context, echoerr) if echo and fhadproblem: @@ -192,7 +198,13 @@ class Spec(object): def check_tuple(self, value, context_mark, data, context, echoerr, start, end): hadproblem = False for (i, item, spec) in zip(itertools.count(), value, self.specs[start:end]): - proceed, ihadproblem = spec.match(item, value.mark, data, context + (('tuple item ' + unicode(i), item),), echoerr) + proceed, ihadproblem = spec.match( + item, + value.mark, + data, + context + (('tuple item ' + unicode(i), item),), + echoerr + ) if ihadproblem: hadproblem = True if not proceed: @@ -221,10 +233,16 @@ class Spec(object): def len(self, comparison, cint, msg_func=None): cmp_func = self.cmp_funcs[comparison] - msg_func = msg_func or (lambda value: 'length of {0!r} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint)) - self.checks.append(('check_func', - (lambda value, *args: (True, True, not cmp_func(len(value), cint))), - msg_func)) + msg_func = ( + msg_func + or (lambda value: 'length of {0!r} is not {1} {2}'.format( + value, self.cmp_msgs[comparison], cint)) + ) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, not cmp_func(len(value), cint))), + msg_func + )) return self def cmp(self, comparison, cint, msg_func=None): @@ -236,16 +254,20 @@ class Spec(object): self.type(type(cint)) cmp_func = self.cmp_funcs[comparison] msg_func = msg_func or (lambda value: '{0} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint)) - self.checks.append(('check_func', - (lambda value, *args: (True, True, not cmp_func(value.value, cint))), - msg_func)) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, not cmp_func(value.value, cint))), + msg_func + )) return self def unsigned(self, msg_func=None): self.type(int) - self.checks.append(('check_func', - (lambda value, *args: (True, True, value < 0)), - lambda value: '{0} must be greater then zero'.format(value))) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, value < 0)), + (lambda value: '{0} must be greater then zero'.format(value)) + )) return self def list(self, item_func, msg_func=None): @@ -286,25 +308,35 @@ class Spec(object): self.type(unicode) compiled = re.compile(regex) msg_func = msg_func or (lambda value: 'String "{0}" does not match "{1}"'.format(value, regex)) - self.checks.append(('check_func', - (lambda value, *args: (True, True, not compiled.match(value.value))), - msg_func)) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, not compiled.match(value.value))), + msg_func + )) return self def ident(self, msg_func=None): - msg_func = msg_func or (lambda value: 'String "{0}" is not an alphanumeric/underscore identifier'.format(value)) + msg_func = ( + msg_func + or (lambda value: 'String "{0}" is not an alphanumeric/underscore identifier'.format(value)) + ) return self.re('^\w+$', msg_func) def oneof(self, collection, msg_func=None): msg_func = msg_func or (lambda value: '"{0}" must be one of {1!r}'.format(value, list(collection))) - self.checks.append(('check_func', - lambda value, *args: (True, True, value not in collection), - msg_func)) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, value not in collection)), + msg_func + )) return self def error(self, msg): - self.checks.append(('check_func', lambda *args: (True, True, True), - lambda value: msg.format(value))) + self.checks.append(( + 'check_func', + (lambda *args: (True, True, True)), + (lambda value: msg.format(value)) + )) return self def either(self, *specs): @@ -334,7 +366,13 @@ class Spec(object): for key, vali in self.keys.items(): valspec = self.specs[vali] if key in value: - proceed, mhadproblem = valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr) + proceed, mhadproblem = valspec.match( + value[key], + value.mark, + data, + context + ((key, value[key]),), + echoerr + ) if mhadproblem: hadproblem = True if not proceed: @@ -358,7 +396,13 @@ class Spec(object): if khadproblem: hadproblem = True if proceed: - proceed, vhadproblem = valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr) + proceed, vhadproblem = valspec.match( + value[key], + value.mark, + data, + context + ((key, value[key]),), + echoerr + ) if vhadproblem: hadproblem = True break @@ -416,9 +460,14 @@ def check_matcher_func(ext, match_name, data, context, echoerr): if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'): if func.func_code.co_argcount != 1: - echoerr(context='Error while loading matcher functions', - problem='function {0} accepts {1} arguments instead of 1. Are you sure it is the proper function?'.format(match_function, func.func_code.co_argcount), - problem_mark=match_name.mark) + echoerr( + context='Error while loading matcher functions', + problem=( + 'function {0} accepts {1} arguments instead of 1. ' + 'Are you sure it is the proper function?' + ).format(match_function, func.func_code.co_argcount), + problem_mark=match_name.mark + ) return True, False @@ -477,17 +526,30 @@ main_spec = (Spec( left=divside_spec(), right=divside_spec(), ), - spaces=Spec().unsigned().cmp('le', 2, - lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value)), + spaces=Spec().unsigned().cmp( + 'le', 2, lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value) + ), term_truecolor=Spec().type(bool).optional(), # Python is capable of loading from zip archives. Thus checking path # only for existence of the path, not for it being a directory - paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))), - lambda value: 'path does not exist: {0}'.format(value)).optional(), - log_file=Spec().type(unicode).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(os.path.expanduser(value)))), - lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))).optional(), - log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)), - lambda value: 'unknown debugging level {0}'.format(value)).optional(), + paths=Spec().list( + (lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))), + (lambda value: 'path does not exist: {0}'.format(value)) + ).optional(), + log_file=Spec().type(unicode).func( + ( + lambda value, *args: ( + True, + True, + not os.path.isdir(os.path.dirname(os.path.expanduser(value))) + ) + ), + (lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))) + ).optional(), + log_level=Spec().re('^[A-Z]+$').func( + (lambda value, *args: (True, True, not hasattr(logging, value))), + (lambda value: 'unknown debugging level {0}'.format(value)) + ).optional(), log_format=Spec().type(unicode).optional(), interval=Spec().either(Spec().cmp('gt', 0.0), Spec().type(type(None))).optional(), reload_config=Spec().type(bool).optional(), @@ -497,8 +559,9 @@ main_spec = (Spec( vim=Spec( colorscheme=colorscheme_spec(), theme=theme_spec(), - local_themes=Spec() - .unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec()) + local_themes=Spec().unknown_spec( + lambda *args: check_matcher_func('vim', *args), theme_spec() + ), ).optional(), ipython=Spec( colorscheme=colorscheme_spec(), @@ -517,24 +580,28 @@ main_spec = (Spec( select=theme_spec(), ), ).optional(), - ).unknown_spec(check_ext, - Spec( - colorscheme=colorscheme_spec(), - theme=theme_spec(), - )) - .context_message('Error while loading extensions configuration (key {key})'), + ).unknown_spec( + check_ext, + Spec( + colorscheme=colorscheme_spec(), + theme=theme_spec(), + ) + ).context_message('Error while loading extensions configuration (key {key})'), ).context_message('Error while loading main configuration')) term_color_spec = Spec().unsigned().cmp('le', 255).copy -true_color_spec = Spec().re('^[0-9a-fA-F]{6}$', - lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value)).copy +true_color_spec = Spec().re( + '^[0-9a-fA-F]{6}$', + (lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value)) +).copy colors_spec = (Spec( colors=Spec().unknown_spec( Spec().ident(), Spec().either( Spec().tuple(term_color_spec(), true_color_spec()), - term_color_spec())) - .context_message('Error while checking colors (key {key})'), + term_color_spec() + ) + ).context_message('Error while checking colors (key {key})'), gradients=Spec().unknown_spec( Spec().ident(), Spec().tuple( @@ -546,10 +613,14 @@ colors_spec = (Spec( def check_color(color, data, context, echoerr): - if color not in data['colors_config'].get('colors', {}) and color not in data['colors_config'].get('gradients', {}): - echoerr(context='Error while checking highlight group in colorscheme (key {key})'.format(key=context_key(context)), - problem='found unexistent color or gradient {0}'.format(color), - problem_mark=color.mark) + if (color not in data['colors_config'].get('colors', {}) + and color not in data['colors_config'].get('gradients', {})): + echoerr( + context='Error while checking highlight group in colorscheme (key {key})'.format( + key=context_key(context)), + problem='found unexistent color or gradient {0}'.format(color), + problem_mark=color.mark + ) return True, False, True return True, False, False @@ -618,9 +689,13 @@ def check_group(group, data, context, echoerr): if not proceed: break if not_found: - new_echoerr(context='Error while checking group definition in colorscheme (key {key})'.format(key=context_key(context)), - problem='name {0} is not present in {1} {2} colorschemes: {3}'.format(group, tofind, ext, ', '.join(not_found)), - problem_mark=group.mark) + new_echoerr( + context='Error while checking group definition in colorscheme (key {key})'.format( + key=context_key(context)), + problem='name {0} is not present in {1} {2} colorschemes: {3}'.format( + group, tofind, ext, ', '.join(not_found)), + problem_mark=group.mark + ) new_echoerr.echo_all() return True, False, hadproblem @@ -717,25 +792,34 @@ def check_key_compatibility(segment, data, context, echoerr): keys = set(segment) if not ((keys - generic_keys) < type_keys[segment_type]): unknown_keys = keys - generic_keys - type_keys[segment_type] - echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - context_mark=context[-1][1].mark, - problem='found keys not used with the current segment type: {0}'.format( - list_sep.join(unknown_keys)), - problem_mark=list(unknown_keys)[0].mark) + echoerr( + context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=context[-1][1].mark, + problem='found keys not used with the current segment type: {0}'.format( + list_sep.join(unknown_keys)), + problem_mark=list(unknown_keys)[0].mark + ) hadproblem = True if not (keys >= required_keys[segment_type]): missing_keys = required_keys[segment_type] - keys - echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - context_mark=context[-1][1].mark, - problem='found missing required keys: {0}'.format( - list_sep.join(missing_keys))) + echoerr( + context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=context[-1][1].mark, + problem='found missing required keys: {0}'.format( + list_sep.join(missing_keys)) + ) hadproblem = True if not (segment_type == 'function' or (keys & highlight_keys)): - echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - context_mark=context[-1][1].mark, - problem='found missing keys required to determine highlight group. Either highlight_group or name key must be present') + echoerr( + context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=context[-1][1].mark, + problem=( + 'found missing keys required to determine highlight group. ' + 'Either highlight_group or name key must be present' + ) + ) hadproblem = True return True, False, hadproblem @@ -842,35 +926,52 @@ def check_segment_name(name, data, context, echoerr): if divider_hl_group: r = hl_exists(divider_hl_group, data, context, echoerr, allow_gradients=True) if r: - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight group {0} not defined in the following colorschemes: {1}\n(Group name was obtained from function documentation.)'.format( - divider_hl_group, list_sep.join(r)), - problem_mark=name.mark) + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem=( + 'found highlight group {0} not defined in the following colorschemes: {1}\n' + '(Group name was obtained from function documentation.)' + ).format(divider_hl_group, list_sep.join(r)), + problem_mark=name.mark + ) hadproblem = True if hl_groups: greg = re.compile(r'``([^`]+)``( \(gradient\))?') - hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (list_sep.join(hl_groups)).split(', ')] + hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] + for s in (list_sep.join(hl_groups)).split(', ')] for required_pack in hl_groups: rs = [hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) for hl_group, gradient in required_pack] if all(rs): - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight groups list ({0}) with all groups not defined in some colorschemes\n(Group names were taken from function documentation.)'.format( - list_sep.join((h[0] for h in required_pack))), - problem_mark=name.mark) + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem=( + 'found highlight groups list ({0}) with all groups not defined in some colorschemes\n' + '(Group names were taken from function documentation.)' + ).format(list_sep.join((h[0] for h in required_pack))), + problem_mark=name.mark + ) for r, h in zip(rs, required_pack): - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( - h[0], list_sep.join(r))) + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + h[0], list_sep.join(r)) + ) hadproblem = True else: r = hl_exists(name, data, context, echoerr, allow_gradients=True) if r: - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight group {0} not defined in the following colorschemes: {1}\n(If not specified otherwise in documentation, highlight group for function segments\nis the same as the function name.)'.format( - name, list_sep.join(r)), - problem_mark=name.mark) + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem=( + 'found highlight group {0} not defined in the following colorschemes: {1}\n' + '(If not specified otherwise in documentation, ' + 'highlight group for function segments\n' + 'is the same as the function name.)' + ).format(name, list_sep.join(r)), + problem_mark=name.mark + ) hadproblem = True return True, False, hadproblem @@ -915,17 +1016,23 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): if hasgradient: hadgradient = True if allow_gradients is False and not hascolor and hasgradient: - echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)), - context_mark=getattr(hl_group, 'mark', None), - problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), - problem_mark=color.mark) + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context_key(context)), + context_mark=getattr(hl_group, 'mark', None), + problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), + problem_mark=color.mark + ) r.append(colorscheme) continue if allow_gradients == 'force' and not hadgradient: - echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)), - context_mark=getattr(hl_group, 'mark', None), - problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), - problem_mark=group_config.mark) + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context_key(context)), + context_mark=getattr(hl_group, 'mark', None), + problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), + problem_mark=group_config.mark + ) r.append(colorscheme) return r @@ -933,10 +1040,12 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): def check_highlight_group(hl_group, data, context, echoerr): r = hl_exists(hl_group, data, context, echoerr) if r: - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( - hl_group, list_sep.join(r)), - problem_mark=hl_group.mark) + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + hl_group, list_sep.join(r)), + problem_mark=hl_group.mark + ) return True, False, True return True, False, False @@ -944,15 +1053,19 @@ def check_highlight_group(hl_group, data, context, echoerr): def check_highlight_groups(hl_groups, data, context, echoerr): rs = [hl_exists(hl_group, data, context, echoerr) for hl_group in hl_groups] if all(rs): - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format( - list_sep.join((unicode(h) for h in hl_groups))), - problem_mark=hl_groups.mark) + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format( + list_sep.join((unicode(h) for h in hl_groups))), + problem_mark=hl_groups.mark + ) for r, hl_group in zip(rs, hl_groups): - echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)), - problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + echoerr( + context='Error while checking theme (key {key})'.format(key=context_key(context)), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( hl_group, list_sep.join(r)), - problem_mark=hl_group.mark) + problem_mark=hl_group.mark + ) return True, False, True return True, False, False @@ -1005,9 +1118,11 @@ def check_args_variant(segment, args, data, context, echoerr): hadproblem = False if required_args - present_args: - echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), - context_mark=args.mark, - problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args))) + echoerr( + context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), + context_mark=args.mark, + problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args)) + ) hadproblem = True if not all_args >= present_args: @@ -1019,7 +1134,13 @@ def check_args_variant(segment, args, data, context, echoerr): if isinstance(segment, ThreadedSegment): for key in set(threaded_args_specs) & present_args: - proceed, khadproblem = threaded_args_specs[key].match(args[key], args.mark, data, context + ((key, args[key]),), echoerr) + proceed, khadproblem = threaded_args_specs[key].match( + args[key], + args.mark, + data, + context + ((key, args[key]),), + echoerr + ) if khadproblem: hadproblem = True if not proceed: @@ -1070,7 +1191,10 @@ def get_all_possible_segments(data, context, echoerr): for segments in theme_config.get('segments', {}).values(): for segment in segments: if segment.get('type', 'function') == 'function': - module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext'])) + module = segment.get( + 'module', + context[0][1].get('default_module', 'powerline.segments.' + data['ext']) + ) func = import_segment(name, data, context, echoerr, module=module) if func: yield func @@ -1101,11 +1225,15 @@ segment_spec = Spec( args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)), contents=Spec().type(unicode).optional(), highlight_group=Spec().list( - highlight_group_spec().re('^(?:(?!:divider$).)+$', - lambda value: 'it is recommended that only divider highlight group names end with ":divider"') + highlight_group_spec().re( + '^(?:(?!:divider$).)+$', + (lambda value: 'it is recommended that only divider highlight group names end with ":divider"') + ) ).func(check_highlight_groups).optional(), - divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$', - lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(), + divider_highlight_group=highlight_group_spec().func(check_highlight_group).re( + ':divider$', + (lambda value: 'it is recommended that divider highlight group names end with ":divider"') + ).optional(), segments=sub_segments_spec, ).func(check_full_segment_data) sub_segments_spec.optional().list(segment_spec) @@ -1114,8 +1242,8 @@ segdict_spec=Spec( left=segments_spec().context_message('Error while loading segments from left side (key {key})'), right=segments_spec().context_message('Error while loading segments from right side (key {key})'), ).func( - lambda value, *args: (True, True, not (('left' in value) or ('right' in value))), - lambda value: 'segments dictionary must contain either left, right or both keys' + (lambda value, *args: (True, True, not (('left' in value) or ('right' in value)))), + (lambda value: 'segments dictionary must contain either left, right or both keys') ).context_message('Error while loading segments (key {key})').copy theme_spec = (Spec( default_module=segment_module_spec(), @@ -1218,7 +1346,12 @@ def check(paths=None, debug=False): sys.stderr.write(str(e) + '\n') hadproblem = True else: - if main_spec.match(main_config, data={'configs': configs, 'lists': lists}, context=(('', main_config),), echoerr=ee)[1]: + if main_spec.match( + main_config, + data={'configs': configs, 'lists': lists}, + context=(('', main_config),), + echoerr=ee + )[1]: hadproblem = True import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] @@ -1330,8 +1463,14 @@ def check(paths=None, debug=False): theme_configs[ext][theme] = config for ext, configs in theme_configs.items(): - data = {'ext': ext, 'colorscheme_configs': colorscheme_configs, 'import_paths': import_paths, - 'main_config': main_config, 'ext_theme_configs': configs, 'colors_config': colors_config} + data = { + 'ext': ext, + 'colorscheme_configs': colorscheme_configs, + 'import_paths': import_paths, + 'main_config': main_config, + 'ext_theme_configs': configs, + 'colors_config': colors_config + } for theme, config in configs.items(): data['theme'] = theme if theme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: From 27db44ac7a262fb9775b55ccbe07409489ea89b5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 13 Jul 2014 23:02:05 +0400 Subject: [PATCH 1164/1472] Implement configuration merging Fixes #418 --- powerline/__init__.py | 54 +++++--- powerline/bindings/config.py | 4 +- powerline/lint/__init__.py | 33 +++-- tests/lib/config_mock.py | 2 +- tests/test_config_merging.py | 251 +++++++++++++++++++++++++++++++++++ 5 files changed, 307 insertions(+), 37 deletions(-) create mode 100644 tests/test_config_merging.py diff --git a/powerline/__init__.py b/powerline/__init__.py index bf3e1212..207a24dd 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -14,13 +14,25 @@ from powerline.lib import mergedicts from threading import Lock, Event -def _find_config_file(search_paths, config_file): +def _config_loader_condition(path): + return path and os.path.isfile(path) + + +def _find_config_files(search_paths, config_file, config_loader=None, loader_callback=None): config_file += '.json' + found = False for path in search_paths: config_file_path = os.path.join(path, config_file) if os.path.isfile(config_file_path): - return config_file_path - raise IOError('Config file not found in search path: {0}'.format(config_file)) + yield config_file_path + found = True + elif config_loader: + config_loader.register_missing(_config_loader_condition, loader_callback, config_file_path) + if not found: + raise IOError('Config file not found in search paths ({0}): {1}'.format( + ', '.join(search_paths), + config_file + )) class PowerlineLogger(object): @@ -103,14 +115,14 @@ def get_config_paths(): config_paths = [config_path] config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR) if config_dirs is not None: - config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')]) + config_paths[:0] = reversed([os.path.join(d, 'powerline') for d in config_dirs.split(':')]) plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files') - config_paths.append(plugin_path) + config_paths.insert(0, plugin_path) return config_paths def generate_config_finder(get_config_paths=get_config_paths): - '''Generate find_config_file function + '''Generate find_config_files function This function will find .json file given its path. @@ -123,17 +135,17 @@ def generate_config_finder(get_config_paths=get_config_paths): to it or raise IOError if it failed to find the file. ''' config_paths = get_config_paths() - return lambda cfg_path: _find_config_file(config_paths, cfg_path) + return lambda *args: _find_config_files(config_paths, *args) -def load_config(cfg_path, find_config_file, config_loader, loader_callback=None): +def load_config(cfg_path, find_config_files, config_loader, loader_callback=None): '''Load configuration file and setup watches Watches are only set up if loader_callback is not None. :param str cfg_path: Path for configuration file that should be loaded. - :param function find_config_file: + :param function find_config_files: Function that finds configuration file. Check out the description of the return value of ``generate_config_finder`` function. :param ConfigLoader config_loader: @@ -144,16 +156,16 @@ def load_config(cfg_path, find_config_file, config_loader, loader_callback=None) :return: Configuration file contents. ''' - try: - path = find_config_file(cfg_path) - except IOError: - if loader_callback: - config_loader.register_missing(find_config_file, loader_callback, cfg_path) - raise - else: + found_files = find_config_files(cfg_path, config_loader, loader_callback) + ret = None + for path in found_files: if loader_callback: config_loader.register(loader_callback, path) - return config_loader.load(path) + if ret is None: + ret = config_loader.load(path) + else: + mergedicts(ret, config_loader.load(path)) + return ret def _get_log_handler(common_config): @@ -286,7 +298,7 @@ class Powerline(object): elif self.renderer_module[-1] == '.': self.renderer_module = self.renderer_module[:-1] - self.find_config_file = generate_config_finder(self.get_config_paths) + self.find_config_files = generate_config_finder(self.get_config_paths) self.cr_kwargs_lock = Lock() self.cr_kwargs = {} @@ -437,7 +449,7 @@ class Powerline(object): '''Load configuration and setup watches.''' return load_config( cfg_path, - self.find_config_file, + self.find_config_files, self.config_loader, self.cr_callbacks[type] ) @@ -445,7 +457,7 @@ class Powerline(object): def _purge_configs(self, type): function = self.cr_callbacks[type] self.config_loader.unregister_functions(set((function,))) - self.config_loader.unregister_missing(set(((self.find_config_file, function),))) + self.config_loader.unregister_missing(set(((self.find_config_files, function),))) def load_theme_config(self, name): '''Get theme configuration. @@ -601,7 +613,7 @@ class Powerline(object): pass functions = tuple(self.cr_callbacks.values()) self.config_loader.unregister_functions(set(functions)) - self.config_loader.unregister_missing(set(((self.find_config_file, function) for function in functions))) + self.config_loader.unregister_missing(set(((self.find_config_files, function) for function in functions))) def __enter__(self): return self diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 4f8d9402..e63a6c00 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -119,9 +119,9 @@ def source_tmux_files(pl, args): def create_powerline_logger(args): - find_config_file = generate_config_finder() + find_config_files = generate_config_finder() config_loader = ConfigLoader(run_once=True) - config = load_config('config', find_config_file, config_loader) + config = load_config('config', find_config_files, config_loader) common_config = finish_common_config(config['common']) logger = create_logger(common_config) return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config') diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index cae49b79..1417c67f 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,8 +1,8 @@ # vim:fileencoding=utf-8:noet from powerline.lint.markedjson import load -from powerline import generate_config_finder, get_config_paths -from powerline.lib.config import load_json_config +from powerline import generate_config_finder, get_config_paths, load_config +from powerline.lib.config import ConfigLoader from powerline.lint.markedjson.error import echoerr, MarkedError from powerline.segments.vim import vim_modes from powerline.lint.inspect import getconfigargspec @@ -1261,9 +1261,19 @@ theme_spec = (Spec( ).context_message('Error while loading theme')) +def generate_json_config_loader(lhadproblem): + def load_json_config(config_file_path, load=load, open_file=open_file): + with open_file(config_file_path) as config_file_fp: + r, hadproblem = load(config_file_fp) + if hadproblem: + lhadproblem[0] = True + return r + return load_json_config + + def check(paths=None, debug=False): search_paths = paths or get_config_paths() - find_config_file = generate_config_finder(lambda: search_paths) + find_config_files = generate_config_finder(lambda: search_paths) logger = logging.getLogger('powerline-lint') logger.setLevel(logging.DEBUG if debug else logging.ERROR) @@ -1271,6 +1281,11 @@ def check(paths=None, debug=False): ee = EchoErr(echoerr, logger) + lhadproblem = [False] + load_json_config = generate_json_config_loader(lhadproblem) + + config_loader = ConfigLoader(run_once=True, load=load_json_config) + paths = { 'themes': defaultdict(lambda: []), 'colorschemes': defaultdict(lambda: []), @@ -1326,17 +1341,9 @@ def check(paths=None, debug=False): typ, )) - lhadproblem = [False] - - def load_config(stream): - r, hadproblem = load(stream) - if hadproblem: - lhadproblem[0] = True - return r - hadproblem = False try: - main_config = load_json_config(find_config_file('config'), load=load_config, open_file=open_file) + main_config = load_config('config', find_config_files, config_loader) except IOError: main_config = {} sys.stderr.write('\nConfiguration file not found: config.json\n') @@ -1357,7 +1364,7 @@ def check(paths=None, debug=False): import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] try: - colors_config = load_json_config(find_config_file('colors'), load=load_config, open_file=open_file) + colors_config = load_config('colors', find_config_files, config_loader) except IOError: colors_config = {} sys.stderr.write('\nConfiguration file not found: colors.json\n') diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 30672cba..35eaf0cd 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -117,7 +117,7 @@ class TestPowerline(Powerline): return self.cr_kwargs -renderer = SimpleRenderer +renderer = EvenSimplerRenderer def get_powerline(**kwargs): diff --git a/tests/test_config_merging.py b/tests/test_config_merging.py new file mode 100644 index 00000000..4e3aaccb --- /dev/null +++ b/tests/test_config_merging.py @@ -0,0 +1,251 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals + +from powerline import Powerline +from tests import TestCase +from shutil import rmtree +import os +import json +from powerline.lib import mergedicts_copy as mdc + + +CONFIG_DIR = 'tests/config' + + +root_config = lambda: { + 'common': { + 'dividers': { + 'left': { + 'hard': '#>', + 'soft': '|>', + }, + 'right': { + 'hard': '<#', + 'soft': '<|', + }, + }, + 'spaces': 0, + 'interval': 0, + 'watcher': 'test', + }, + 'ext': { + 'test': { + 'theme': 'default', + 'colorscheme': 'default', + }, + }, +} + + +colors_config = lambda: { + 'colors': { + 'c1': 1, + 'c2': 2, + }, + 'gradients': { + }, +} + + +colorscheme_config = lambda: { + 'groups': { + 'g': {'fg': 'c1', 'bg': 'c2', 'attr': []}, + } +} + + +theme_config = lambda: { + 'segment_data': { + 's': { + 'before': 'b', + }, + }, + 'segments': { + 'left': [ + { + 'type': 'string', + 'name': 's', + 'contents': 't', + 'highlight_group': ['g'], + }, + ], + 'right': [], + } +} + + +main_tree = lambda: { + '1/config': root_config(), + '1/colors': colors_config(), + '1/colorschemes/default': colorscheme_config(), + '1/themes/test/default': theme_config(), +} + + +def mkdir_recursive(directory): + if os.path.isdir(directory): + return + mkdir_recursive(os.path.dirname(directory)) + os.mkdir(directory) + + +class TestPowerline(Powerline): + def get_config_paths(self): + return tuple(sorted([ + os.path.join(CONFIG_DIR, d) + for d in os.listdir(CONFIG_DIR) + ])) + + +class WithConfigTree(object): + __slots__ = ('tree', 'p', 'p_kwargs') + + def __init__(self, tree, p_kwargs={'run_once': True}): + self.tree = tree + self.p = None + self.p_kwargs = p_kwargs + + def __enter__(self, *args): + os.mkdir(CONFIG_DIR) + for k, v in self.tree.items(): + fname = os.path.join(CONFIG_DIR, k) + '.json' + mkdir_recursive(os.path.dirname(fname)) + with open(fname, 'w') as F: + json.dump(v, F) + self.p = TestPowerline( + ext='test', + renderer_module='tests.lib.config_mock', + **self.p_kwargs + ) + return self.p.__enter__(*args) + + def __exit__(self, *args): + try: + rmtree(CONFIG_DIR) + finally: + if self.p: + self.p.__exit__(*args) + + +class TestMerging(TestCase): + def assertRenderEqual(self, p, output, **kwargs): + self.assertEqual(p.render(**kwargs).replace(' ', ' '), output) + + def test_not_merged_config(self): + with WithConfigTree(main_tree()) as p: + self.assertRenderEqual(p, '{12} bt{2-}#>{--}') + + def test_root_config_merging(self): + with WithConfigTree(mdc(main_tree(), { + '2/config': { + 'common': { + 'dividers': { + 'left': { + 'hard': '!>', + } + } + } + }, + })) as p: + self.assertRenderEqual(p, '{12} bt{2-}!>{--}') + with WithConfigTree(mdc(main_tree(), { + '2/config': { + 'common': { + 'dividers': { + 'left': { + 'hard': '!>', + } + } + } + }, + '3/config': { + 'common': { + 'dividers': { + 'left': { + 'hard': '>>', + } + } + } + }, + })) as p: + self.assertRenderEqual(p, '{12} bt{2-}>>{--}') + with WithConfigTree(mdc(main_tree(), { + '2/config': { + 'common': { + 'spaces': 3, + } + }, + '3/config': { + 'common': { + 'dividers': { + 'left': { + 'hard': '>>', + } + } + } + }, + })) as p: + self.assertRenderEqual(p, '{12} bt {2-}>>{--}') + + def test_colors_config_merging(self): + with WithConfigTree(mdc(main_tree(), { + '2/colors': { + 'colors': { + 'c1': 3, + } + }, + })) as p: + self.assertRenderEqual(p, '{32} bt{2-}#>{--}') + with WithConfigTree(mdc(main_tree(), { + '2/colors': { + 'colors': { + 'c1': 3, + } + }, + '3/colors': { + 'colors': { + 'c1': 4, + } + }, + })) as p: + self.assertRenderEqual(p, '{42} bt{2-}#>{--}') + with WithConfigTree(mdc(main_tree(), { + '2/colors': { + 'colors': { + 'c1': 3, + } + }, + '3/colors': { + 'colors': { + 'c2': 4, + } + }, + })) as p: + self.assertRenderEqual(p, '{34} bt{4-}#>{--}') + + def test_colorschemes_merging(self): + with WithConfigTree(mdc(main_tree(), { + '2/colorschemes/default': { + 'groups': { + 'g': {'fg': 'c2', 'bg': 'c1', 'attr': []}, + } + }, + })) as p: + self.assertRenderEqual(p, '{21} bt{1-}#>{--}') + + def test_theme_merging(self): + with WithConfigTree(mdc(main_tree(), { + '2/themes/test/default': { + 'segment_data': { + 's': { + 'after': 'a', + } + } + }, + })) as p: + self.assertRenderEqual(p, '{12} bta{2-}#>{--}') + + +if __name__ == '__main__': + from tests import main + main() From 2fd04348f7e4388a9239cb81b14b2039efee2e2d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 13 Jul 2014 23:11:25 +0400 Subject: [PATCH 1165/1472] Fix name regex: allow single-letter names --- powerline/lint/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 1417c67f..f4e77032 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1209,7 +1209,7 @@ segment_module_spec = Spec().type(unicode).func(check_segment_module).optional() sub_segments_spec = Spec() segment_spec = Spec( type=Spec().oneof(type_keys).optional(), - name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(), + name=Spec().re('^[a-zA-Z_]\w*$').func(check_segment_name).optional(), exclude_modes=Spec().list(vim_mode_spec()).optional(), include_modes=Spec().list(vim_mode_spec()).optional(), draw_hard_divider=Spec().type(bool).optional(), From 01585edeabb56a96619cd3371bb2ade3ab11e508 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 13 Jul 2014 23:15:10 +0400 Subject: [PATCH 1166/1472] Fix errors reported by linter, allow running it during tests --- tests/test_config_merging.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/test_config_merging.py b/tests/test_config_merging.py index 4e3aaccb..1c8d5071 100644 --- a/tests/test_config_merging.py +++ b/tests/test_config_merging.py @@ -7,6 +7,8 @@ from shutil import rmtree import os import json from powerline.lib import mergedicts_copy as mdc +from subprocess import check_call +from operator import add CONFIG_DIR = 'tests/config' @@ -25,8 +27,8 @@ root_config = lambda: { }, }, 'spaces': 0, - 'interval': 0, - 'watcher': 'test', + 'interval': None, + 'watcher': 'auto', }, 'ext': { 'test': { @@ -117,6 +119,14 @@ class WithConfigTree(object): renderer_module='tests.lib.config_mock', **self.p_kwargs ) + if os.environ.get('POWERLINE_RUN_LINT_DURING_TESTS'): + try: + check_call(['scripts/powerline-lint'] + reduce(add, ( + ['-p', d] for d in self.p.get_config_paths() + ))) + except: + self.__exit__() + raise return self.p.__enter__(*args) def __exit__(self, *args): @@ -172,7 +182,7 @@ class TestMerging(TestCase): with WithConfigTree(mdc(main_tree(), { '2/config': { 'common': { - 'spaces': 3, + 'spaces': 1, } }, '3/config': { @@ -185,7 +195,7 @@ class TestMerging(TestCase): } }, })) as p: - self.assertRenderEqual(p, '{12} bt {2-}>>{--}') + self.assertRenderEqual(p, '{12} bt {2-}>>{--}') def test_colors_config_merging(self): with WithConfigTree(mdc(main_tree(), { From 66e09991470e1b4eb35244a7bf73dcca04bfd202 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 13 Jul 2014 23:27:44 +0400 Subject: [PATCH 1167/1472] Document configuration merging --- docs/source/configuration.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 50b39e54..8d21db77 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -30,6 +30,24 @@ corresponds to :file:`~/.config/powerline` on both platforms. If you need per-instance configuration please refer to :ref:`Local configuration overrides `. +.. note:: If you have multiple configuration files with the same name in + different directories then these files will be merged. Merging happens in + the following order: + + * :file:`{powerline_root}/powerline/config_files` is checked for + configuration first. Configuration from this source has least priority. + * :file:`$XDG_CONFIG_DIRS/powerline` directories are the next ones to check. + Checking happens in the reversed order: directories mentioned last are + checked before directories mentioned first. Each new found file is merged + with the result of previous merge. + * :file:`$XDG_CONFIG_HOME/powerline` directory is the last to check. + Configuration from there has top priority. + + When merging configuration only dictionaries are merged and they are merged + recursively: keys from next file overrule those from the previous unless + corresponding values are both dictionaries in which case these dictionaries + are merged and key is assigned the result of the merge. + .. _quick-guide: Quick setup guide From 0f4e1bafda72b3493b0c85c1ff351b1f5350157b Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 13:39:01 +0400 Subject: [PATCH 1168/1472] Refactor config_mock module to not use globals and fix tests --- tests/lib/config_mock.py | 141 ++++++++------ tests/test_cmdline.py | 3 +- tests/test_config_merging.py | 2 + tests/test_config_reload.py | 344 +++++++++++++++++------------------ tests/test_configuration.py | 322 ++++++++++++++++---------------- 5 files changed, 420 insertions(+), 392 deletions(-) diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 35eaf0cd..55f206d1 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -3,37 +3,53 @@ from threading import Lock from powerline.renderer import Renderer from powerline.lib.config import ConfigLoader from powerline import Powerline +from tests.lib import Args, replace_attr from copy import deepcopy from time import sleep from functools import wraps +import os -access_log = [] -access_lock = Lock() +class TestHelpers(object): + def __init__(self, config): + self.config = config + self.access_log = [] + self.access_lock = Lock() + def loader_condition(self, path): + return (path in self.config) and path -def load_json_config(config_file_path, *args, **kwargs): - global access_log - with access_lock: - access_log.append(config_file_path) - try: - return deepcopy(config_container['config'][config_file_path]) - except KeyError: - raise IOError(config_file_path) + def find_config_files(self, cfg_path, config_loader, loader_callback): + if cfg_path.endswith('.json'): + cfg_path = cfg_path[:-5] + if cfg_path.startswith('/'): + cfg_path = cfg_path.lstrip('/') + with self.access_lock: + self.access_log.append('check:' + cfg_path) + if cfg_path in self.config: + yield cfg_path + else: + if config_loader: + config_loader.register_missing(self.loader_condition, loader_callback, cfg_path) + raise IOError(('fcf:' if cfg_path.endswith('raise') else '') + cfg_path) + def load_json_config(self, config_file_path, *args, **kwargs): + if config_file_path.endswith('.json'): + config_file_path = config_file_path[:-5] + if config_file_path.startswith('/'): + config_file_path = config_file_path.lstrip('/') + with self.access_lock: + self.access_log.append('load:' + config_file_path) + try: + return deepcopy(self.config[config_file_path]) + except KeyError: + raise IOError(config_file_path) -def _find_config_file(config, search_paths, config_file): - if config_file.endswith('raise') and config_file not in config: - raise IOError('fcf:' + config_file) - return config_file - - -def pop_events(): - global access_log - with access_lock: - r = access_log[:] - access_log = [] - return r + def pop_events(self): + with self.access_lock: + r = self.access_log[:] + self.access_log = [] + return r def log_call(func): @@ -44,7 +60,7 @@ def log_call(func): return ret -class Watcher(object): +class TestWatcher(object): events = set() lock = Lock() @@ -109,20 +125,46 @@ class EvenSimplerRenderer(Renderer): class TestPowerline(Powerline): _created = False + def __init__(self, _helpers, **kwargs): + super(TestPowerline, self).__init__(**kwargs) + self._helpers = _helpers + self.find_config_files = _helpers.find_config_files + @staticmethod def get_local_themes(local_themes): return local_themes + @staticmethod + def get_config_paths(): + return [''] + def _will_create_renderer(self): return self.cr_kwargs + def _pop_events(self): + return self._helpers.pop_events() + renderer = EvenSimplerRenderer -def get_powerline(**kwargs): +class TestConfigLoader(ConfigLoader): + def __init__(self, _helpers, **kwargs): + watcher = TestWatcher() + super(TestConfigLoader, self).__init__( + load=_helpers.load_json_config, + watcher=watcher, + watcher_type='test', + **kwargs + ) + + +def get_powerline(config, **kwargs): + helpers = TestHelpers(config) return get_powerline_raw( + helpers, TestPowerline, + _helpers=helpers, ext='test', renderer_module='tests.lib.config_mock', logger=Logger(), @@ -130,45 +172,42 @@ def get_powerline(**kwargs): ) -def get_powerline_raw(PowerlineClass, **kwargs): +def select_renderer(simpler_renderer=False): global renderer - watcher = Watcher() - if kwargs.pop('simpler_renderer', False): - renderer = EvenSimplerRenderer - else: - renderer = SimpleRenderer + renderer = EvenSimplerRenderer if simpler_renderer else SimpleRenderer + + +def get_powerline_raw(helpers, PowerlineClass, **kwargs): + if not isinstance(helpers, TestHelpers): + helpers = TestHelpers(helpers) + select_renderer(kwargs.pop('simpler_renderer', False)) pl = PowerlineClass( - config_loader=ConfigLoader( - load=load_json_config, - watcher=watcher, - watcher_type='test', + config_loader=TestConfigLoader( + _helpers=helpers, run_once=kwargs.get('run_once') ), **kwargs ) - pl._watcher = watcher + pl._watcher = pl.config_loader.watcher return pl -config_container = None - - -def swap_attributes(cfg_container, powerline_module, replaces): - global config_container - config_container = cfg_container - if not replaces: - replaces = { - '_find_config_file': lambda *args: _find_config_file(config_container['config'], *args), - } - for attr, val in replaces.items(): - old_val = getattr(powerline_module, attr) - setattr(powerline_module, attr, val) - replaces[attr] = old_val - return replaces +def swap_attributes(config, powerline_module): + return replace_attr(powerline_module, 'os', Args( + path=Args( + isfile=lambda path: path.lstrip('/').replace('.json', '') in config, + join=os.path.join, + expanduser=lambda path: path, + realpath=lambda path: path, + dirname=os.path.dirname, + ), + environ={}, + )) def add_watcher_events(p, *args, **kwargs): - p._watcher._reset(args) + if isinstance(p._watcher, TestWatcher): + p._watcher._reset(args) while not p._will_create_renderer(): sleep(kwargs.get('interval', 0.1)) if not kwargs.get('wait', True): diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 9895709f..f1ff683a 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -71,6 +71,7 @@ class TestParser(TestCase): '-c', 'common.spaces=4', '-t', 'default.segment_data.hostname.before=H:', '-p', '.', + '-p', '..', '-R', 'smth={"abc":"def"}', ], { 'ext': ['shell'], @@ -90,7 +91,7 @@ class TestParser(TestCase): } } }, - 'config_path': ['.'], + 'config_path': ['.', '..'], 'renderer_arg': {'smth': {'abc': 'def'}}, }), (['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}), diff --git a/tests/test_config_merging.py b/tests/test_config_merging.py index 1c8d5071..7941e148 100644 --- a/tests/test_config_merging.py +++ b/tests/test_config_merging.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from powerline import Powerline from tests import TestCase +from tests.lib.config_mock import select_renderer from shutil import rmtree import os import json @@ -114,6 +115,7 @@ class WithConfigTree(object): mkdir_recursive(os.path.dirname(fname)) with open(fname, 'w') as F: json.dump(v, F) + select_renderer(simpler_renderer=True) self.p = TestPowerline( ext='test', renderer_module='tests.lib.config_mock', diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index cc6f331d..8deb0d1a 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -1,11 +1,12 @@ # vim:fileencoding=utf-8:noet from __future__ import unicode_literals -import powerline as powerline_module + from time import sleep -from tests import TestCase -from tests.lib import replace_item -from tests.lib.config_mock import swap_attributes, get_powerline, pop_events, add_watcher_events from copy import deepcopy +from functools import wraps + +from tests import TestCase +from tests.lib.config_mock import get_powerline, add_watcher_events config = { @@ -93,201 +94,192 @@ config = { } +def with_new_config(func): + @wraps(func) + def f(self): + return func(self, deepcopy(config)) + return f + + class TestConfigReload(TestCase): - def assertAccessEvents(self, *args): - self.assertEqual(set(pop_events()), set(args)) + def assertAccessEvents(self, p, *args): + events = set() + for event in args: + if ':' not in event: + events.add('check:' + event) + events.add('load:' + event) + else: + events.add(event) + self.assertEqual(set(p._pop_events()), events) - def test_noreload(self): - with get_powerline(run_once=True) as p: - with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['config']['common']['spaces'] = 1 - add_watcher_events(p, 'config', wait=False, interval=0.05) - # When running once thread should not start - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents() - self.assertEqual(p.logger._pop_msgs(), []) - # Without the following assertion test_reload_colors may fail for - # unknown reason (with AssertionError telling about “config” accessed - # one more time then needed) - pop_events() + @with_new_config + def test_noreload(self, config): + with get_powerline(config, run_once=True) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + config['config']['common']['spaces'] = 1 + add_watcher_events(p, 'config', wait=False, interval=0.05) + # When running once thread should not start + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p) + self.assertEqual(p.logger._pop_msgs(), []) - def test_reload_main(self): - with get_powerline(run_once=False) as p: - with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_reload_main(self, config): + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['config']['common']['spaces'] = 1 - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents('config') - self.assertEqual(p.logger._pop_msgs(), []) + config['config']['common']['spaces'] = 1 + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents(p, 'config') + self.assertEqual(p.logger._pop_msgs(), []) - config['config']['ext']['test']['theme'] = 'nonexistent' - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents('config', 'themes/test/nonexistent') - # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent']) + config['config']['ext']['test']['theme'] = 'nonexistent' + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents(p, 'config', 'check:themes/test/nonexistent') + # It should normally handle file missing error + self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent']) - config['config']['ext']['test']['theme'] = 'default' - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents('config', 'themes/test/default') - self.assertEqual(p.logger._pop_msgs(), []) + config['config']['ext']['test']['theme'] = 'default' + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents(p, 'config', 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), []) - config['config']['ext']['test']['colorscheme'] = 'nonexistent' - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents('config', 'colorschemes/nonexistent', 'colorschemes/test/__main__', 'colorschemes/test/nonexistent') - # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), [ - 'exception:test:powerline:Failed to load colorscheme: colorschemes/nonexistent', - 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/__main__', - 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/nonexistent', - 'exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent' - ]) + config['config']['ext']['test']['colorscheme'] = 'nonexistent' + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') + self.assertAccessEvents(p, 'config', 'check:colorschemes/nonexistent', 'check:colorschemes/test/__main__', 'check:colorschemes/test/nonexistent') + # It should normally handle file missing error + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load colorscheme: colorschemes/nonexistent', + 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/__main__', + 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/nonexistent', + 'exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent' + ]) - config['config']['ext']['test']['colorscheme'] = '2' - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>>') - self.assertAccessEvents('config', 'colorschemes/2', 'colorschemes/test/__main__', 'colorschemes/test/2') - self.assertEqual(p.logger._pop_msgs(), []) + config['config']['ext']['test']['colorscheme'] = '2' + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>>') + self.assertAccessEvents(p, 'config', 'check:colorschemes/2', 'check:colorschemes/test/__main__', 'colorschemes/test/2') + self.assertEqual(p.logger._pop_msgs(), []) - config['config']['ext']['test']['theme'] = '2' - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') - self.assertAccessEvents('config', 'themes/test/2') - self.assertEqual(p.logger._pop_msgs(), []) + config['config']['ext']['test']['theme'] = '2' + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') + self.assertAccessEvents(p, 'config', 'themes/test/2') + self.assertEqual(p.logger._pop_msgs(), []) - self.assertEqual(p.renderer.local_themes, None) - config['config']['ext']['test']['local_themes'] = 'something' - add_watcher_events(p, 'config') - self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') - self.assertAccessEvents('config') - self.assertEqual(p.logger._pop_msgs(), []) - self.assertEqual(p.renderer.local_themes, 'something') - pop_events() + self.assertEqual(p.renderer.local_themes, None) + config['config']['ext']['test']['local_themes'] = 'something' + add_watcher_events(p, 'config') + self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') + self.assertAccessEvents(p, 'config') + self.assertEqual(p.logger._pop_msgs(), []) + self.assertEqual(p.renderer.local_themes, 'something') - def test_reload_unexistent(self): - with get_powerline(run_once=False) as p: - with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_reload_unexistent(self, config): + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' - add_watcher_events(p, 'config') - # It may appear that p.logger._pop_msgs() is called after given - # exception is added to the mesagges, but before config_loader - # exception was added (this one: - # “exception:test:config_loader:Error while running condition - # function for key colorschemes/test/nonexistentraise: - # fcf:colorschemes/test/nonexistentraise”). - # sleep(0.1) - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - # For colorschemes/{test/,}*raise find_config_file raises - # IOError, but it does not do so for colorschemes/test/__main__, - # so powerline is trying to load this, but not other - # colorschemes/* - self.assertAccessEvents('config', 'colorschemes/test/__main__') - self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) + config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' + add_watcher_events(p, 'config') + # It may appear that p.logger._pop_msgs() is called after given + # exception is added to the mesagges, but before config_loader + # exception was added (this one: + # “exception:test:config_loader:Error while running condition + # function for key colorschemes/test/nonexistentraise: + # fcf:colorschemes/test/nonexistentraise”). + # sleep(0.1) + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + # For colorschemes/{test/,}*raise find_config_file raises + # IOError, but it does not do so for check:colorschemes/test/__main__, + # so powerline is trying to load this, but not other + # colorschemes/* + self.assertAccessEvents(p, 'config', 'check:colorschemes/test/__main__', 'check:colorschemes/nonexistentraise', 'check:colorschemes/test/nonexistentraise') + self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) - config['colorschemes/nonexistentraise'] = {} - config['colorschemes/test/nonexistentraise'] = { - 'groups': { - "str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]}, - "str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]}, - }, - } - while not p._will_create_renderer(): - sleep(0.1) - self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') - # Same as above - self.assertAccessEvents('colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'colorschemes/test/__main__') - self.assertEqual(p.logger._pop_msgs(), []) - pop_events() + config['colorschemes/nonexistentraise'] = {} + config['colorschemes/test/nonexistentraise'] = { + 'groups': { + "str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]}, + "str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]}, + }, + } + while not p._will_create_renderer(): + sleep(0.1) + self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>>') + # Same as above + self.assertAccessEvents(p, 'colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'check:colorschemes/test/__main__') + self.assertEqual(p.logger._pop_msgs(), []) - def test_reload_colors(self): - with get_powerline(run_once=False) as p: - with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_reload_colors(self, config): + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['colors']['colors']['col1'] = 5 - add_watcher_events(p, 'colors') - self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('colors') - self.assertEqual(p.logger._pop_msgs(), []) - pop_events() + config['colors']['colors']['col1'] = 5 + add_watcher_events(p, 'colors') + self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'colors') + self.assertEqual(p.logger._pop_msgs(), []) - def test_reload_colorscheme(self): - with get_powerline(run_once=False) as p: - with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_reload_colorscheme(self, config): + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' - add_watcher_events(p, 'colorschemes/test/default') - self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default') - self.assertEqual(p.logger._pop_msgs(), []) - pop_events() + config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' + add_watcher_events(p, 'colorschemes/test/default') + self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default') + self.assertEqual(p.logger._pop_msgs(), []) - def test_reload_theme(self): - with get_powerline(run_once=False) as p: - with replace_item(globals(), 'config', deepcopy(config)): - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_reload_theme(self, config): + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' - add_watcher_events(p, 'themes/test/default') - self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('themes/test/default') - self.assertEqual(p.logger._pop_msgs(), []) - pop_events() + config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' + add_watcher_events(p, 'themes/test/default') + self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), []) - def test_reload_theme_main(self): - with replace_item(globals(), 'config', deepcopy(config)): - config['config']['common']['interval'] = None - with get_powerline(run_once=False) as p: - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_reload_theme_main(self, config): + config['config']['common']['interval'] = None + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' - add_watcher_events(p, 'themes/test/default', wait=False) - self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('themes/test/default') - self.assertEqual(p.logger._pop_msgs(), []) - self.assertTrue(p._watcher._calls) - pop_events() + config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' + add_watcher_events(p, 'themes/test/default', wait=False) + self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), []) + self.assertTrue(p._watcher._calls) - def test_run_once_no_theme_reload(self): - with replace_item(globals(), 'config', deepcopy(config)): - config['config']['common']['interval'] = None - with get_powerline(run_once=True) as p: - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + @with_new_config + def test_run_once_no_theme_reload(self, config): + config['config']['common']['interval'] = None + with get_powerline(config, run_once=True) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') - config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' - add_watcher_events(p, 'themes/test/default', wait=False) - self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents() - self.assertEqual(p.logger._pop_msgs(), []) - self.assertEqual(p._watcher._calls, []) - pop_events() - - -replaces = {} - - -def setUpModule(): - global replaces - replaces = swap_attributes(globals(), powerline_module, replaces) - - -tearDownModule = setUpModule + config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' + add_watcher_events(p, 'themes/test/default', wait=False) + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p) + self.assertEqual(p.logger._pop_msgs(), []) if __name__ == '__main__': diff --git a/tests/test_configuration.py b/tests/test_configuration.py index faf355b5..a1d6b836 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,11 +1,8 @@ # vim:fileencoding=utf-8:noet from __future__ import unicode_literals, absolute_import, division import tests.vim as vim_module -import powerline as powerline_module from tests import TestCase -from tests.lib import replace_item -from tests.lib.config_mock import swap_attributes, get_powerline -from tests.lib.config_mock import get_powerline_raw +from tests.lib.config_mock import get_powerline, get_powerline_raw, swap_attributes from functools import wraps from copy import deepcopy import sys @@ -123,11 +120,19 @@ config = { } -def add_p_arg(func): +def with_new_config(func): @wraps(func) def f(self): - with get_powerline(run_once=True, simpler_renderer=True) as p: - func(self, p) + return func(self, deepcopy(config)) + return f + + +def add_args(func): + @wraps(func) + def f(self): + new_config = deepcopy(config) + with get_powerline(new_config, run_once=True, simpler_renderer=True) as p: + func(self, p, new_config) return f @@ -140,166 +145,158 @@ class TestRender(TestCase): class TestLines(TestRender): - @add_p_arg - def test_without_above(self, p): + @add_args + def test_without_above(self, p, config): self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}') self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', width=10) # self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344} f {--}', width=11) self.assertEqual(list(p.render_above_lines()), []) - def test_with_above(self): - with replace_item(globals(), 'config', deepcopy(config)): - old_segments = deepcopy(config['themes/test/default']['segments']) - config['themes/test/default']['segments']['above'] = [old_segments] - with get_powerline(run_once=True, simpler_renderer=True) as p: - self.assertRenderLinesEqual(p, [ - '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', - ]) - self.assertRenderLinesEqual(p, [ - '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', - ], width=10) + @with_new_config + def test_with_above(self, config): + old_segments = deepcopy(config['themes/test/default']['segments']) + config['themes/test/default']['segments']['above'] = [old_segments] + with get_powerline(config, run_once=True, simpler_renderer=True) as p: + self.assertRenderLinesEqual(p, [ + '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + ]) + self.assertRenderLinesEqual(p, [ + '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + ], width=10) - config['themes/test/default']['segments']['above'] = [old_segments] * 2 - with get_powerline(run_once=True, simpler_renderer=True) as p: - self.assertRenderLinesEqual(p, [ - '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', - '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', - ]) - self.assertRenderLinesEqual(p, [ - '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', - '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', - ], width=10) + config['themes/test/default']['segments']['above'] = [old_segments] * 2 + with get_powerline(config, run_once=True, simpler_renderer=True) as p: + self.assertRenderLinesEqual(p, [ + '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + ]) + self.assertRenderLinesEqual(p, [ + '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + ], width=10) class TestSegments(TestRender): - @add_p_arg - def test_display(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - config['themes/test/default']['segments']['left'][0]['display'] = False - config['themes/test/default']['segments']['left'][1]['display'] = True - config['themes/test/default']['segments']['right'][0]['display'] = False - self.assertRenderEqual(p, '{344} g{4-}>>{--}') + @add_args + def test_display(self, p, config): + config['themes/test/default']['segments']['left'][0]['display'] = False + config['themes/test/default']['segments']['left'][1]['display'] = True + config['themes/test/default']['segments']['right'][0]['display'] = False + self.assertRenderEqual(p, '{344} g{4-}>>{--}') class TestColorschemesHierarchy(TestRender): - @add_p_arg - def test_group_redirects(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('a', 'd1', draw_hard_divider=False), - highlighted_string('b', 'd2', draw_hard_divider=False), - highlighted_string('c', 'd3', draw_hard_divider=False), - highlighted_string('A', 'm1', draw_hard_divider=False), - highlighted_string('B', 'm2', draw_hard_divider=False), - highlighted_string('C', 'm3', draw_hard_divider=False), - highlighted_string('1', 'g1', draw_hard_divider=False), - highlighted_string('2', 'g2', draw_hard_divider=False), - highlighted_string('3', 'g3', draw_hard_divider=False), - ], - 'right': [], - } - self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}') - self.assertEqual(p.logger._pop_msgs(), []) + @add_args + def test_group_redirects(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('a', 'd1', draw_hard_divider=False), + highlighted_string('b', 'd2', draw_hard_divider=False), + highlighted_string('c', 'd3', draw_hard_divider=False), + highlighted_string('A', 'm1', draw_hard_divider=False), + highlighted_string('B', 'm2', draw_hard_divider=False), + highlighted_string('C', 'm3', draw_hard_divider=False), + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) - @add_p_arg - def test_group_redirects_no_main(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - del config['colorschemes/test/__main__'] - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('a', 'd1', draw_hard_divider=False), - highlighted_string('1', 'g1', draw_hard_divider=False), - highlighted_string('2', 'g2', draw_hard_divider=False), - highlighted_string('3', 'g3', draw_hard_divider=False), - ], - 'right': [], - } - self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}') - self.assertEqual(p.logger._pop_msgs(), []) + @add_args + def test_group_redirects_no_main(self, p, config): + del config['colorschemes/test/__main__'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('a', 'd1', draw_hard_divider=False), + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) - @add_p_arg - def test_group_redirects_no_top_default(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - del config['colorschemes/default'] - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('c', 'd3', draw_soft_divider=False), - highlighted_string('C', 'm3', draw_hard_divider=False), - ], - 'right': [], - } - self.assertRenderEqual(p, '{1112} c{1112}C{--}') - self.assertEqual(p.logger._pop_msgs(), []) + @add_args + def test_group_redirects_no_top_default(self, p, config): + del config['colorschemes/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('c', 'd3', draw_soft_divider=False), + highlighted_string('C', 'm3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{1112} c{1112}C{--}') + self.assertEqual(p.logger._pop_msgs(), []) - @add_p_arg - def test_group_redirects_no_test_default(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - del config['colorschemes/test/default'] - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('A', 'm1', draw_hard_divider=False), - highlighted_string('B', 'm2', draw_hard_divider=False), - highlighted_string('C', 'm3', draw_hard_divider=False), - highlighted_string('1', 'g1', draw_hard_divider=False), - highlighted_string('2', 'g2', draw_hard_divider=False), - highlighted_string('3', 'g3', draw_hard_divider=False), - ], - 'right': [], - } - self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}') - self.assertEqual(p.logger._pop_msgs(), []) + @add_args + def test_group_redirects_no_test_default(self, p, config): + del config['colorschemes/test/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('A', 'm1', draw_hard_divider=False), + highlighted_string('B', 'm2', draw_hard_divider=False), + highlighted_string('C', 'm3', draw_hard_divider=False), + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) - @add_p_arg - def test_group_redirects_only_main(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - del config['colorschemes/default'] - del config['colorschemes/test/default'] - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('C', 'm3', draw_hard_divider=False), - ], - 'right': [], - } - # Powerline is not able to work without default colorscheme - # somewhere, thus it will output error string - self.assertRenderEqual(p, 'colorschemes/test/default') - self.assertEqual(p.logger._pop_msgs(), [ - 'exception:test:powerline:Failed to load colorscheme: colorschemes/default', - 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default', - 'exception:test:powerline:Failed to create renderer: colorschemes/test/default', - 'exception:test:powerline:Failed to render: colorschemes/test/default', - ]) + @add_args + def test_group_redirects_only_main(self, p, config): + del config['colorschemes/default'] + del config['colorschemes/test/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('C', 'm3', draw_hard_divider=False), + ], + 'right': [], + } + # Powerline is not able to work without default colorscheme + # somewhere, thus it will output error string + self.assertRenderEqual(p, 'colorschemes/test/default') + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load colorscheme: colorschemes/default', + 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default', + 'exception:test:powerline:Failed to create renderer: colorschemes/test/default', + 'exception:test:powerline:Failed to render: colorschemes/test/default', + ]) - @add_p_arg - def test_group_redirects_only_top_default(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - del config['colorschemes/test/__main__'] - del config['colorschemes/test/default'] - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('1', 'g1', draw_hard_divider=False), - highlighted_string('2', 'g2', draw_hard_divider=False), - highlighted_string('3', 'g3', draw_hard_divider=False), - ], - 'right': [], - } - self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}') - self.assertEqual(p.logger._pop_msgs(), []) + @add_args + def test_group_redirects_only_top_default(self, p, config): + del config['colorschemes/test/__main__'] + del config['colorschemes/test/default'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('1', 'g1', draw_hard_divider=False), + highlighted_string('2', 'g2', draw_hard_divider=False), + highlighted_string('3', 'g3', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}') + self.assertEqual(p.logger._pop_msgs(), []) - @add_p_arg - def test_group_redirects_only_test_default(self, p): - with replace_item(globals(), 'config', deepcopy(config)): - del config['colorschemes/default'] - del config['colorschemes/test/__main__'] - config['themes/test/default']['segments'] = { - 'left': [ - highlighted_string('s', 'str1', draw_hard_divider=False), - ], - 'right': [], - } - self.assertRenderEqual(p, '{121} s{--}') - self.assertEqual(p.logger._pop_msgs(), []) + @add_args + def test_group_redirects_only_test_default(self, p, config): + del config['colorschemes/default'] + del config['colorschemes/test/__main__'] + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s', 'str1', draw_hard_divider=False), + ], + 'right': [], + } + self.assertRenderEqual(p, '{121} s{--}') + self.assertEqual(p.logger._pop_msgs(), []) class TestVim(TestCase): @@ -307,28 +304,25 @@ class TestVim(TestCase): # Regression test: test that segment obtains environment from vim, not # from os.environ. from powerline.vim import VimPowerline - with vim_module._with('environ', TEST='abc'): - with get_powerline_raw(VimPowerline) as powerline: - window = vim_module.current.window - window_id = 1 - winnr = window.number - self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>') - vim_module._environ['TEST'] = 'def' - self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>') - - -replaces = {} + import powerline as powerline_module + import vim + vim.vars['powerline_config_path'] = '/' + with swap_attributes(config, powerline_module): + with vim_module._with('environ', TEST='abc'): + with get_powerline_raw(config, VimPowerline) as powerline: + window = vim_module.current.window + window_id = 1 + winnr = window.number + self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>') + vim_module._environ['TEST'] = 'def' + self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>') def setUpModule(): - global replaces - replaces = swap_attributes(globals(), powerline_module, replaces) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) def tearDownModule(): - global replaces - replaces = swap_attributes(globals(), powerline_module, replaces) sys.path.pop(0) From 97978eaf77efdcaeaeae9ee9a2351c29750c4537 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:18:28 +0400 Subject: [PATCH 1169/1472] Improve visual_range segment --- .../config_files/themes/vim/default.json | 2 +- powerline/segments/vim.py | 71 +++++++++++++------ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index d80010df..bcd65016 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -22,7 +22,7 @@ }, { "name": "visual_range", - "exclude_modes": ["nc"], + "include_modes": ["v", "V", "^V", "s", "S", "^S"], "priority": 10 }, { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 077bd67e..d2ff9dcf 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -74,7 +74,10 @@ def window_cached(func): if segment_info['mode'] == 'nc': return cache.get(window_id) else: - r = func(**kwargs) + if getattr(func, 'powerline_requires_segment_info', False): + r = func(segment_info=segment_info, **kwargs) + else: + r = func(**kwargs) cache[window_id] = r return r @@ -99,29 +102,57 @@ def mode(pl, segment_info, override=None): return vim_modes[mode] +@window_cached @requires_segment_info -def visual_range(pl, segment_info): +def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='{vcols} cols', v_text_multiline='{rows} rows', V_text='{rows} rows'): '''Return the current visual selection range. - Returns a value similar to `showcmd`. + :param str CTRL_V_text: + Text to display when in block visual or select mode. + :param str v_text_oneline: + Text to display when in charaterwise visual or select mode, assuming + selection occupies only one line. + :param str v_text_multiline: + Text to display when in charaterwise visual or select mode, assuming + selection occupies more then one line. + :param str V_text: + Text to display when in linewise visual or select mode. + + All texts are format strings which are passed the following parameters: + + ========= ============================================================= + Parameter Description + ========= ============================================================= + sline Line number of the first line of the selection + eline Line number of the last line of the selection + scol Column number of the first character of the selection + ecol Column number of the last character of the selection + svcol Virtual column number of the first character of the selection + secol Virtual column number of the last character of the selection + rows Number of lines in the selection + cols Number of columns in the selection + vcols Number of virtual columns in the selection + ========= ============================================================= ''' - if segment_info['mode'] not in ('v', 'V', '^V'): - return None - pos_start = vim_funcs['getpos']('v') - pos_end = vim_funcs['getpos']('.') - # Workaround for vim's "excellent" handling of multibyte characters and display widths - pos_start[2] = vim_funcs['virtcol']([pos_start[1], pos_start[2], pos_start[3]]) - pos_end[2] = vim_funcs['virtcol']([pos_end[1], pos_end[2], pos_end[3]]) - visual_start = (int(pos_start[1]), int(pos_start[2])) - visual_end = (int(pos_end[1]), int(pos_end[2])) - diff_rows = abs(visual_end[0] - visual_start[0]) + 1 - diff_cols = abs(visual_end[1] - visual_start[1]) + 1 - if segment_info['mode'] == '^V': - return '{0} × {1}'.format(diff_rows, diff_cols) - elif segment_info['mode'] == 'V' or diff_rows > 1: - return '{0} rows'.format(diff_rows) - else: - return '{0} cols'.format(diff_cols) + sline, scol, soff = vim_funcs['getpos']("v")[1:] + eline, ecol, eoff = vim_funcs['getpos'](".")[1:] + svcol = vim_funcs['virtcol']([sline, scol, soff]) + evcol = vim_funcs['virtcol']([eline, ecol, eoff]) + rows = abs(eline - sline) + 1 + cols = abs(ecol - scol) + 1 + vcols = abs(evcol - svcol) + 1 + return { + '^': CTRL_V_text, + 's': v_text_oneline if rows == 1 else v_text_multiline, + 'S': V_text, + 'v': v_text_oneline if rows == 1 else v_text_multiline, + 'V': V_text, + }.get(segment_info['mode'][0], '').format( + sline=sline, eline=eline, + scol=scol, ecol=ecol, + svcol=svcol, evcol=evcol, + rows=rows, cols=cols, vcols=vcols, + ) @requires_segment_info From 702f43858b4f33bbc79617718f7c089e54ebf8a8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:44:29 +0400 Subject: [PATCH 1170/1472] =?UTF-8?q?Do=20not=20use=20=E2=80=9Crows?= =?UTF-8?q?=E2=80=9D=20and=20=E2=80=9Ccols=E2=80=9D=20in=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason: string “1 rows” is incorrect and I do not want to implement proper number handling (*proper* handling is *not* limited to plural/singular form like in English). --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index d2ff9dcf..57572c1b 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -104,7 +104,7 @@ def mode(pl, segment_info, override=None): @window_cached @requires_segment_info -def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='{vcols} cols', v_text_multiline='{rows} rows', V_text='{rows} rows'): +def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='C:{vcols}', v_text_multiline='L:{rows}', V_text='L:{rows}'): '''Return the current visual selection range. :param str CTRL_V_text: From 117661e18675886f6226b10f865a75f3fe2862af Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:49:53 +0400 Subject: [PATCH 1171/1472] Add tests for visual_range --- tests/test_segments.py | 46 ++++++++++++++++++++++++++++++++++++++++-- tests/vim.py | 30 +++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index fc729858..345a6ccc 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -589,8 +589,50 @@ class TestVim(TestCase): self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') def test_visual_range(self): - # TODO - pass + pl = Pl() + vr = partial(vim.visual_range, pl=pl) + vim_module.current.window.cursor = [0, 0] + try: + with vim_module._with('mode', 'i') as segment_info: + self.assertEqual(vr(segment_info=segment_info), '') + with vim_module._with('mode', '^V') as segment_info: + self.assertEqual(vr(segment_info=segment_info), '1 × 1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 4') + with vim_module._with('mode', '^S') as segment_info: + self.assertEqual(vr(segment_info=segment_info), '1 × 1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 4') + with vim_module._with('mode', 'V') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'L:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('mode', 'S') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'L:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('mode', 'v') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'C:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('mode', 's') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'C:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + finally: + vim_module._close(1) def test_modified_indicator(self): pl = Pl() diff --git a/tests/vim.py b/tests/vim.py index c7db4230..7867f563 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -342,15 +342,22 @@ def _emul_setwinvar(winnr, varname, value): @_vim def _emul_virtcol(expr): - if expr == '.' or isinstance(expr, list): + if expr == '.': return current.window.cursor[1] + 1 + if isinstance(expr, list) and len(expr) == 3: + return expr[-2] + expr[-1] raise NotImplementedError +_v_pos = None + + @_vim def _emul_getpos(expr): - if expr == '.' or expr == 'v': + if expr == '.': return [0, current.window.cursor[0] + 1, current.window.cursor[1] + 1, 0] + if expr == 'v': + return _v_pos or [0, current.window.cursor[0] + 1, current.window.cursor[1] + 1, 0] raise NotImplementedError @@ -848,6 +855,23 @@ class _WithNewTabPage(object): self.tab._close() +class _WithGlobal(object): + def __init__(self, **kwargs): + self.kwargs = kwargs + + def __enter__(self): + self.empty = object() + self.old = dict(((key, globals().get(key, self.empty)) for key in self.kwargs)) + globals().update(self.kwargs) + + def __exit__(self, *args): + for k, v in self.old.items(): + if v is self.empty: + globals().pop(k, None) + else: + globals()[k] = v + + @_vim def _with(key, *args, **kwargs): if key == 'buffer': @@ -870,6 +894,8 @@ def _with(key, *args, **kwargs): return _WithSplit() elif key == 'tabpage': return _WithNewTabPage(*args, **kwargs) + elif key == 'vpos': + return _WithGlobal(_v_pos=[0, kwargs['line'], kwargs['col'], kwargs['off']]) class error(Exception): From f4b5c6a63f388c1316a3f91d0b637206ea72b41f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 23:03:59 +0400 Subject: [PATCH 1172/1472] Add fix for old Vims where getpos does not return list of integers --- powerline/segments/vim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 57572c1b..b8ebb286 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -134,8 +134,8 @@ def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneli vcols Number of virtual columns in the selection ========= ============================================================= ''' - sline, scol, soff = vim_funcs['getpos']("v")[1:] - eline, ecol, eoff = vim_funcs['getpos'](".")[1:] + sline, scol, soff = [int(v) for v in vim_funcs['getpos']("v")[1:]] + eline, ecol, eoff = [int(v) for v in vim_funcs['getpos'](".")[1:]] svcol = vim_funcs['virtcol']([sline, scol, soff]) evcol = vim_funcs['virtcol']([eline, ecol, eoff]) rows = abs(eline - sline) + 1 From 145e1c2050a7a84f265e8dc373fa9bdd781643d6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 19:36:33 +0400 Subject: [PATCH 1173/1472] Only check that all colorschemes have theme, but not the opposite --- powerline/lint/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index f4e77032..6af261c6 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1329,7 +1329,7 @@ def check(paths=None, debug=False): name = os.path.basename(path)[:-5] configs['top_' + typ][name] = path - diff = set(configs['themes']) ^ set(configs['colorschemes']) + diff = set(configs['colorschemes']) - set(configs['themes']) if diff: hadproblem = True for ext in diff: From bdde4ae99f3cbba53961d372bb2722aa90bb71cb Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 20:29:03 +0400 Subject: [PATCH 1174/1472] Implement theme hierarchy Fixes #783 --- docs/source/configuration.rst | 8 +- docs/source/configuration/reference.rst | 52 +++- powerline/__init__.py | 115 +++++---- powerline/config_files/config.json | 13 +- powerline/config_files/themes/powerline.json | 13 + powerline/lint/__init__.py | 258 +++++++++++++------ powerline/theme.py | 4 +- tests/test_config_merging.py | 76 +++--- tests/test_config_reload.py | 86 +++++-- tests/test_configuration.py | 125 +++++++-- 10 files changed, 511 insertions(+), 239 deletions(-) create mode 100644 powerline/config_files/themes/powerline.json diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 8d21db77..88663a6c 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -17,9 +17,11 @@ Powerline provides default configurations in the following locations: :file:`powerline/config.json` :ref:`Colorschemes ` :file:`powerline/colorschemes/{name}.json`, - :file:`powerline/colorscheme/__main__.json`, + :file:`powerline/colorscheme/{extension}/__main__.json`, :file:`powerline/colorschemes/{extension}/{name}.json` :ref:`Themes ` + :file:`powerline/themes/{top_theme}.json`, + :file:`powerline/themes/{extension}/__main__.json`, :file:`powerline/themes/{extension}/default.json` The default configuration files are stored in the main package. User @@ -48,6 +50,10 @@ overrides `. corresponding values are both dictionaries in which case these dictionaries are merged and key is assigned the result of the merge. +.. note:: Some configuration files (i.e. themes and colorschemes) have two level + of merging: first happens merging described above, second theme- or + colorscheme-specific merging happens. + .. _quick-guide: Quick setup guide diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 28c11863..6b899ea7 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -58,15 +58,6 @@ Common configuration is a subdictionary that is a value of ``common`` key in codes thus rendering powerline prompt colorless. Valid values: ``"tmux"``, ``"screen"``, ``null`` (default). -``dividers`` - Defines the dividers used in all Powerline extensions. This option - should usually only be changed if you don't have a patched font, or if - you use a font patched with the legacy font patcher. - - The ``hard`` dividers are used to divide segments with different - background colors, while the ``soft`` dividers are used to divide - segments with the same background color. - .. _config-common-paths: ``paths`` @@ -95,6 +86,12 @@ Common configuration is a subdictionary that is a value of ``common`` key in Boolean, determines whether configuration should be reloaded at all. Defaults to ``True``. +.. _config-common-default_top_theme: + +``default_top_theme`` + String, determines which top-level theme will be used as the default. + Defaults to ``powerline``. See `Themes`_ section for more details. + Extension-specific configuration -------------------------------- @@ -109,6 +106,12 @@ Common configuration is a subdictionary that is a value of ``ext`` key in Defines the theme used for this extension. +``top_theme`` + .. _config-ext-top_theme: + + Defines the top-level theme used for this extension. See `Themes`_ section + for more details. + ``local_themes`` .. _config-ext-local_themes: @@ -155,7 +158,7 @@ Colorschemes ============ :Location: :file:`powerline/colorschemes/{name}.json`, - :file:`powerline/colorscheme/__main__.json`, + :file:`powerline/colorschemes/__main__.json`, :file:`powerline/colorschemes/{extension}/{name}.json` Colorscheme files are processed in order given: definitions from each next file @@ -213,7 +216,17 @@ override those from each previous file. It is required that either Themes ====== -:Location: :file:`powerline/themes/{extension}/{name}.json` +:Location: :file:`powerline/themes/{top_theme}.json`, + :file:`powerline/themes/__main__.json`, + :file:`powerline/themes/{extension}/{name}.json` + +Theme files are processed in order given: definitions from each next file +override those from each previous file. It is required that file +:file:`powerline/themes/{extension}/{name}.json` exists. + +`{top_theme}` component of the file name is obtained either from :ref:`top_theme +extension-specific key ` or from :ref:`default_top_theme +common configuration key `. ``name`` Name of the theme. @@ -223,6 +236,20 @@ Themes ``default_module`` Python module where segments will be looked by default. +``spaces`` + Defines number of spaces just before the divider (on the right side) or just + after it (on the left side). These spaces will not be added if divider is + not drawn. + +``dividers`` + Defines the dividers used in all Powerline extensions. This option + should usually only be changed if you don't have a patched font, or if + you use a font patched with the legacy font patcher. + + The ``hard`` dividers are used to divide segments with different + background colors, while the ``soft`` dividers are used to divide + segments with the same background color. + .. _config-themes-segment_data: ``segment_data`` @@ -240,6 +267,9 @@ Themes `. For the :ref:`default theme ` itself step 2 is obviously avoided. + .. note:: Top-level themes are out of equation here: they are merged + before the above merging process happens. + ``segments`` A dict with a ``left`` and a ``right`` lists, consisting of segment dictionaries. Shell themes may also contain ``above`` list of dictionaries. diff --git a/powerline/__init__.py b/powerline/__init__.py index 207a24dd..08446057 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -215,6 +215,7 @@ def finish_common_config(common_config): paths. ''' common_config = common_config.copy() + common_config.setdefault('default_top_theme', 'powerline') common_config.setdefault('paths', []) common_config.setdefault('watcher', 'auto') common_config.setdefault('log_level', 'WARNING') @@ -345,13 +346,15 @@ class Powerline(object): if load_main: self._purge_configs('main') config = self.load_main_config() - self.common_config = config['common'] + self.common_config = finish_common_config(config['common']) if self.common_config != self.prev_common_config: common_config_differs = True - self.prev_common_config = self.common_config + load_theme = (load_theme + or not self.prev_common_config + or self.prev_common_config['default_top_theme'] != self.common_config['default_top_theme']) - self.common_config = finish_common_config(self.common_config) + self.prev_common_config = self.common_config self.import_paths = self.common_config['paths'] @@ -386,6 +389,8 @@ class Powerline(object): if interval is not None and not self.config_loader.is_alive(): self.config_loader.start() + self.default_top_theme = self.common_config['default_top_theme'] + self.ext_config = config['ext'][self.ext] if self.ext_config != self.prev_ext_config: ext_config_differs = True @@ -445,30 +450,20 @@ class Powerline(object): ''' return get_config_paths() - def _load_config(self, cfg_path, type): + def _load_config(self, cfg_path, cfg_type): '''Load configuration and setup watches.''' return load_config( cfg_path, self.find_config_files, self.config_loader, - self.cr_callbacks[type] + self.cr_callbacks[cfg_type] ) - def _purge_configs(self, type): - function = self.cr_callbacks[type] + def _purge_configs(self, cfg_type): + function = self.cr_callbacks[cfg_type] self.config_loader.unregister_functions(set((function,))) self.config_loader.unregister_missing(set(((self.find_config_files, function),))) - def load_theme_config(self, name): - '''Get theme configuration. - - :param str name: - Name of the theme to load. - - :return: dictionary with :ref:`theme configuration ` - ''' - return self._load_config(os.path.join('themes', self.ext, name), 'theme') - def load_main_config(self): '''Get top-level configuration. @@ -476,6 +471,47 @@ class Powerline(object): ''' return self._load_config('config', 'main') + def _load_hierarhical_config(self, cfg_type, levels, ignore_levels): + '''Load and merge multiple configuration files + + :param str cfg_type: + Type of the loaded configuration files (e.g. ``colorscheme``, + ``theme``). + :param list levels: + Configuration names resembling levels in hierarchy, sorted by + priority. Configuration file names with higher priority should go + last. + :param set ignore_levels: + If only files listed in this variable are present then configuration + file is considered not loaded: at least one file on the level not + listed in this variable must be present. + ''' + config = {} + loaded = 0 + exceptions = [] + for i, cfg_path in enumerate(levels): + try: + lvl_config = self._load_config(cfg_path, cfg_type) + except IOError as e: + if sys.version_info < (3,): + tb = sys.exc_info()[2] + exceptions.append((e, tb)) + else: + exceptions.append(e) + else: + if i not in ignore_levels: + loaded += 1 + mergedicts(config, lvl_config) + if not loaded: + for exception in exceptions: + if type(exception) is tuple: + e = exception[0] + else: + e = exception + self.exception('Failed to load %s: {0}' % cfg_type, e, exception=exception) + raise e + return config + def load_colorscheme_config(self, name): '''Get colorscheme. @@ -484,40 +520,27 @@ class Powerline(object): :return: dictionary with :ref:`colorscheme configuration `. ''' - # TODO Make sure no colorscheme name ends with __ (do it in - # powerline-lint) levels = ( os.path.join('colorschemes', name), os.path.join('colorschemes', self.ext, '__main__'), os.path.join('colorschemes', self.ext, name), ) - config = {} - loaded = 0 - exceptions = [] - for cfg_path in levels: - try: - lvl_config = self._load_config(cfg_path, 'colorscheme') - except IOError as e: - if sys.version_info < (3,): - tb = sys.exc_info()[2] - exceptions.append((e, tb)) - else: - exceptions.append(e) - else: - if not cfg_path.endswith('__'): - loaded += 1 - # TODO Either make sure `attr` list is always present or make - # mergedicts not merge group definitions. - mergedicts(config, lvl_config) - if not loaded: - for exception in exceptions: - if type(exception) is tuple: - e = exception[0] - else: - e = exception - self.exception('Failed to load colorscheme: {0}', e, exception=exception) - raise e - return config + return self._load_hierarhical_config('colorscheme', levels, (1,)) + + def load_theme_config(self, name): + '''Get theme configuration. + + :param str name: + Name of the theme to load. + + :return: dictionary with :ref:`theme configuration ` + ''' + levels = ( + os.path.join('themes', self.ext_config.get('top_theme') or self.default_top_theme), + os.path.join('themes', self.ext, '__main__'), + os.path.join('themes', self.ext, name), + ) + return self._load_hierarhical_config('theme', levels, (0, 1,)) def load_colors_config(self): '''Get colorscheme. diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index f64bdfc1..4968fc6b 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -1,17 +1,6 @@ { "common": { - "term_truecolor": false, - "dividers": { - "left": { - "hard": " ", - "soft": " " - }, - "right": { - "hard": " ", - "soft": " " - } - }, - "spaces": 1 + "term_truecolor": false }, "ext": { "ipython": { diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json new file mode 100644 index 00000000..ea29344e --- /dev/null +++ b/powerline/config_files/themes/powerline.json @@ -0,0 +1,13 @@ +{ + "dividers": { + "left": { + "hard": " ", + "soft": " " + }, + "right": { + "hard": " ", + "soft": " " + } + }, + "spaces": 1 +} diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6af261c6..721d1a5a 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -431,9 +431,6 @@ class WithPath(object): def check_matcher_func(ext, match_name, data, context, echoerr): import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])] - if match_name == '__tabline__': - return True, False - match_module, separator, match_function = match_name.rpartition('.') if not separator: match_module = 'powerline.matchers.{0}'.format(ext) @@ -512,23 +509,30 @@ def check_config(d, theme, data, context, echoerr): return True, False, False +def check_top_theme(theme, data, context, echoerr): + if theme not in data['configs']['top_themes']: + echoerr(context='Error while checking extension configuration (key {key})'.format(key=context_key(context)), + context_mark=context[-2][0].mark, + problem='failed to find top theme {0}'.format(theme), + problem_mark=theme.mark) + return True, False, True + return True, False, False + + divider_spec = Spec().type(unicode).len('le', 3, lambda value: 'Divider {0!r} is too large!'.format(value)).copy -divside_spec = Spec( - hard=divider_spec(), - soft=divider_spec(), +ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy +top_theme_spec = Spec().type(unicode).func(check_top_theme).copy +ext_spec = Spec( + colorscheme=Spec().type(unicode).func( + (lambda *args: check_config('colorschemes', *args)) + ), + theme=ext_theme_spec(), + top_theme=top_theme_spec().optional(), ).copy -colorscheme_spec = Spec().type(unicode).func(lambda *args: check_config('colorschemes', *args)).copy -theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy main_spec = (Spec( common=Spec( - dividers=Spec( - left=divside_spec(), - right=divside_spec(), - ), - spaces=Spec().unsigned().cmp( - 'le', 2, lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value) - ), + default_top_theme=top_theme_spec().optional(), term_truecolor=Spec().type(bool).optional(), # Python is capable of loading from zip archives. Thus checking path # only for existence of the path, not for it being a directory @@ -556,36 +560,29 @@ main_spec = (Spec( watcher=Spec().type(unicode).oneof(set(('auto', 'inotify', 'stat'))).optional(), ).context_message('Error while loading common configuration (key {key})'), ext=Spec( - vim=Spec( - colorscheme=colorscheme_spec(), - theme=theme_spec(), - local_themes=Spec().unknown_spec( - lambda *args: check_matcher_func('vim', *args), theme_spec() + vim=ext_spec().update( + local_themes=Spec( + __tabline__=ext_theme_spec(), + ).unknown_spec( + lambda *args: check_matcher_func('vim', *args), ext_theme_spec() ), ).optional(), - ipython=Spec( - colorscheme=colorscheme_spec(), - theme=theme_spec(), + ipython=ext_spec().update( local_themes=Spec( - in2=theme_spec(), - out=theme_spec(), - rewrite=theme_spec(), + in2=ext_theme_spec(), + out=ext_theme_spec(), + rewrite=ext_theme_spec(), ), ).optional(), - shell=Spec( - colorscheme=colorscheme_spec(), - theme=theme_spec(), + shell=ext_spec().update( local_themes=Spec( - continuation=theme_spec(), - select=theme_spec(), + continuation=ext_theme_spec(), + select=ext_theme_spec(), ), ).optional(), ).unknown_spec( check_ext, - Spec( - colorscheme=colorscheme_spec(), - theme=theme_spec(), - ) + ext_spec(), ).context_message('Error while loading extensions configuration (key {key})'), ).context_message('Error while loading main configuration')) @@ -771,7 +768,7 @@ type_keys = { } required_keys = { 'function': set(('name',)), - 'string': set(('contents',)), + 'string': set(()), 'filler': set(), 'segment_list': set(('name', 'segments',)), } @@ -845,11 +842,11 @@ def check_full_segment_data(segment, data, context, echoerr): ext = data['ext'] theme_segment_data = context[0][1].get('segment_data', {}) - top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) - if not top_theme_name or data['theme'] == top_theme_name: + main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + if not main_theme_name or data['theme'] == main_theme_name: top_segment_data = {} else: - top_segment_data = data['ext_theme_configs'].get(top_theme_name, {}).get('segment_data', {}) + top_segment_data = data['ext_theme_configs'].get(main_theme_name, {}).get('segment_data', {}) names = [segment['name']] if segment.get('type', 'function') == 'function': @@ -977,12 +974,16 @@ def check_segment_name(name, data, context, echoerr): return True, False, hadproblem elif context[-2][1].get('type') != 'segment_list': if name not in context[0][1].get('segment_data', {}): - top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) - if data['theme'] == top_theme_name: - top_theme = {} + main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + if data['theme'] == main_theme_name: + main_theme = {} else: - top_theme = data['ext_theme_configs'].get(top_theme_name, {}) - if name not in top_theme.get('segment_data', {}): + main_theme = data['ext_theme_configs'].get(main_theme_name, {}) + if ( + name not in main_theme.get('segment_data', {}) + and name not in data['ext_theme_configs'].get('__main__', {}).get('segment_data', {}) + and not any(((name in theme.get('segment_data', {})) for theme in data['top_themes'].values())) + ): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), problem='found useless use of name key (such name is not present in theme/segment_data)', problem_mark=name.mark) @@ -1070,34 +1071,49 @@ def check_highlight_groups(hl_groups, data, context, echoerr): return True, False, False -def check_segment_data_key(key, data, context, echoerr): +def list_themes(data, context): + theme_type = data['theme_type'] ext = data['ext'] - top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) - is_top_theme = (data['theme'] == top_theme_name) - if is_top_theme: - themes = data['ext_theme_configs'].values() + main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + is_main_theme = (data['theme'] == main_theme_name) + if theme_type == 'top': + return list(itertools.chain(*[ + [(ext, theme) for theme in theme_configs.values()] + for ext, theme_configs in data['theme_configs'].items() + ])) + elif theme_type == 'main' or is_main_theme: + return [(ext, theme) for theme in data['ext_theme_configs'].values()] else: - themes = [context[0][1]] + return [(ext, context[0][1])] - for theme in themes: + +def check_segment_data_key(key, data, context, echoerr): + has_module_name = '.' in key + found = False + for ext, theme in list_themes(data, context): for segments in theme.get('segments', {}).values(): - found = False for segment in segments: if 'name' in segment: - if key == segment['name']: - found = True - module = segment.get('module', theme.get('default_module', 'powerline.segments.' + ext)) - if key == unicode(module) + '.' + unicode(segment['name']): - found = True + if has_module_name: + module = segment.get('module', theme.get('default_module', 'powerline.segments.' + ext)) + full_name = unicode(module) + '.' + unicode(segment['name']) + if key == full_name: + found = True + break + else: + if key == segment['name']: + found = True + break if found: break if found: break else: - echoerr(context='Error while checking segment data', - problem='found key {0} that cannot be associated with any segment'.format(key), - problem_mark=key.mark) - return True, False, True + if data['theme_type'] != 'top': + echoerr(context='Error while checking segment data', + problem='found key {0} that cannot be associated with any segment'.format(key), + problem_mark=key.mark) + return True, False, True return True, False, False @@ -1109,8 +1125,8 @@ threaded_args_specs = { } -def check_args_variant(segment, args, data, context, echoerr): - argspec = getconfigargspec(segment) +def check_args_variant(func, args, data, context, echoerr): + argspec = getconfigargspec(func) present_args = set(args) all_args = set(argspec.args) required_args = set(argspec.args[:-len(argspec.defaults)]) @@ -1132,7 +1148,7 @@ def check_args_variant(segment, args, data, context, echoerr): problem_mark=next(iter(present_args - all_args)).mark) hadproblem = True - if isinstance(segment, ThreadedSegment): + if isinstance(func, ThreadedSegment): for key in set(threaded_args_specs) & present_args: proceed, khadproblem = threaded_args_specs[key].match( args[key], @@ -1149,13 +1165,13 @@ def check_args_variant(segment, args, data, context, echoerr): return hadproblem -def check_args(get_segment_variants, args, data, context, echoerr): +def check_args(get_functions, args, data, context, echoerr): new_echoerr = DelayedEchoErr(echoerr) count = 0 hadproblem = False - for segment in get_segment_variants(data, context, new_echoerr): + for func in get_functions(data, context, new_echoerr): count += 1 - shadproblem = check_args_variant(segment, args, data, context, echoerr) + shadproblem = check_args_variant(func, args, data, context, echoerr) if shadproblem: hadproblem = True @@ -1171,7 +1187,7 @@ def check_args(get_segment_variants, args, data, context, echoerr): return True, False, hadproblem -def get_one_segment_variant(data, context, echoerr): +def get_one_segment_function(data, context, echoerr): name = context[-2][1].get('name') if name: func = import_segment(name, data, context, echoerr) @@ -1179,7 +1195,7 @@ def get_one_segment_variant(data, context, echoerr): yield func -def get_all_possible_segments(data, context, echoerr): +def get_all_possible_functions(data, context, echoerr): name = context[-2][0] module, name = name.rpartition('.')[::2] if module: @@ -1187,13 +1203,13 @@ def get_all_possible_segments(data, context, echoerr): if func: yield func else: - for theme_config in data['ext_theme_configs'].values(): + for ext, theme_config in list_themes(data, context): for segments in theme_config.get('segments', {}).values(): for segment in segments: if segment.get('type', 'function') == 'function': module = segment.get( 'module', - context[0][1].get('default_module', 'powerline.segments.' + data['ext']) + theme_config.get('default_module', 'powerline.segments.' + data['ext']) ) func = import_segment(name, data, context, echoerr, module=module) if func: @@ -1222,7 +1238,7 @@ segment_spec = Spec( before=Spec().type(unicode).optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), align=Spec().oneof(set('lr')).optional(), - args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)), + args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_function, *args, **kwargs)), contents=Spec().type(unicode).optional(), highlight_group=Spec().list( highlight_group_spec().re( @@ -1245,20 +1261,52 @@ segdict_spec=Spec( (lambda value, *args: (True, True, not (('left' in value) or ('right' in value)))), (lambda value: 'segments dictionary must contain either left, right or both keys') ).context_message('Error while loading segments (key {key})').copy -theme_spec = (Spec( - default_module=segment_module_spec(), +divside_spec = Spec( + hard=divider_spec(), + soft=divider_spec(), +).copy +segment_data_value_spec = Spec( + after=Spec().type(unicode).optional(), + before=Spec().type(unicode).optional(), + display=Spec().type(bool).optional(), + args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_functions, *args, **kwargs)), + contents=Spec().type(unicode).optional(), +).copy +dividers_spec = Spec( + left=divside_spec(), + right=divside_spec(), +).copy +spaces_spec = Spec().unsigned().cmp( + 'le', 2, (lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value)) +).copy +common_theme_spec = Spec( + default_module=segment_module_spec().optional(), +).context_message('Error while loading theme').copy +top_theme_spec = common_theme_spec().update( + dividers=dividers_spec(), + spaces=spaces_spec(), segment_data=Spec().unknown_spec( Spec().func(check_segment_data_key), - Spec( - after=Spec().type(unicode).optional(), - before=Spec().type(unicode).optional(), - display=Spec().type(bool).optional(), - args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_segments, *args, **kwargs)), - contents=Spec().type(unicode).optional(), - ), + segment_data_value_spec(), + ).optional().context_message('Error while loading segment data (key {key})'), +) +main_theme_spec = common_theme_spec().update( + dividers=dividers_spec().optional(), + spaces=spaces_spec().optional(), + segment_data=Spec().unknown_spec( + Spec().func(check_segment_data_key), + segment_data_value_spec(), + ).optional().context_message('Error while loading segment data (key {key})'), +) +theme_spec = common_theme_spec().update( + dividers=dividers_spec().optional(), + spaces=spaces_spec().optional(), + segment_data=Spec().unknown_spec( + Spec().func(check_segment_data_key), + segment_data_value_spec(), ).optional().context_message('Error while loading segment data (key {key})'), segments=segdict_spec().update(above=Spec().list(segdict_spec()).optional()), -).context_message('Error while loading theme')) +) def generate_json_config_loader(lhadproblem): @@ -1315,6 +1363,8 @@ def check(paths=None, debug=False): hadproblem = True sys.stderr.write('Path {0} is supposed to be a directory, but it is not\n'.format(d)) + hadproblem = False + configs = defaultdict(lambda: defaultdict(lambda: {})) for typ in ('themes', 'colorschemes'): for ext in paths[typ]: @@ -1324,6 +1374,11 @@ def check(paths=None, debug=False): name = subp[:-5] if name != '__main__': lists[typ].add(name) + if name.startswith('__') or name.endswith('__'): + hadproblem = True + sys.stderr.write('File name is not supposed to start or end with “__”: {0}'.format( + os.path.join(d, subp) + )) configs[typ][ext][name] = os.path.join(d, subp) for path in paths['top_' + typ]: name = os.path.basename(path)[:-5] @@ -1341,7 +1396,6 @@ def check(paths=None, debug=False): typ, )) - hadproblem = False try: main_config = load_config('config', find_config_files, config_loader) except IOError: @@ -1469,17 +1523,53 @@ def check(paths=None, debug=False): hadproblem = True theme_configs[ext][theme] = config + top_theme_configs = {} + for top_theme, top_theme_file in configs['top_themes'].items(): + with open_file(top_theme_file) as config_file_fp: + try: + config, lhadproblem = load(config_file_fp) + except MarkedError as e: + sys.stderr.write(str(e) + '\n') + hadproblem = True + continue + if lhadproblem: + hadproblem = True + top_theme_configs[top_theme] = config + for ext, configs in theme_configs.items(): data = { 'ext': ext, 'colorscheme_configs': colorscheme_configs, 'import_paths': import_paths, 'main_config': main_config, + 'top_themes': top_theme_configs, 'ext_theme_configs': configs, 'colors_config': colors_config } for theme, config in configs.items(): data['theme'] = theme - if theme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + if theme == '__main__': + data['theme_type'] = 'main' + spec = main_theme_spec + else: + data['theme_type'] = 'regular' + spec = theme_spec + if spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: hadproblem = True + + for top_theme, config in top_theme_configs.items(): + data = { + 'ext': ext, + 'colorscheme_configs': colorscheme_configs, + 'import_paths': import_paths, + 'main_config': main_config, + 'theme_configs': theme_configs, + 'ext_theme_configs': configs, + 'colors_config': colors_config + } + data['theme_type'] = 'top' + data['theme'] = top_theme + if top_theme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + hadproblem = True + return hadproblem diff --git a/powerline/theme.py b/powerline/theme.py index 25fa7e45..9c61ddc7 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -31,13 +31,13 @@ class Theme(object): top_theme_config=None, run_once=False, shutdown_event=None): - self.dividers = theme_config.get('dividers', common_config['dividers']) + self.dividers = theme_config['dividers'] self.dividers = dict(( (key, dict((k, u(v)) for k, v in val.items())) for key, val in self.dividers.items() )) - self.spaces = theme_config.get('spaces', common_config['spaces']) + self.spaces = theme_config['spaces'] self.segments = [] self.EMPTY_SEGMENT = { 'contents': None, diff --git a/tests/test_config_merging.py b/tests/test_config_merging.py index 7941e148..5249a0a3 100644 --- a/tests/test_config_merging.py +++ b/tests/test_config_merging.py @@ -17,17 +17,6 @@ CONFIG_DIR = 'tests/config' root_config = lambda: { 'common': { - 'dividers': { - 'left': { - 'hard': '#>', - 'soft': '|>', - }, - 'right': { - 'hard': '<#', - 'soft': '<|', - }, - }, - 'spaces': 0, 'interval': None, 'watcher': 'auto', }, @@ -76,12 +65,41 @@ theme_config = lambda: { } } +top_theme_config = lambda: { + 'dividers': { + 'left': { + 'hard': '#>', + 'soft': '|>', + }, + 'right': { + 'hard': '<#', + 'soft': '<|', + }, + }, + 'spaces': 0, +} + main_tree = lambda: { '1/config': root_config(), '1/colors': colors_config(), '1/colorschemes/default': colorscheme_config(), '1/themes/test/default': theme_config(), + '1/themes/powerline': top_theme_config(), + '1/themes/other1': mdc(top_theme_config(), { + 'dividers': { + 'left': { + 'hard': '!>', + } + } + }), + '1/themes/other2': mdc(top_theme_config(), { + 'dividers': { + 'left': { + 'hard': '>>', + } + } + }), } @@ -151,11 +169,7 @@ class TestMerging(TestCase): with WithConfigTree(mdc(main_tree(), { '2/config': { 'common': { - 'dividers': { - 'left': { - 'hard': '!>', - } - } + 'default_top_theme': 'other1', } }, })) as p: @@ -163,36 +177,26 @@ class TestMerging(TestCase): with WithConfigTree(mdc(main_tree(), { '2/config': { 'common': { - 'dividers': { - 'left': { - 'hard': '!>', - } - } + 'default_top_theme': 'other1', } }, '3/config': { 'common': { - 'dividers': { - 'left': { - 'hard': '>>', - } - } + 'default_top_theme': 'other2', } }, })) as p: self.assertRenderEqual(p, '{12} bt{2-}>>{--}') + + def test_top_theme_merging(self): with WithConfigTree(mdc(main_tree(), { - '2/config': { - 'common': { - 'spaces': 1, - } + '2/themes/powerline': { + 'spaces': 1, }, - '3/config': { - 'common': { - 'dividers': { - 'left': { - 'hard': '>>', - } + '3/themes/powerline': { + 'dividers': { + 'left': { + 'hard': '>>', } } }, diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index 8deb0d1a..eb7e1cb9 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -12,17 +12,6 @@ from tests.lib.config_mock import get_powerline, add_watcher_events config = { 'config': { 'common': { - 'dividers': { - "left": { - "hard": ">>", - "soft": ">", - }, - "right": { - "hard": "<<", - "soft": "<", - }, - }, - 'spaces': 0, 'interval': 0, 'watcher': 'test', }, @@ -73,6 +62,32 @@ config = { ], }, }, + 'themes/powerline': { + 'dividers': { + "left": { + "hard": ">>", + "soft": ">", + }, + "right": { + "hard": "<<", + "soft": "<", + }, + }, + 'spaces': 0, + }, + 'themes/other': { + 'dividers': { + "left": { + "hard": ">>", + "soft": ">", + }, + "right": { + "hard": "<<", + "soft": "<", + }, + }, + 'spaces': 1, + }, 'themes/test/2': { 'segments': { "left": [ @@ -116,7 +131,7 @@ class TestConfigReload(TestCase): def test_noreload(self, config): with get_powerline(config, run_once=True) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['config']['common']['spaces'] = 1 add_watcher_events(p, 'config', wait=False, interval=0.05) # When running once thread should not start @@ -128,25 +143,30 @@ class TestConfigReload(TestCase): def test_reload_main(self, config): with get_powerline(config, run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') - config['config']['common']['spaces'] = 1 + config['config']['common']['default_top_theme'] = 'other' add_watcher_events(p, 'config') + p.render() self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents(p, 'config') + self.assertAccessEvents(p, 'config', 'themes/other', 'check:themes/test/__main__', 'themes/test/default') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['theme'] = 'nonexistent' add_watcher_events(p, 'config') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents(p, 'config', 'check:themes/test/nonexistent') + self.assertAccessEvents(p, 'config', 'check:themes/test/nonexistent', 'themes/other', 'check:themes/test/__main__') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load theme: themes/test/__main__', + 'exception:test:powerline:Failed to load theme: themes/test/nonexistent', + 'exception:test:powerline:Failed to create renderer: themes/test/nonexistent' + ]) config['config']['ext']['test']['theme'] = 'default' add_watcher_events(p, 'config') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') - self.assertAccessEvents(p, 'config', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'themes/test/default', 'themes/other', 'check:themes/test/__main__') self.assertEqual(p.logger._pop_msgs(), []) config['config']['ext']['test']['colorscheme'] = 'nonexistent' @@ -170,7 +190,7 @@ class TestConfigReload(TestCase): config['config']['ext']['test']['theme'] = '2' add_watcher_events(p, 'config') self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>>') - self.assertAccessEvents(p, 'config', 'themes/test/2') + self.assertAccessEvents(p, 'config', 'themes/test/2', 'themes/other', 'check:themes/test/__main__') self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.renderer.local_themes, None) @@ -185,7 +205,7 @@ class TestConfigReload(TestCase): def test_reload_unexistent(self, config): with get_powerline(config, run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' add_watcher_events(p, 'config') @@ -222,7 +242,7 @@ class TestConfigReload(TestCase): def test_reload_colors(self, config): with get_powerline(config, run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['colors']['colors']['col1'] = 5 add_watcher_events(p, 'colors') @@ -234,7 +254,7 @@ class TestConfigReload(TestCase): def test_reload_colorscheme(self, config): with get_powerline(config, run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' add_watcher_events(p, 'colorschemes/test/default') @@ -246,12 +266,24 @@ class TestConfigReload(TestCase): def test_reload_theme(self, config): with get_powerline(config, run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'themes/test/default') + self.assertAccessEvents(p, 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') + self.assertEqual(p.logger._pop_msgs(), []) + + @with_new_config + def test_reload_top_theme(self, config): + with get_powerline(config, run_once=False) as p: + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') + + config['themes/powerline']['dividers']['left']['hard'] = '|>' + add_watcher_events(p, 'themes/powerline') + self.assertEqual(p.render(), '<1 2 1> s<2 4 False>|><3 4 4>g<4 False False>|>') + self.assertAccessEvents(p, 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') self.assertEqual(p.logger._pop_msgs(), []) @with_new_config @@ -259,12 +291,12 @@ class TestConfigReload(TestCase): config['config']['common']['interval'] = None with get_powerline(config, run_once=False) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default', wait=False) self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'themes/test/default') + self.assertAccessEvents(p, 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') self.assertEqual(p.logger._pop_msgs(), []) self.assertTrue(p._watcher._calls) @@ -273,7 +305,7 @@ class TestConfigReload(TestCase): config['config']['common']['interval'] = None with get_powerline(config, run_once=True) as p: self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') - self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') + self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default', 'themes/powerline', 'check:themes/test/__main__') config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' add_watcher_events(p, 'themes/test/default', wait=False) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index a1d6b836..6a64ce36 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -22,17 +22,6 @@ def highlighted_string(s, group, **kwargs): config = { 'config': { 'common': { - 'dividers': { - 'left': { - 'hard': '>>', - 'soft': '>', - }, - 'right': { - 'hard': '<<', - 'soft': '<', - }, - }, - 'spaces': 0, 'interval': 0, 'watcher': 'test', }, @@ -104,6 +93,26 @@ config = { ], }, }, + 'themes/powerline': { + 'dividers': { + 'left': { + 'hard': '>>', + 'soft': '>', + }, + 'right': { + 'hard': '<<', + 'soft': '<', + }, + }, + 'spaces': 0, + }, + 'themes/test/__main__': { + 'dividers': { + 'right': { + 'soft': '|', + }, + }, + }, 'themes/vim/default': { 'default_module': 'powerline.segments.common', 'segments': { @@ -147,9 +156,9 @@ class TestRender(TestCase): class TestLines(TestRender): @add_args def test_without_above(self, p, config): - self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}') - self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', width=10) - # self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344} f {--}', width=11) + self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}|{344}f {--}') + self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}|{344}f {--}', width=10) + # self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}|{344} f {--}', width=11) self.assertEqual(list(p.render_above_lines()), []) @with_new_config @@ -158,21 +167,21 @@ class TestLines(TestRender): config['themes/test/default']['segments']['above'] = [old_segments] with get_powerline(config, run_once=True, simpler_renderer=True) as p: self.assertRenderLinesEqual(p, [ - '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s{24}>>{344}g{34}>{34}|{344}f {--}', ]) self.assertRenderLinesEqual(p, [ - '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s {24}>>{344}g{34}>{34}|{344}f {--}', ], width=10) config['themes/test/default']['segments']['above'] = [old_segments] * 2 with get_powerline(config, run_once=True, simpler_renderer=True) as p: self.assertRenderLinesEqual(p, [ - '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', - '{121} s{24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s{24}>>{344}g{34}>{34}|{344}f {--}', + '{121} s{24}>>{344}g{34}>{34}|{344}f {--}', ]) self.assertRenderLinesEqual(p, [ - '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', - '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', + '{121} s {24}>>{344}g{34}>{34}|{344}f {--}', + '{121} s {24}>>{344}g{34}>{34}|{344}f {--}', ], width=10) @@ -299,6 +308,82 @@ class TestColorschemesHierarchy(TestRender): self.assertEqual(p.logger._pop_msgs(), []) +class TestThemeHierarchy(TestRender): + @add_args + def test_hierarchy(self, p, config): + self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}|{344}f {--}') + + @add_args + def test_no_main(self, p, config): + del config['themes/test/__main__'] + self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}') + self.assertEqual(p.logger._pop_msgs(), []) + + @add_args + def test_no_powerline(self, p, config): + config['themes/test/__main__']['dividers'] = config['themes/powerline']['dividers'] + config['themes/test/__main__']['spaces'] = 1 + del config['themes/powerline'] + self.assertRenderEqual(p, '{121} s {24}>>{344}g {34}>{34}<{344} f {--}') + self.assertEqual(p.logger._pop_msgs(), []) + + @add_args + def test_no_default(self, p, config): + del config['themes/test/default'] + self.assertRenderEqual(p, 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load theme: themes/test/default', + 'exception:test:powerline:Failed to create renderer: themes/test/default', + 'exception:test:powerline:Failed to render: themes/test/default', + ]) + + @add_args + def test_only_default(self, p, config): + config['themes/test/default']['dividers'] = config['themes/powerline']['dividers'] + config['themes/test/default']['spaces'] = 1 + del config['themes/test/__main__'] + del config['themes/powerline'] + self.assertRenderEqual(p, '{121} s {24}>>{344}g {34}>{34}<{344} f {--}') + + @add_args + def test_only_main(self, p, config): + del config['themes/test/default'] + del config['themes/powerline'] + self.assertRenderEqual(p, 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load theme: themes/powerline', + 'exception:test:powerline:Failed to load theme: themes/test/default', + 'exception:test:powerline:Failed to create renderer: themes/test/default', + 'exception:test:powerline:Failed to render: themes/test/default', + ]) + + @add_args + def test_only_powerline(self, p, config): + del config['themes/test/default'] + del config['themes/test/__main__'] + self.assertRenderEqual(p, 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load theme: themes/test/__main__', + 'exception:test:powerline:Failed to load theme: themes/test/default', + 'exception:test:powerline:Failed to create renderer: themes/test/default', + 'exception:test:powerline:Failed to render: themes/test/default', + ]) + + @add_args + def test_nothing(self, p, config): + del config['themes/test/default'] + del config['themes/powerline'] + del config['themes/test/__main__'] + self.assertRenderEqual(p, 'themes/test/default') + self.assertEqual(p.logger._pop_msgs(), [ + 'exception:test:powerline:Failed to load theme: themes/powerline', + 'exception:test:powerline:Failed to load theme: themes/test/__main__', + 'exception:test:powerline:Failed to load theme: themes/test/default', + 'exception:test:powerline:Failed to create renderer: themes/test/default', + 'exception:test:powerline:Failed to render: themes/test/default', + ]) + + class TestVim(TestCase): def test_environ_update(self): # Regression test: test that segment obtains environment from vim, not From 37b1f967a23178d2d7bd819706ff9bf4f19d02f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:55:01 +0400 Subject: [PATCH 1175/1472] Move all unicode characters from themes to powerline.json --- powerline/config_files/themes/powerline.json | 33 ++++++++++++++++++- .../config_files/themes/shell/__main__.json | 14 ++++++++ .../config_files/themes/shell/default.json | 19 +---------- .../themes/shell/default_leftonly.json | 19 +---------- .../config_files/themes/tmux/default.json | 11 ++----- .../config_files/themes/vim/__main__.json | 10 ++++++ .../config_files/themes/vim/default.json | 15 --------- powerline/config_files/themes/wm/default.json | 7 ++-- 8 files changed, 63 insertions(+), 65 deletions(-) create mode 100644 powerline/config_files/themes/shell/__main__.json create mode 100644 powerline/config_files/themes/vim/__main__.json diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index ea29344e..40cd0d6e 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -9,5 +9,36 @@ "soft": " " } }, - "spaces": 1 + "spaces": 1, + "segment_data": { + "branch": { + "before": " " + }, + + "line_current_symbol": { + "contents": " " + }, + + "powerline.segments.common.uptime": { + "before": "⇑ " + }, + "powerline.segments.common.date": { + "before": "⌚ " + }, + "powerline.segments.common.email_imap_alert": { + "before": "✉ " + }, + "powerline.segments.common.virtualenv": { + "before": "ⓔ " + }, + "powerline.segments.common.hostname": { + "before": " " + }, + + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + } + } } diff --git a/powerline/config_files/themes/shell/__main__.json b/powerline/config_files/themes/shell/__main__.json new file mode 100644 index 00000000..13ae942b --- /dev/null +++ b/powerline/config_files/themes/shell/__main__.json @@ -0,0 +1,14 @@ +{ + "segment_data": { + "hostname": { + "args": { + "only_if_ssh": true + } + }, + "cwd": { + "args": { + "dir_limit_depth": 3 + } + } + } +} diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 35e8e18b..25867a30 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -1,19 +1,5 @@ { "default_module": "powerline.segments.common", - "segment_data": { - "hostname": { - "before": " ", - "args": { - "only_if_ssh": true - } - }, - "virtualenv": { - "before": "ⓔ " - }, - "branch": { - "before": " " - } - }, "segments": { "left": [ { @@ -30,10 +16,7 @@ "name": "virtualenv" }, { - "name": "cwd", - "args": { - "dir_limit_depth": 3 - } + "name": "cwd" }, { "module": "powerline.segments.shell", diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 5060e1ea..d6c49de1 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -1,19 +1,5 @@ { "default_module": "powerline.segments.common", - "segment_data": { - "hostname": { - "before": " ", - "args": { - "only_if_ssh": true - } - }, - "virtualenv": { - "before": "ⓔ " - }, - "branch": { - "before": " " - } - }, "segments": { "left": [ { @@ -29,10 +15,7 @@ "name": "branch" }, { - "name": "cwd", - "args": { - "dir_limit_depth": 3 - } + "name": "cwd" }, { "module": "powerline.segments.shell", diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index ecf6fbb1..479506ae 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -1,13 +1,5 @@ { "default_module": "powerline.segments.common", - "segment_data": { - "uptime": { - "before": "⇑ " - }, - "date": { - "before": "⌚ " - } - }, "segments": { "right": [ { @@ -19,7 +11,8 @@ "priority": 50 }, { - "name": "date" + "name": "date", + "before": "" }, { "name": "date", diff --git a/powerline/config_files/themes/vim/__main__.json b/powerline/config_files/themes/vim/__main__.json new file mode 100644 index 00000000..7cd33055 --- /dev/null +++ b/powerline/config_files/themes/vim/__main__.json @@ -0,0 +1,10 @@ +{ + "segment_data": { + "line_percent": { + "args": { + "gradient": true + }, + "after": "%" + } + } +} diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index bcd65016..3998e187 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -1,19 +1,4 @@ { - "segment_data": { - "branch": { - "before": " " - }, - "modified_indicator": { - "args": { "text": "+" } - }, - "line_percent": { - "args": { "gradient": true }, - "after": "%" - }, - "line_current_symbol": { - "contents": " " - } - }, "segments": { "left": [ { diff --git a/powerline/config_files/themes/wm/default.json b/powerline/config_files/themes/wm/default.json index c1cee4b7..b00699a5 100644 --- a/powerline/config_files/themes/wm/default.json +++ b/powerline/config_files/themes/wm/default.json @@ -7,19 +7,18 @@ "priority": 50 }, { - "name": "date" + "name": "date", + "before": "" }, { "name": "date", "args": { "format": "%H:%M", "istime": true - }, - "before": "⌚ " + } }, { "name": "email_imap_alert", - "before": "✉ ", "priority": 10, "args": { "username": "", From 759b42a823527a5f33bd8a0d8cac71bc8d383d13 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 23:24:16 +0400 Subject: [PATCH 1176/1472] Make now_playing segment accept state symbols from arguments --- powerline/segments/common.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 79b24c52..a0e54492 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -896,29 +896,30 @@ Highlight groups used: ``email_alert_gradient`` (gradient), ``email_alert``. ''') -class NowPlayingSegment(object): - STATE_SYMBOLS = { - 'fallback': '♫', - 'play': '▶', - 'pause': '▮▮', - 'stop': '■', - } +STATE_SYMBOLS = { + 'fallback': '♫', + 'play': '▶', + 'pause': '▮▮', + 'stop': '■', +} - def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', **kwargs): + +class NowPlayingSegment(object): + def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs): player_func = getattr(self, 'player_{0}'.format(player)) stats = { - 'state': None, - 'state_symbol': self.STATE_SYMBOLS['fallback'], + 'state': 'fallback', 'album': None, 'artist': None, 'title': None, 'elapsed': None, 'total': None, } - func_stats = player_func(**kwargs) + func_stats = player_func(state_symbol=state_symbols, **kwargs) if not func_stats: return None stats.update(func_stats) + stats['state_symbol'] = state_symbols.get(stats['state']) return format.format(**stats) @staticmethod @@ -965,7 +966,6 @@ class NowPlayingSegment(object): state = self._convert_state(now_playing.get('status')) return { 'state': state, - 'state_symbol': self.STATE_SYMBOLS.get(state), 'album': now_playing.get('album'), 'artist': now_playing.get('artist'), 'title': now_playing.get('title'), @@ -998,7 +998,6 @@ class NowPlayingSegment(object): client.disconnect() return { 'state': status.get('state'), - 'state_symbol': self.STATE_SYMBOLS.get(status.get('state')), 'album': now_playing.get('album'), 'artist': now_playing.get('artist'), 'title': now_playing.get('title'), @@ -1027,7 +1026,6 @@ class NowPlayingSegment(object): state = self._convert_state(status) return { 'state': state, - 'state_symbol': self.STATE_SYMBOLS.get(state), 'album': info.get('xesam:album'), 'artist': info.get('xesam:artist')[0], 'title': info.get('xesam:title'), @@ -1074,7 +1072,6 @@ class NowPlayingSegment(object): return None return { 'state': state, - 'state_symbol': self.STATE_SYMBOLS.get(state), 'album': spotify_status[1], 'artist': spotify_status[2], 'title': spotify_status[3], From b0093c6b6716d44b6dc8c2cb0488ea874268d6ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 23:43:31 +0400 Subject: [PATCH 1177/1472] =?UTF-8?q?Implement=20single-level=20merging=20?= =?UTF-8?q?of=20=E2=80=9Cargs=E2=80=9D=20dictionaries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/configuration/reference.rst | 11 ++- .../config_files/themes/shell/__main__.json | 2 +- powerline/segment.py | 70 ++++++++++++++----- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 6b899ea7..6722865f 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -256,11 +256,16 @@ common configuration key `. A dict where keys are segment names or strings ``{module}.{name}``. Used to specify default values for various keys: :ref:`after `, - :ref:`args ` (only for function segments), :ref:`before `, :ref:`contents ` (only for string segments if :ref:`name ` is defined), :ref:`display `. + + Key :ref:`args ` (only for function and + segments_list segments) is handled specially: unlike other values it is + merged with all other values, except that a single ``{module}.{name}`` key + if found prevents merging all ``{name}`` values. + When using :ref:`local themes ` values of these keys are first searched in the segment description, then in ``segment_data`` key of a local theme, then in ``segment_data`` key of a :ref:`default theme @@ -289,8 +294,8 @@ common configuration key `. Each segment dictionary has the following options: ``type`` - The segment type. Can be one of ``function`` (default), ``string`` - or ``filler``: + The segment type. Can be one of ``function`` (default), ``string``, + ``filler`` or ``segments_list``: ``function`` The segment contents is the return value of the function defined diff --git a/powerline/config_files/themes/shell/__main__.json b/powerline/config_files/themes/shell/__main__.json index 13ae942b..2b37f8b6 100644 --- a/powerline/config_files/themes/shell/__main__.json +++ b/powerline/config_files/themes/shell/__main__.json @@ -5,7 +5,7 @@ "only_if_ssh": true } }, - "cwd": { + "powerline.segments.common.cwd": { "args": { "dir_limit_depth": 3 } diff --git a/powerline/segment.py b/powerline/segment.py index 9ae5f0fe..8d5a956c 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -6,20 +6,52 @@ from powerline.lib.file_watcher import create_file_watcher import sys -def get_segment_key(segment, theme_configs, key, module=None, default=None): +def list_segment_key_values(segment, theme_configs, key, module=None, default=None): try: - return segment[key] + yield segment[key] except KeyError: - if 'name' in segment: - name = segment['name'] - for theme_config in theme_configs: - if 'segment_data' in theme_config: - for segment_key in ((module + '.' + name, name) if module else (name,)): - try: - return theme_config['segment_data'][segment_key][key] - except KeyError: - pass - return default + pass + try: + name = segment['name'] + except KeyError: + pass + else: + found_module_key = False + for theme_config in theme_configs: + try: + segment_data = theme_config['segment_data'] + except KeyError: + pass + else: + if module: + try: + yield segment_data[module + '.' + name][key] + found_module_key = True + except KeyError: + pass + if not found_module_key: + try: + yield segment_data[name][key] + except KeyError: + pass + yield default + + +def get_segment_key(merge, *args, **kwargs): + if merge: + ret = None + for value in list_segment_key_values(*args, **kwargs): + if ret is None: + ret = value + elif isinstance(ret, dict) and isinstance(value, dict): + old_ret = ret + ret = value.copy() + ret.update(old_ret) + else: + return ret + return ret + else: + return next(list_segment_key_values(*args, **kwargs)) def get_function(data, segment): @@ -33,7 +65,7 @@ def get_function(data, segment): def get_string(data, segment): - return data['get_key'](segment, None, 'contents'), None, None + return data['get_key'](False, segment, None, 'contents'), None, None def get_filler(data, segment): @@ -129,8 +161,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'path': common_config['paths'], } - def get_key(segment, module, key, default=None): - return get_segment_key(segment, theme_configs, key, module, default) + def get_key(merge, segment, module, key, default=None): + return get_segment_key(merge, segment, theme_configs, key, module, default) data['get_key'] = get_key def get(segment, side): @@ -146,7 +178,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') return None - if not get_key(segment, module, 'display', True): + if not get_key(False, segment, module, 'display', True): return None if segment_type == 'function': @@ -155,7 +187,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non highlight_group = segment.get('highlight_group') or segment.get('name') if segment_type in ('function', 'segment_list'): - args = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items())) + args = dict(((str(k), v) for k, v in get_key(True, segment, module, 'args', {}).items())) if segment_type == 'segment_list': # Handle startup and shutdown of _contents_func? @@ -219,8 +251,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non 'type': segment_type, 'highlight_group': highlight_group, 'divider_highlight_group': None, - 'before': get_key(segment, module, 'before', ''), - 'after': get_key(segment, module, 'after', ''), + 'before': get_key(False, segment, module, 'before', ''), + 'after': get_key(False, segment, module, 'after', ''), 'contents_func': contents_func, 'contents': contents, 'priority': segment.get('priority', None), From 416a0efd84e31a0d17cf60ba81515b123b406bd6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 00:00:15 +0400 Subject: [PATCH 1178/1472] Copy a number of unicode values to powerline.json --- powerline/config_files/themes/powerline.json | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index 40cd0d6e..0122a3d7 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -19,6 +19,33 @@ "contents": " " }, + "powerline.segments.common.cwd": { + "args": { + "ellipsis": "⋯" + } + }, + "powerline.segments.common.network_load": { + "args": { + "recv_format": "⬇ {value:>8}", + "sent_format": "⬆ {value:>8}" + } + }, + "powerline.segments.common.now_playing": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + "powerline.segments.common.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥" + } + }, "powerline.segments.common.uptime": { "before": "⇑ " }, @@ -35,6 +62,43 @@ "before": " " }, + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "R": "REPLACE", + "Rv": "V·RPLCE", + "c": "COMMND", + "cv": "VIM EX", + "ce": "EX", + "r": "PROMPT", + "rm": "MORE", + "r?": "CONFIRM", + "!": "SHELL" + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows} × {vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "" + } + }, "powerline.segments.vim.modified_indicator": { "args": { "text": "+" From 0255df2f7b1df623f60d0c223fc7cea6eaedb46e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 00:01:35 +0400 Subject: [PATCH 1179/1472] Allow checking NowPlayingSegment --- powerline/lint/inspect.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 345f1a5c..dc7b0182 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -44,12 +44,22 @@ def getconfigargspec(obj): else: obj = obj - argspec = getargspec(obj) + remove_self = False + try: + argspec = getargspec(obj) + except TypeError: # Workaround for now_playing segment + # TODO For NowPlayingSegment for linter: merge in information about + # player-specific arguments + argspec = getargspec(obj.__call__) + remove_self = True args = [] defaults = [] for i, arg in zip(count(1), reversed(argspec.args)): - if ((arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) or - arg == 'pl'): + if ( + (arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) + or arg == 'pl' + or (arg == 'self' and remove_self) + ): continue if argspec.defaults and len(argspec.defaults) >= i: default = argspec.defaults[-i] From 75d2c62f622ee898934e3193260a8bab99fc70fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandrs=20=C4=BBedovskis?= Date: Tue, 5 Aug 2014 23:52:34 +0300 Subject: [PATCH 1180/1472] Fix weather segment display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most likely a change on GitHub/Yahoo servers made it crash, when non-robot ready result page started to appear inside YQL processor. Using "raw.githubusercontent.com" URL helps and weather icon is again visible. Closes: Lokaltog/powerline#949 Signed-off-by: Aleksandrs Ļedovskis --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 79b24c52..22a76532 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -371,7 +371,7 @@ class WeatherSegment(ThreadedSegment): location_data['country_code']]) query_data = { 'q': - 'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;' + 'use "https://raw.githubusercontent.com/yql/yql-tables/master/weather/weather.bylocation.xml" as we;' 'select * from we where location="{0}" and unit="c"'.format(self.location).encode('utf-8'), 'format': 'json', } From 48254ea657fd3344e7d40e8af1ac1e99dc4d97e7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 01:33:37 +0400 Subject: [PATCH 1181/1472] Use more precise error messages in test_tabline.vim --- tests/test_tabline.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 7289c0ce..3819e74b 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -13,7 +13,7 @@ catch endtry if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' - call writefile(['Unexpected result', result], 'message.fail') + call writefile(['Unexpected tabline', result], 'message.fail') cquit endif @@ -27,7 +27,7 @@ catch endtry if result isnot# '%#Pl_240_5789784_235_2500134_NONE# ./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                               %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' - call writefile(['Unexpected result (2)', result], 'message.fail') + call writefile(['Unexpected tabline (2)', result], 'message.fail') cquit endif From fa5c1e8ce443ad1308917851266235334815acae Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 02:04:58 +0400 Subject: [PATCH 1182/1472] Add ASCII-only theme --- powerline/config_files/themes/ascii.json | 108 +++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 powerline/config_files/themes/ascii.json diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json new file mode 100644 index 00000000..af0df089 --- /dev/null +++ b/powerline/config_files/themes/ascii.json @@ -0,0 +1,108 @@ +{ + "dividers": { + "left": { + "hard": " ", + "soft": "| " + }, + "right": { + "hard": " ", + "soft": " |" + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "BR " + }, + + "line_current_symbol": { + "contents": "LN " + }, + + "powerline.segments.common.cwd": { + "args": { + "ellipsis": "..." + } + }, + "powerline.segments.common.network_load": { + "args": { + "recv_format": "DL {value:>8}", + "sent_format": "UL {value:>8}" + } + }, + "powerline.segments.common.now_playing": { + "args": { + "state_symbols": { + "fallback": "", + "play": ">", + "pause": "~", + "stop": "X" + } + } + }, + "powerline.segments.common.battery": { + "args": { + "full_heart": "O", + "empty_heart": "O" + } + }, + "powerline.segments.common.uptime": { + "before": "UP " + }, + "powerline.segments.common.date": { + "before": "" + }, + "powerline.segments.common.email_imap_alert": { + "before": "MAIL " + }, + "powerline.segments.common.virtualenv": { + "before": "(e) " + }, + "powerline.segments.common.hostname": { + "before": "H " + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N-OPER", + "v": "VISUAL", + "V": "V-LINE", + "^V": "V-BLCK", + "s": "SELECT", + "S": "S-LINE", + "^S": "S-BLCK", + "i": "INSERT", + "R": "REPLACE", + "Rv": "V-RPLCE", + "c": "COMMND", + "cv": "VIM EX", + "ce": "EX", + "r": "PROMPT", + "rm": "MORE", + "r?": "CONFIRM", + "!": "SHELL" + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows} x {vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "RO" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + } + } +} From 625aa243d6e58f3051d899def2773e49cc55c036 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 02:10:29 +0400 Subject: [PATCH 1183/1472] Use ascii theme in local overrides --- tests/test_local_overrides.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index c054df78..5b80d26c 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -1,6 +1,6 @@ #!/usr/bin/vim -S let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' -let g:powerline_config_overrides = {'common': {'dividers': {'left': {'hard': ' ', 'soft': ' > '}, 'right': {'hard': ' ', 'soft': ' < '}}}} +let g:powerline_config_overrides = {'common': {'default_top_theme': 'ascii'}} let g:powerline_theme_overrides__default = {'segment_data': {'line_current_symbol': {'contents': 'LN '}, 'branch': {'before': 'B '}}} try python import powerline.vim From 3c93ac2f4465aa010af9d2a8721a861f85856852 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 02:25:07 +0400 Subject: [PATCH 1184/1472] Add unicode-only theme --- powerline/config_files/themes/unicode.json | 108 +++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 powerline/config_files/themes/unicode.json diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json new file mode 100644 index 00000000..0179f984 --- /dev/null +++ b/powerline/config_files/themes/unicode.json @@ -0,0 +1,108 @@ +{ + "dividers": { + "left": { + "hard": "▌ ", + "soft": "│ " + }, + "right": { + "hard": " ▐", + "soft": " │" + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "⎇ " + }, + + "line_current_symbol": { + "contents": "␤ " + }, + + "powerline.segments.common.cwd": { + "args": { + "ellipsis": "⋯" + } + }, + "powerline.segments.common.network_load": { + "args": { + "recv_format": "⬇ {value:>8}", + "sent_format": "⬆ {value:>8}" + } + }, + "powerline.segments.common.now_playing": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + "powerline.segments.common.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥" + } + }, + "powerline.segments.common.uptime": { + "before": "⇑ " + }, + "powerline.segments.common.date": { + "before": "⌚ " + }, + "powerline.segments.common.email_imap_alert": { + "before": "✉ " + }, + "powerline.segments.common.virtualenv": { + "before": "ⓔ " + }, + "powerline.segments.common.hostname": { + "before": "⌂ " + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "R": "REPLACE", + "Rv": "V·RPLCE", + "c": "COMMND", + "cv": "VIM EX", + "ce": "EX", + "r": "PROMPT", + "rm": "MORE", + "r?": "CONFIRM", + "!": "SHELL" + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows} × {vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "⊗" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + } + } +} From cd576f0d4e40b3f1cd4975d9560d77942d56485d Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 02:47:33 +0400 Subject: [PATCH 1185/1472] Add unicode_terminus top-level theme This one contains only glyphs present in terminus font. --- .../config_files/themes/unicode_terminus.json | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 powerline/config_files/themes/unicode_terminus.json diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json new file mode 100644 index 00000000..c1310fca --- /dev/null +++ b/powerline/config_files/themes/unicode_terminus.json @@ -0,0 +1,108 @@ +{ + "dividers": { + "left": { + "hard": "▌ ", + "soft": "│ " + }, + "right": { + "hard": " ▐", + "soft": " │" + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "BR " + }, + + "line_current_symbol": { + "contents": "␤ " + }, + + "powerline.segments.common.cwd": { + "args": { + "ellipsis": "…" + } + }, + "powerline.segments.common.network_load": { + "args": { + "recv_format": "⇓ {value:>8}", + "sent_format": "⇑ {value:>8}" + } + }, + "powerline.segments.common.now_playing": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + "powerline.segments.common.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥" + } + }, + "powerline.segments.common.uptime": { + "before": "↑ " + }, + "powerline.segments.common.date": { + "before": "" + }, + "powerline.segments.common.email_imap_alert": { + "before": "MAIL " + }, + "powerline.segments.common.virtualenv": { + "before": "(e) " + }, + "powerline.segments.common.hostname": { + "before": "⌂ " + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "R": "REPLACE", + "Rv": "V·RPLCE", + "c": "COMMND", + "cv": "VIM EX", + "ce": "EX", + "r": "PROMPT", + "rm": "MORE", + "r?": "CONFIRM", + "!": "SHELL" + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows} × {vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "RO" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + } + } +} From 2b5734c767d831ac3bfb8e8a58e26a5cb633d484 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 03:03:56 +0400 Subject: [PATCH 1186/1472] Add unicode_terminus_condensed theme --- .../themes/unicode_terminus_condensed.json | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 powerline/config_files/themes/unicode_terminus_condensed.json diff --git a/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline/config_files/themes/unicode_terminus_condensed.json new file mode 100644 index 00000000..e56309ac --- /dev/null +++ b/powerline/config_files/themes/unicode_terminus_condensed.json @@ -0,0 +1,109 @@ +{ + "dividers": { + "left": { + "hard": "▌", + "soft": "│" + }, + "right": { + "hard": "▐", + "soft": "│" + } + }, + "spaces": 0, + "segment_data": { + "branch": { + "before": "B " + }, + + "line_current_symbol": { + "contents": "␤" + }, + + "powerline.segments.common.cwd": { + "args": { + "use_path_separator": true, + "ellipsis": "…" + } + }, + "powerline.segments.common.network_load": { + "args": { + "recv_format": "⇓{value:>8}", + "sent_format": "⇑{value:>8}" + } + }, + "powerline.segments.common.now_playing": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + "powerline.segments.common.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥" + } + }, + "powerline.segments.common.uptime": { + "before": "↑" + }, + "powerline.segments.common.date": { + "before": "" + }, + "powerline.segments.common.email_imap_alert": { + "before": "M " + }, + "powerline.segments.common.virtualenv": { + "before": "E " + }, + "powerline.segments.common.hostname": { + "before": "⌂" + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NML", + "no": "NOP", + "v": "VIS", + "V": "VLN", + "^V": "VBL", + "s": "SEL", + "S": "SLN", + "^S": "SBL", + "i": "INS", + "R": "REP", + "Rv": "VRP", + "c": "CMD", + "cv": "VEX", + "ce": " EX", + "r": "PRT", + "rm": "MOR", + "r?": "CON", + "!": " SH" + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows}×{vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "RO" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + } + } +} From c3e6329262943c1e574a5bcd3f9b56b868efbb97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 03:09:31 +0400 Subject: [PATCH 1187/1472] Add description of shipped themes to documentation --- docs/source/configuration/reference.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 6722865f..dc14209c 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -226,7 +226,18 @@ override those from each previous file. It is required that file `{top_theme}` component of the file name is obtained either from :ref:`top_theme extension-specific key ` or from :ref:`default_top_theme -common configuration key `. +common configuration key `. Powerline ships +with the following top themes: + +========================== ==================================================== +Theme Description +========================== ==================================================== +powerline Default powerline theme with fancy powerline symbols +unicode Theme without any symbols from private use area +unicode_terminus Theme containing only symbols from terminus PCF font +unicode_terminus_condensed Like above, but occupies as less space as possible +ascii Theme without any unicode characters at all +========================== ==================================================== ``name`` Name of the theme. From d4735c87df9339392ec37870dd49496f35d8e1b5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 12:48:17 +0400 Subject: [PATCH 1188/1472] Fix labels in configuration/reference.rst --- docs/source/configuration/reference.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index dc14209c..f8e7f148 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -413,11 +413,12 @@ ascii Theme without any unicode characters at all *not* included in any modes, *except* for the modes in this list. ``display`` - .. _config-themes-seg-display: Boolean. If false disables displaying of the segment. Defaults to ``True``. ``segments`` + .. _config-themes-seg-segments: + A list of subsegments. From 1a87006310d342c807b79204c9f7484850755c8f Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 12:52:52 +0400 Subject: [PATCH 1189/1472] Refactor powerline.lint.inspect: do not special-case *ThreadedSegment Fixes now_playing segment handling in python-3.4 --- powerline/lib/threaded.py | 34 ++++++++++- powerline/lint/inspect.py | 101 +++++++++++++++------------------ powerline/segment.py | 4 +- powerline/segments/__init__.py | 49 ++++++++++++++++ powerline/segments/common.py | 3 +- 5 files changed, 130 insertions(+), 61 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index b253ae8f..acb258c3 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -2,9 +2,11 @@ from __future__ import absolute_import -from powerline.lib.monotonic import monotonic - from threading import Thread, Lock, Event +from types import MethodType + +from powerline.lib.monotonic import monotonic +from powerline.segments import Segment class MultiRunnedThread(object): @@ -28,12 +30,14 @@ class MultiRunnedThread(object): return None -class ThreadedSegment(MultiRunnedThread): +class ThreadedSegment(Segment, MultiRunnedThread): min_sleep_time = 0.1 update_first = True interval = 1 daemon = False + argmethods = ('render', 'set_state') + def __init__(self): super(ThreadedSegment, self).__init__() self.run_once = True @@ -145,10 +149,34 @@ class ThreadedSegment(MultiRunnedThread): def debug(self, *args, **kwargs): self.pl.debug(prefix=self.__class__.__name__, *args, **kwargs) + def argspecobjs(self): + for name in self.argmethods: + try: + yield name, getattr(self, name) + except AttributeError: + pass + + def additional_args(self): + return (('interval', self.interval),) + + def omitted_args(self, name, method): + if isinstance(getattr(self, name, None), MethodType): + omitted_indexes = (0,) + else: + omitted_indexes = () + if name.startswith('render'): + if omitted_indexes: + omitted_indexes += (1,) + else: + omitted_indexes = (0,) + return omitted_indexes + class KwThreadedSegment(ThreadedSegment): update_first = True + argmethods = ('render', 'set_state', 'key', 'render_one') + def __init__(self): super(KwThreadedSegment, self).__init__() self.updated = True diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index dc7b0182..58d0dc05 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -1,72 +1,63 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import + from inspect import ArgSpec, getargspec -from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from itertools import count +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from powerline.segments import Segment + def getconfigargspec(obj): - if isinstance(obj, ThreadedSegment): - args = ['interval'] - defaults = [getattr(obj, 'interval', 1)] - if obj.update_first: - args.append('update_first') - defaults.append(True) - methods = ['render', 'set_state'] - if isinstance(obj, KwThreadedSegment): - methods += ['key', 'render_one'] - - for method in methods: - if hasattr(obj, method): - # Note: on = i: - default = argspec.defaults[-i] - defaults.append(default) - args.append(arg) - else: - args.insert(0, arg) - argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) + if hasattr(obj, 'powerline_origin'): + obj = obj.powerline_origin else: - if hasattr(obj, 'powerline_origin'): - obj = obj.powerline_origin - else: - obj = obj + obj = obj - remove_self = False - try: - argspec = getargspec(obj) - except TypeError: # Workaround for now_playing segment - # TODO For NowPlayingSegment for linter: merge in information about - # player-specific arguments - argspec = getargspec(obj.__call__) - remove_self = True - args = [] - defaults = [] - for i, arg in zip(count(1), reversed(argspec.args)): + args = [] + defaults = [] + + if isinstance(obj, Segment): + additional_args = obj.additional_args() + argspecobjs = obj.argspecobjs() + get_omitted_args = obj.omitted_args + else: + additional_args = () + argspecobjs = ((None, obj),) + get_omitted_args = lambda *args: () + + for arg in additional_args: + args.append(arg[0]) + if len(arg) > 1: + defaults.append(arg[1]) + + requires_segment_info = getattr(obj, 'powerline_requires_segment_info', False) + requires_filesystem_watcher = getattr(obj, 'powerline_requires_filesystem_watcher', False) + + for name, method in argspecobjs: + argspec = getargspec(method) + omitted_args = get_omitted_args(name, method) + largs = len(argspec.args) + for i, arg in enumerate(reversed(argspec.args)): if ( - (arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) + largs - (i + 1) in omitted_args or arg == 'pl' - or (arg == 'self' and remove_self) + or (arg == 'create_watcher' and requires_filesystem_watcher) + or (arg == 'segment_info' and requires_segment_info) ): continue - if argspec.defaults and len(argspec.defaults) >= i: - default = argspec.defaults[-i] + if argspec.defaults and len(argspec.defaults) > i: + if arg in args: + idx = args.index(arg) + if len(args) - idx > len(defaults): + args.pop(idx) + else: + continue + default = argspec.defaults[-(i + 1)] defaults.append(default) args.append(arg) else: - args.insert(0, arg) - argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults)) + if arg not in args: + args.insert(0, arg) - return argspec + return ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) diff --git a/powerline/segment.py b/powerline/segment.py index 8d5a956c..97538813 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,10 +1,10 @@ # vim:fileencoding=utf-8:noet - from __future__ import absolute_import, unicode_literals, division, print_function -from powerline.lib.file_watcher import create_file_watcher import sys +from powerline.lib.file_watcher import create_file_watcher + def list_segment_key_values(segment, theme_configs, key, module=None, default=None): try: diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py index 3ad9513f..3c2da39c 100644 --- a/powerline/segments/__init__.py +++ b/powerline/segments/__init__.py @@ -1,2 +1,51 @@ +# vim:fileencoding=utf-8:noet +from __future__ import absolute_import + +import sys + from pkgutil import extend_path +from types import MethodType + + __path__ = extend_path(__path__, __name__) + + +class Segment(object): + '''Base class for any segment that is not a function + + Required for powerline.lint.inspect to work properly. + ''' + if sys.version_info < (3, 4): + def argspecobjs(self): + yield '__call__', self.__call__ + else: + def argspecobjs(self): # NOQA + yield '__call__', self + + argspecobjs.__doc__ = ( + '''Return a list of valid arguments for inspect.getargspec + + Used to determine function arguments. + ''' + ) + + def omitted_args(self, name, method): + '''List arguments which should be omitted + + Returns a tuple with indexes of omitted arguments. + + .. note::``segment_info``, ``create_watcher`` and ``pl`` will be omitted + regardless of the below return (for ``segment_info`` and + ``create_watcher``: only if object was marked to require segment + info or filesystem watcher). + ''' + if isinstance(self.__call__, MethodType): + return (0,) + else: + return () + + @staticmethod + def additional_args(): + '''Returns a list of (additional argument name[, default value]) tuples. + ''' + return () diff --git a/powerline/segments/common.py b/powerline/segments/common.py index a0e54492..365a92da 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -19,6 +19,7 @@ from powerline.lib.monotonic import monotonic from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.unicode import u from powerline.theme import requires_segment_info, requires_filesystem_watcher +from powerline.segments import Segment from collections import namedtuple @@ -904,7 +905,7 @@ STATE_SYMBOLS = { } -class NowPlayingSegment(object): +class NowPlayingSegment(Segment): def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs): player_func = getattr(self, 'player_{0}'.format(player)) stats = { From 85ea44b1ee9908b1e7c3e29704e607818c746fa8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 13:38:45 +0400 Subject: [PATCH 1190/1472] Do not output \x01/\x02 symbols for non-prompts in ipython Should fix #664 --- powerline/bindings/ipython/post_0_11.py | 34 +++++++++++++++++-------- powerline/bindings/ipython/pre_0_11.py | 25 ++++++++++-------- powerline/ipython.py | 8 ++++-- powerline/renderers/ipython.py | 3 --- powerline/renderers/ipython_prompt.py | 12 +++++++++ 5 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 powerline/renderers/ipython_prompt.py diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index f91a5aed..1a364e5c 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -15,28 +15,36 @@ class IpythonInfo(object): class PowerlinePromptManager(PromptManager): - powerline = None - - def __init__(self, powerline, shell): - self.powerline = powerline + def __init__(self, prompt_powerline, non_prompt_powerline, shell): + self.prompt_powerline = prompt_powerline + self.non_prompt_powerline = non_prompt_powerline self.powerline_segment_info = IpythonInfo(shell) self.shell = shell def render(self, name, color=True, *args, **kwargs): width = None if name == 'in' else self.width - res, res_nocolor = self.powerline.render(output_raw=True, width=width, matcher_info=name, segment_info=self.powerline_segment_info) + if name == 'out' or name == 'rewrite': + powerline = self.non_prompt_powerline + else: + powerline = self.prompt_powerline + res, res_nocolor = powerline.render( + output_raw=True, + width=width, + matcher_info=name, + segment_info=self.powerline_segment_info, + ) self.txtwidth = len(res_nocolor) self.width = self.txtwidth return res if color else res_nocolor class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, ip): + def __init__(self, ip, is_prompt): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) self.path = config.get('path') - super(ConfigurableIpythonPowerline, self).__init__() + super(ConfigurableIpythonPowerline, self).__init__(is_prompt) old_prompt_manager = None @@ -46,12 +54,18 @@ def load_ipython_extension(ip): global old_prompt_manager old_prompt_manager = ip.prompt_manager - powerline = ConfigurableIpythonPowerline(ip) + prompt_powerline = ConfigurableIpythonPowerline(ip, True) + non_prompt_powerline = ConfigurableIpythonPowerline(ip, False) - ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell) + ip.prompt_manager = PowerlinePromptManager( + prompt_powerline=prompt_powerline, + non_prompt_powerline=non_prompt_powerline, + shell=ip.prompt_manager.shell + ) def shutdown_hook(): - powerline.shutdown() + prompt_powerline.shutdown() + non_prompt_powerline.shutdown() raise TryNext() ip.hooks.shutdown_hook.add(shutdown_hook) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 628c39bc..26356c5d 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -41,8 +41,9 @@ class IpythonInfo(object): class PowerlinePrompt(BasePrompt): - def __init__(self, powerline, powerline_last_in, old_prompt): + def __init__(self, powerline, other_powerline, powerline_last_in, old_prompt): self.powerline = powerline + self.other_powerline = other_powerline self.powerline_last_in = powerline_last_in self.powerline_segment_info = IpythonInfo(old_prompt.cache) self.cache = old_prompt.cache @@ -85,7 +86,7 @@ class PowerlinePrompt1(PowerlinePrompt): self.powerline_last_in['prompt_text_len'] = self.prompt_text_len def auto_rewrite(self): - return RewriteResult(self.powerline.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) + return RewriteResult(self.other_powerline.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) + (' ' * self.nrspaces)) @@ -104,31 +105,33 @@ class PowerlinePrompt2(PowerlinePromptOut): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, config_overrides=None, theme_overrides={}, path=None): + def __init__(self, is_prompt, config_overrides=None, theme_overrides={}, path=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides self.path = path - super(ConfigurableIpythonPowerline, self).__init__() + super(ConfigurableIpythonPowerline, self).__init__(is_prompt) def setup(**kwargs): ip = get_ipython() - powerline = ConfigurableIpythonPowerline(**kwargs) + prompt_powerline = ConfigurableIpythonPowerline(True, **kwargs) + non_prompt_powerline = ConfigurableIpythonPowerline(False, **kwargs) def late_startup_hook(): last_in = {'nrspaces': 0, 'prompt_text_len': None} - for attr, prompt_class in ( - ('prompt1', PowerlinePrompt1), - ('prompt2', PowerlinePrompt2), - ('prompt_out', PowerlinePromptOut) + for attr, prompt_class, powerline, other_powerline in ( + ('prompt1', PowerlinePrompt1, prompt_powerline, non_prompt_powerline), + ('prompt2', PowerlinePrompt2, prompt_powerline, None), + ('prompt_out', PowerlinePromptOut, non_prompt_powerline, None) ): old_prompt = getattr(ip.IP.outputcache, attr) - setattr(ip.IP.outputcache, attr, prompt_class(powerline, last_in, old_prompt)) + setattr(ip.IP.outputcache, attr, prompt_class(powerline, other_powerline, last_in, old_prompt)) raise TryNext() def shutdown_hook(): - powerline.shutdown() + prompt_powerline.shutdown() + non_prompt_powerline.shutdown() raise TryNext() ip.IP.hooks.late_startup_hook.add(late_startup_hook) diff --git a/powerline/ipython.py b/powerline/ipython.py index bed41514..51cc4881 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -5,8 +5,12 @@ from powerline.lib import mergedicts class IpythonPowerline(Powerline): - def __init__(self): - super(IpythonPowerline, self).__init__('ipython', use_daemon_threads=True) + def __init__(self, is_prompt): + super(IpythonPowerline, self).__init__( + 'ipython', + renderer_module=('ipython_prompt' if is_prompt else 'ipython'), + use_daemon_threads=True + ) def get_config_paths(self): if self.path: diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index 69f38eaa..a2c071cc 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -6,9 +6,6 @@ from powerline.theme import Theme class IpythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' - escape_hl_start = '\x01' - escape_hl_end = '\x02' - def get_segment_info(self, segment_info, mode): r = self.segment_info.copy() r['ipython'] = segment_info diff --git a/powerline/renderers/ipython_prompt.py b/powerline/renderers/ipython_prompt.py new file mode 100644 index 00000000..a1103ec8 --- /dev/null +++ b/powerline/renderers/ipython_prompt.py @@ -0,0 +1,12 @@ +# vim:fileencoding=utf-8:noet + +from powerline.renderers.ipython import IpythonRenderer + + +class IpythonPromptRenderer(IpythonRenderer): + '''Powerline ipython prompt renderer''' + escape_hl_start = '\x01' + escape_hl_end = '\x02' + + +renderer = IpythonPromptRenderer From ed70cc2eb21c79bdadfdbc2e0b94880d7837d810 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 13:47:11 +0400 Subject: [PATCH 1191/1472] Fix tests --- tests/test_provided_config_files.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index a6371588..90dc3d4e 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -116,9 +116,14 @@ class TestConfig(TestCase): config_overrides = None theme_overrides = {} - with IpyPowerline() as powerline: - segment_info = Args(prompt_count=1) - for prompt_type in ['in', 'in2', 'out', 'rewrite']: + segment_info = Args(prompt_count=1) + + with IpyPowerline(True) as powerline: + for prompt_type in ['in', 'in2']: + powerline.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.render(matcher_info=prompt_type, segment_info=segment_info) + with IpyPowerline(False) as powerline: + for prompt_type in ['out', 'rewrite']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) From ea6e28a037f69f04596530f0ce60f368461ce069 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 16:56:20 +0400 Subject: [PATCH 1192/1472] Use the same hack for post_0_11 rewrite prompt IPython again tries to leave us without unicode in rewrite prompt. But in Python, one needs to be more serious than that to be actually able to do this. --- powerline/bindings/ipython/post_0_11.py | 8 ++++++-- powerline/bindings/ipython/pre_0_11.py | 27 ++----------------------- powerline/ipython.py | 18 +++++++++++++++++ powerline/lib/unicode.py | 7 +++++++ 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 1a364e5c..dc6c8112 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from powerline.ipython import IpythonPowerline +from powerline.ipython import IpythonPowerline, RewriteResult from IPython.core.prompts import PromptManager from IPython.core.hooks import TryNext @@ -35,7 +35,11 @@ class PowerlinePromptManager(PromptManager): ) self.txtwidth = len(res_nocolor) self.width = self.txtwidth - return res if color else res_nocolor + ret = res if color else res_nocolor + if name == 'rewrite': + return RewriteResult(ret) + else: + return ret class ConfigurableIpythonPowerline(IpythonPowerline): diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 26356c5d..8d7f0503 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,5 +1,6 @@ # vim:fileencoding=utf-8:noet -from powerline.ipython import IpythonPowerline +from powerline.ipython import IpythonPowerline, RewriteResult +from powerline.lib.unicode import string from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython from IPython.ipapi import TryNext @@ -7,30 +8,6 @@ from IPython.ipapi import TryNext import re -def string(s): - if type(s) is not str: - return s.encode('utf-8') - else: - return s - - -# HACK: ipython tries to only leave us with plain ASCII -class RewriteResult(object): - def __init__(self, prompt): - self.prompt = string(prompt) - - def __str__(self): - return self.prompt - - def __add__(self, s): - if type(s) is not str: - try: - s = s.encode('utf-8') - except AttributeError: - raise NotImplementedError - return RewriteResult(self.prompt + s) - - class IpythonInfo(object): def __init__(self, cache): self._cache = cache diff --git a/powerline/ipython.py b/powerline/ipython.py index 51cc4881..6716e075 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -2,6 +2,24 @@ from powerline import Powerline from powerline.lib import mergedicts +from powerline.lib.unicode import string + + +# HACK: ipython tries to only leave us with plain ASCII +class RewriteResult(object): + def __init__(self, prompt): + self.prompt = string(prompt) + + def __str__(self): + return self.prompt + + def __add__(self, s): + if type(s) is not str: + try: + s = s.encode('utf-8') + except AttributeError: + raise NotImplementedError + return RewriteResult(self.prompt + s) class IpythonPowerline(Powerline): diff --git a/powerline/lib/unicode.py b/powerline/lib/unicode.py index a1a5d567..34505e9a 100644 --- a/powerline/lib/unicode.py +++ b/powerline/lib/unicode.py @@ -56,3 +56,10 @@ class FailedUnicode(unicode): FailedUnicode. ''' pass + + +def string(s): + if type(s) is not str: + return s.encode('utf-8') + else: + return s From c403eef4340f4014305eb7b4cc6521f0abcbcd97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 17:18:42 +0400 Subject: [PATCH 1193/1472] Add ipython shell tests --- tests/install.sh | 2 +- tests/test_shells/input.ipython | 7 ++ tests/test_shells/ipython.ok | 14 +++ .../profile_default/ipython_config.py | 3 + tests/test_shells/postproc.py | 10 +- tests/test_shells/test.sh | 100 ++++++++++-------- 6 files changed, 86 insertions(+), 50 deletions(-) create mode 100644 tests/test_shells/input.ipython create mode 100644 tests/test_shells/ipython.ok create mode 100644 tests/test_shells/ipython_home/profile_default/ipython_config.py diff --git a/tests/install.sh b/tests/install.sh index 1f1a3729..573a5beb 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -12,7 +12,7 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -sudo apt-get install -qq screen zsh tcsh mksh busybox +sudo apt-get install -qq screen zsh tcsh mksh busybox ipython # Travis has too outdated fish. It cannot be used for tests. # sudo apt-get install fish true diff --git a/tests/test_shells/input.ipython b/tests/test_shells/input.ipython new file mode 100644 index 00000000..23b80198 --- /dev/null +++ b/tests/test_shells/input.ipython @@ -0,0 +1,7 @@ +print ('cd ' + 'tests/shell/3rd') # Start of the test marker +bool 42 +bool 44 +class Test(object): +pass + +exit diff --git a/tests/test_shells/ipython.ok b/tests/test_shells/ipython.ok new file mode 100644 index 00000000..85bd82a4 --- /dev/null +++ b/tests/test_shells/ipython.ok @@ -0,0 +1,14 @@ + + In [2]  bool 42 +     2>  bool(42) + Out[2]  True + + In [3]  bool 44 +     3>  bool(44) + Out[3]  True + + In [4]  class Test(object): +          pass +          + + In [5]  exit diff --git a/tests/test_shells/ipython_home/profile_default/ipython_config.py b/tests/test_shells/ipython_home/profile_default/ipython_config.py new file mode 100644 index 00000000..babd9e79 --- /dev/null +++ b/tests/test_shells/ipython_home/profile_default/ipython_config.py @@ -0,0 +1,3 @@ +c = get_config() +c.InteractiveShellApp.extensions = ['powerline.bindings.ipython.post_0_11'] +c.TerminalInteractiveShell.autocall = 1 diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index f34fa81c..dd12f829 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -15,8 +15,11 @@ new_fname = os.path.join('tests', 'shell', shell + '.' + test_type + '.log') pid_fname = os.path.join('tests', 'shell', '3rd', 'pid') -with open(pid_fname, 'r') as P: - pid = P.read().strip() +try: + with open(pid_fname, 'r') as P: + pid = P.read().strip() +except IOError: + pid = None hostname = socket.gethostname() user = os.environ['USER'] @@ -34,7 +37,8 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: }) line = line.replace(hostname, 'HOSTNAME') line = line.replace(user, 'USER') - line = line.replace(pid, 'PID') + if pid is not None: + line = line.replace(pid, 'PID') if shell == 'fish': try: start = line.index('\033[0;') diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index f224e22f..3977d77f 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -120,60 +120,68 @@ mkdir tests/shell/3rd/'$(echo)' mkdir tests/shell/3rd/'`echo`' mkdir tests/shell/fish_home +cp -r tests/test_shells/ipython_home tests/shell export XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" +export IPYTHONDIR="$PWD/tests/shell/ipython_home" unset ENV -powerline-daemon -k || true -sleep 1s +if test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbb ; then + powerline-daemon -k || true + sleep 1s -scripts/powerline-config shell command + scripts/powerline-config shell command -for TEST_TYPE in "daemon" "nodaemon" ; do - if test $TEST_TYPE == daemon ; then - sh -c 'echo $$ > tests/shell/daemon_pid; ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & - fi - if ! run_test $TEST_TYPE bash --norc --noprofile -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE zsh -f -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE fish -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE tcsh -f -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE bb -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE mksh -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE dash -i ; then - # dash tests are not stable, see #931 - # FAILED=1 - true - fi - if test $TEST_TYPE == daemon ; then - ./scripts/powerline-daemon -k - wait $(cat tests/shell/daemon_pid) - if ! test -z "$(cat tests/shell/daemon_log)" ; then - echo '____________________________________________________________' - echo "Daemon log:" - echo '============================================================' - cat tests/shell/daemon_log + for TEST_TYPE in "daemon" "nodaemon" ; do + if test $TEST_TYPE == daemon ; then + sh -c 'echo $$ > tests/shell/daemon_pid; ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & + fi + if ! run_test $TEST_TYPE bash --norc --noprofile -i ; then FAILED=1 fi - fi -done + + if ! run_test $TEST_TYPE zsh -f -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE fish -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE tcsh -f -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE bb -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE mksh -i ; then + FAILED=1 + fi + + if ! run_test $TEST_TYPE dash -i ; then + # dash tests are not stable, see #931 + # FAILED=1 + true + fi + if test $TEST_TYPE == daemon ; then + ./scripts/powerline-daemon -k + wait $(cat tests/shell/daemon_pid) + if ! test -z "$(cat tests/shell/daemon_log)" ; then + echo '____________________________________________________________' + echo "Daemon log:" + echo '============================================================' + cat tests/shell/daemon_log + FAILED=1 + fi + fi + done +fi + +if ! run_test ipython ipython ; then + FAILED=1 +fi test "x$ONLY_SHELL" = "x" && rm -r tests/shell exit $FAILED From 3e43995d2ce2dd5a757daae02bdd6dda9f559e93 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 17:21:01 +0400 Subject: [PATCH 1194/1472] Use pip to install ipython, not apt-get I am very unsure that it will install ipython for all required python versions --- tests/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/install.sh b/tests/install.sh index 573a5beb..24fdd09b 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -1,6 +1,6 @@ #!/bin/sh pip install . -pip install psutil +pip install psutil ipython if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 if python -c 'import platform, sys; sys.exit(1 - (platform.python_implementation() == "CPython"))' ; then @@ -12,7 +12,7 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then pip install unittest2 argparse fi fi -sudo apt-get install -qq screen zsh tcsh mksh busybox ipython +sudo apt-get install -qq screen zsh tcsh mksh busybox # Travis has too outdated fish. It cannot be used for tests. # sudo apt-get install fish true From accb174b8ba855556b00b0f5ff4ae1f207928ba7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 18:06:54 +0400 Subject: [PATCH 1195/1472] Do not install ipython when using python-2.6 --- tests/install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/install.sh b/tests/install.sh index 24fdd09b..f40b99ee 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -1,15 +1,19 @@ #!/bin/sh pip install . -pip install psutil ipython +pip install psutil if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 if python -c 'import platform, sys; sys.exit(1 - (platform.python_implementation() == "CPython"))' ; then + # PyPy pip install mercurial pip install --allow-external bzr --allow-unverified bzr bzr fi if python -c 'import sys; sys.exit(1 * (sys.version_info[1] >= 7))' ; then # Python 2.6 pip install unittest2 argparse + else + # Python 2.7 + pip install ipython fi fi sudo apt-get install -qq screen zsh tcsh mksh busybox From 863264cd9945cc033c657baf048eb2959005dcb9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 18:09:57 +0400 Subject: [PATCH 1196/1472] Disable virtualenv segment, also test config_overrides --- tests/test_shells/ipython.ok | 20 +++++++++---------- .../profile_default/ipython_config.py | 14 +++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/tests/test_shells/ipython.ok b/tests/test_shells/ipython.ok index 85bd82a4..2ccd6e14 100644 --- a/tests/test_shells/ipython.ok +++ b/tests/test_shells/ipython.ok @@ -1,14 +1,14 @@ - In [2]  bool 42 -     2>  bool(42) - Out[2]  True + In [2]  bool 42 +     2>  bool(42) + Out[2]  True - In [3]  bool 44 -     3>  bool(44) - Out[3]  True + In [3]  bool 44 +     3>  bool(44) + Out[3]  True - In [4]  class Test(object): -          pass -          + In [4]  class Test(object): +          pass +          - In [5]  exit + In [5]  exit diff --git a/tests/test_shells/ipython_home/profile_default/ipython_config.py b/tests/test_shells/ipython_home/profile_default/ipython_config.py index babd9e79..658334f1 100644 --- a/tests/test_shells/ipython_home/profile_default/ipython_config.py +++ b/tests/test_shells/ipython_home/profile_default/ipython_config.py @@ -1,3 +1,17 @@ c = get_config() c.InteractiveShellApp.extensions = ['powerline.bindings.ipython.post_0_11'] c.TerminalInteractiveShell.autocall = 1 +c.Powerline.theme_overrides = { + 'in': { + 'segment_data': { + 'virtualenv': { + 'display': False + } + } + } +} +c.Powerline.config_overrides = { + 'common': { + 'default_top_theme': 'ascii' + } +} From c3e2358931c16451c8064629c742985a0145a2e6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 18:12:59 +0400 Subject: [PATCH 1197/1472] Also install ipython on Python-3.3+ --- tests/install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/install.sh b/tests/install.sh index f40b99ee..fcfd0b73 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -15,6 +15,12 @@ if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2.7 pip install ipython fi +else + # Python 3 + if python -c 'import sys; sys.exit(1 * (sys.version_info < (3, 3)))' ; then + # Python 3.3+ + pip install ipython + fi fi sudo apt-get install -qq screen zsh tcsh mksh busybox # Travis has too outdated fish. It cannot be used for tests. From ae2ac05cedecca553f8f8c19c69bab6b0eebafa6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 22:04:15 +0400 Subject: [PATCH 1198/1472] Handle errors from write() Fixes #964 --- client/powerline.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/client/powerline.c b/client/powerline.c index fd08c066..97f433c3 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -120,7 +120,30 @@ int main(int argc, char *argv[]) { close(sd); HANDLE_ERROR("read() failed"); } else if (i > 0) { - (void) write(STDOUT_FILENO, buf, (size_t) i); + ptrdiff_t written = 0; + const char *bufp = &(buf[0]); + while (written < i) { + written = write(STDOUT_FILENO, buf, (size_t) i); + if (written >= 0) { + bufp += written; + i -= written; + written = 0; + } else { + errno = 0; + switch (errno) { + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + case EINTR: + break; + default: + (void) printf("Error while writing output: %s", strerror(errno)); + close(sd); + return 2; + } + } + } } } From 0a6bb9ce083ad48c724c5f0395f2f786c988d3c8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 22:31:16 +0400 Subject: [PATCH 1199/1472] Some style fixes --- client/powerline.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/client/powerline.c b/client/powerline.c index 97f433c3..6e8c8df1 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -13,14 +13,15 @@ #define HANDLE_ERROR(msg) \ do { \ - perror(msg); exit(EXIT_FAILURE); \ + perror(msg); \ + exit(EXIT_FAILURE); \ } while (0) #define TEMP_FAILURE_RETRY(var, expression) \ do { \ - long int __result; \ + ptrdiff_t __result; \ do { \ - __result = (long int) (expression); \ + __result = (expression); \ } while (__result == -1L && errno == EINTR); \ var = __result; \ } while (0) @@ -56,7 +57,8 @@ void do_write(int sd, const char *raw, size_t len) { int main(int argc, char *argv[]) { int sd = -1; - ptrdiff_t i; + int i; + ptrdiff_t read_size; struct sockaddr_un server; char address[ADDRESS_SIZE]; const char eof[2] = "\0\0"; @@ -67,7 +69,8 @@ int main(int argc, char *argv[]) { char **envp; if (argc < 2) { - printf("Must provide at least one argument.\n"); return EXIT_FAILURE; + printf("Must provide at least one argument.\n"); + return EXIT_FAILURE; } snprintf(address, ADDRESS_SIZE, ADDRESS_TEMPLATE, getuid()); @@ -113,20 +116,20 @@ int main(int argc, char *argv[]) { do_write(sd, eof, 2); - i = -1; - while (i != 0) { - TEMP_FAILURE_RETRY(i, read(sd, buf, BUF_SIZE)); - if (i == -1) { + read_size = -1; + while (read_size != 0) { + TEMP_FAILURE_RETRY(read_size, read(sd, buf, BUF_SIZE)); + if (read_size == -1) { close(sd); HANDLE_ERROR("read() failed"); - } else if (i > 0) { + } else if (read_size > 0) { ptrdiff_t written = 0; const char *bufp = &(buf[0]); - while (written < i) { - written = write(STDOUT_FILENO, buf, (size_t) i); + while (written < read_size) { + written = write(STDOUT_FILENO, buf, (size_t) read_size); if (written >= 0) { bufp += written; - i -= written; + read_size -= written; written = 0; } else { errno = 0; From 660925f0c2ca80abda0f3880cd93a762e458833c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 6 Aug 2014 22:32:39 +0400 Subject: [PATCH 1200/1472] Use do_write function in place of self-written replacement --- client/powerline.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/client/powerline.c b/client/powerline.c index 6e8c8df1..0800f701 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -123,30 +123,7 @@ int main(int argc, char *argv[]) { close(sd); HANDLE_ERROR("read() failed"); } else if (read_size > 0) { - ptrdiff_t written = 0; - const char *bufp = &(buf[0]); - while (written < read_size) { - written = write(STDOUT_FILENO, buf, (size_t) read_size); - if (written >= 0) { - bufp += written; - read_size -= written; - written = 0; - } else { - errno = 0; - switch (errno) { - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - case EINTR: - break; - default: - (void) printf("Error while writing output: %s", strerror(errno)); - close(sd); - return 2; - } - } - } + do_write(STDOUT_FILENO, buf, (size_t) read_size); } } From 91826e4483e6e4132ef947fbc4574d83856d3bc1 Mon Sep 17 00:00:00 2001 From: Mike Reinhardt Date: Thu, 7 Aug 2014 13:00:45 -0700 Subject: [PATCH 1201/1472] Fix NowPlaying segment when comma in artist/title --- powerline/segments/common.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index c32f01e7..7587c063 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1035,6 +1035,7 @@ class NowPlayingSegment(Segment): } def player_spotify_apple_script(self, pl): + status_delimiter = '-~`/=' ascript = ''' tell application "System Events" set process_list to (name of every process) @@ -1047,13 +1048,8 @@ class NowPlayingSegment(Segment): set artist_name to artist of current track set album_name to album of current track set track_length to duration of current track - set trim_length to 40 - set now_playing to player state & album_name & artist_name & track_name & track_length - if length of now_playing is less than trim_length then - set now_playing_trim to now_playing - else - set now_playing_trim to characters 1 thru trim_length of now_playing as string - end if + set now_playing to "" & player state & "{0}" & album_name & "{0}" & artist_name & "{0}" & track_name & "{0}" & track_length + return now_playing else return player state end if @@ -1062,13 +1058,13 @@ class NowPlayingSegment(Segment): else return "stopped" end if - ''' + '''.format(status_delimiter) spotify = asrun(pl, ascript) if not asrun: return None - spotify_status = spotify.split(", ") + spotify_status = spotify.split(status_delimiter) state = self._convert_state(spotify_status[0]) if state == 'stop': return None From f89744209f44de6207be8c0c2caf9b992fc7fd95 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Aug 2014 12:13:00 +0400 Subject: [PATCH 1202/1472] Improve linter coding style --- powerline/lint/__init__.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 721d1a5a..afb39dc1 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -8,6 +8,7 @@ from powerline.segments.vim import vim_modes from powerline.lint.inspect import getconfigargspec from powerline.lib.threaded import ThreadedSegment from powerline.lib import mergedicts_copy +from powerline.lib.unicode import unicode import itertools import sys import os @@ -17,12 +18,6 @@ from copy import copy import logging -try: - from __builtin__ import unicode -except ImportError: - unicode = str - - def open_file(path): return open(path, 'rb') @@ -1078,8 +1073,8 @@ def list_themes(data, context): is_main_theme = (data['theme'] == main_theme_name) if theme_type == 'top': return list(itertools.chain(*[ - [(ext, theme) for theme in theme_configs.values()] - for ext, theme_configs in data['theme_configs'].items() + [(theme_ext, theme) for theme in theme_configs.values()] + for theme_ext, theme_configs in data['theme_configs'].items() ])) elif theme_type == 'main' or is_main_theme: return [(ext, theme) for theme in data['ext_theme_configs'].values()] From 757e5632505bb753244d0e709a89f45f1193e34f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Aug 2014 12:18:04 +0400 Subject: [PATCH 1203/1472] Place labels before labeled key name, not before the description --- docs/source/configuration/reference.rst | 56 ++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index f8e7f148..9a828d2f 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -101,20 +101,20 @@ Common configuration is a subdictionary that is a value of ``ext`` key in ``colorscheme`` Defines the colorscheme used for this extension. -``theme`` - .. _config-ext-theme: +.. _config-ext-theme: +``theme`` Defines the theme used for this extension. -``top_theme`` - .. _config-ext-top_theme: +.. _config-ext-top_theme: +``top_theme`` Defines the top-level theme used for this extension. See `Themes`_ section for more details. -``local_themes`` - .. _config-ext-local_themes: +.. _config-ext-local_themes: +``local_themes`` Defines themes used when certain conditions are met, e.g. for buffer-specific statuslines in vim. Value depends on extension used. For vim it is a dictionary ``{matcher_name : theme_name}``, where ``matcher_name`` @@ -324,54 +324,54 @@ ascii Theme without any unicode characters at all ` and :ref:`args ` options. - ``module`` - .. _config-themes-seg-module: + .. _config-themes-seg-module: + ``module`` Function module, only required for function segments. Defaults to ``powerline.segments.{extension}``. Default is overriden by :ref:`default_module theme option `. - ``name`` - .. _config-themes-seg-name: + .. _config-themes-seg-name: + ``name`` Function name, only required for function and list segments. - ``highlight_group`` - .. _config-themes-seg-highlight_group: + .. _config-themes-seg-highlight_group: - Highlighting group for this segment. Consists of a prioritized list - of highlighting groups, where the first highlighting group that is + ``highlight_group`` + Highlighting group for this segment. Consists of a prioritized list of + highlighting groups, where the first highlighting group that is available in the colorscheme is used. Ignored for segments that have ``function`` type. - ``before`` - .. _config-themes-seg-before: + .. _config-themes-seg-before: + ``before`` A string which will be prepended to the segment contents. - ``after`` - .. _config-themes-seg-after: + .. _config-themes-seg-after: + ``after`` A string which will be appended to the segment contents. - ``contents`` - .. _config-themes-seg-contents: + .. _config-themes-seg-contents: + ``contents`` Segment contents, only required for ``string`` segments. - ``args`` - .. _config-themes-seg-args: + .. _config-themes-seg-args: + ``args`` A dict of arguments to be passed to a ``function`` segment. ``align`` Aligns the segments contents to the left (``l``), center (``c``) or right (``r``). - ``width`` - .. _config-themes-seg-width: + .. _config-themes-seg-width: + ``width`` Enforces a specific width for this segment. This segment will work as a spacer if the width is set to ``auto``. @@ -412,13 +412,13 @@ ascii Theme without any unicode characters at all A list of modes where this segment will be included: The segment is *not* included in any modes, *except* for the modes in this list. - ``display`` - .. _config-themes-seg-display: + .. _config-themes-seg-display: + ``display`` Boolean. If false disables displaying of the segment. Defaults to ``True``. - ``segments`` - .. _config-themes-seg-segments: + .. _config-themes-seg-segments: + ``segments`` A list of subsegments. From e64811c661ee0ed087256b31a794e2c9a463c5ba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 9 Aug 2014 13:04:30 +0400 Subject: [PATCH 1204/1472] Add vim_getoption and vim_setoption functions --- powerline/bindings/vim/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 76c40ffe..d030bd43 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -99,10 +99,23 @@ else: if hasattr(vim, 'options'): def vim_getbufoption(info, option): return info['buffer'].options[str(option)] + + def vim_getoption(option): + return vim.options[str(option)] + + def vim_setoption(option, value): + vim.options[str(option)] = value else: def vim_getbufoption(info, option): # NOQA return getbufvar(info['bufnr'], '&' + option) + def vim_getoption(option): # NOQA + return vim.eval('&g:' + option) + + def vim_setoption(option, value): # NOQA + vim.command('let &g:{option} = {value}'.format( + option=option, value=json.encode(value))) + if hasattr(vim, 'tabpages'): current_tabpage = lambda: vim.current.tabpage From 8de14c1fd7d984c475da7d7c5916ed9aa462f302 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 14:51:38 +0400 Subject: [PATCH 1205/1472] Do not use os.environ for write_output This is required for daemon to work properly --- powerline/shell.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/powerline/shell.py b/powerline/shell.py index baf0973c..513a6974 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -1,7 +1,5 @@ # vim:fileencoding=utf-8:noet -import os - from powerline import Powerline from powerline.lib import mergedicts, parsedotval @@ -82,7 +80,7 @@ def write_output(args, powerline, segment_info, write): for line in powerline.render_above_lines( width=args.width, segment_info=segment_info, - mode=os.environ.get('_POWERLINE_MODE'), + mode=segment_info['environ'].get('_POWERLINE_MODE'), ): write(line) write('\n') @@ -93,6 +91,6 @@ def write_output(args, powerline, segment_info, write): width=args.width, side=args.side, segment_info=segment_info, - mode=os.environ.get('_POWERLINE_MODE'), + mode=segment_info['environ'].get('_POWERLINE_MODE'), ) write(rendered) From 17b32b1765bcd3001bf1e4636c8674904dcf20c7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 15:28:30 +0400 Subject: [PATCH 1206/1472] Make ipython accept paths, not a single path --- docs/source/configuration/local.rst | 4 ++-- powerline/bindings/ipython/post_0_11.py | 2 +- powerline/bindings/ipython/pre_0_11.py | 4 ++-- powerline/ipython.py | 4 ++-- tests/test_provided_config_files.py | 2 +- .../ipython_home/profile_default/ipython_config.py | 2 ++ 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index d8d55e84..01545fb6 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -104,8 +104,8 @@ use ``c.Powerline.KEY``. Supported ``KEY`` strings or keyword argument names: a dictionary where keys are theme names and values are dictionaries which will be recursively merged with the contents of the given theme. -``path`` - Sets directory where configuration should be read from. If present, no +``paths`` + Sets directories where configuration should be read from. If present, no default locations are searched for configuration. No expansions are performed thus you cannot use paths starting with ``~/``. diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index dc6c8112..92f0887c 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -47,7 +47,7 @@ class ConfigurableIpythonPowerline(IpythonPowerline): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) - self.path = config.get('path') + self.paths = config.get('paths') super(ConfigurableIpythonPowerline, self).__init__(is_prompt) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 8d7f0503..a007cdb0 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -82,10 +82,10 @@ class PowerlinePrompt2(PowerlinePromptOut): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, is_prompt, config_overrides=None, theme_overrides={}, path=None): + def __init__(self, is_prompt, config_overrides=None, theme_overrides={}, paths=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides - self.path = path + self.paths = paths super(ConfigurableIpythonPowerline, self).__init__(is_prompt) diff --git a/powerline/ipython.py b/powerline/ipython.py index 6716e075..80327e72 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -31,8 +31,8 @@ class IpythonPowerline(Powerline): ) def get_config_paths(self): - if self.path: - return [self.path] + if self.paths: + return self.paths else: return super(IpythonPowerline, self).get_config_paths() diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 90dc3d4e..e738301f 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -112,7 +112,7 @@ class TestConfig(TestCase): from powerline.ipython import IpythonPowerline class IpyPowerline(IpythonPowerline): - path = None + paths = None config_overrides = None theme_overrides = {} diff --git a/tests/test_shells/ipython_home/profile_default/ipython_config.py b/tests/test_shells/ipython_home/profile_default/ipython_config.py index 658334f1..9f6bb567 100644 --- a/tests/test_shells/ipython_home/profile_default/ipython_config.py +++ b/tests/test_shells/ipython_home/profile_default/ipython_config.py @@ -1,6 +1,8 @@ +import os c = get_config() c.InteractiveShellApp.extensions = ['powerline.bindings.ipython.post_0_11'] c.TerminalInteractiveShell.autocall = 1 +c.Powerline.paths = [os.path.abspath('powerline/config_files')] c.Powerline.theme_overrides = { 'in': { 'segment_data': { From 5c5407cffd1a259596e9432f2d6aa144d2b6f9be Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 15:35:57 +0400 Subject: [PATCH 1207/1472] Make libzpython bindings accept multiple paths when using overrides --- docs/source/configuration/local.rst | 9 +++++---- powerline/bindings/zsh/__init__.py | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index 01545fb6..527f9650 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -81,12 +81,13 @@ are taken from zsh variables. but only subdictionaries for ``THEME_NAME`` key are merged with theme configuration when theme with given name is requested. -``POWERLINE_CONFIG_PATH`` - Sets directory where configuration should be read from. If present, no +``POWERLINE_CONFIG_PATHS`` + Sets directories where configuration should be read from. If present, no default locations are searched for configuration. No expansions are performed by powerline script itself, but zsh usually performs them on its - own if you set variable without quotes: ``POWERLINE_CONFIG_PATH=~/example``. - Expansion depends on zsh configuration. + own if you set variable without quotes: ``POWERLINE_CONFIG_PATHS=( ~/example + )``. You should use array parameter or the usual colon-separated + ``POWERLINE_CONFIG_PATHS=$HOME/path1:$HOME/path2``. Ipython overrides ================= diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 2bbf3184..444490b5 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -5,6 +5,7 @@ import zsh import atexit from powerline.shell import ShellPowerline from powerline.lib import parsedotval +from powerline.lib.unicode import unicode used_powerlines = [] @@ -44,9 +45,14 @@ class Args(object): @property def config_path(self): try: - return zsh.getvalue('POWERLINE_CONFIG_PATH') + ret = zsh.getvalue('POWERLINE_CONFIG_PATHS') except IndexError: return None + else: + if isinstance(ret, (unicode, str, bytes)): + return ret.split(type(ret)(':')) + else: + return ret @property def jobnum(self): From 310af9ae7595e5946e8fecf728c422e18a14479e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 15:46:43 +0400 Subject: [PATCH 1208/1472] Prefer powerline bindings directory over fish_function_path --- tests/test_shells/input.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index f8da144e..d256c6fa 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -1,4 +1,4 @@ -set fish_function_path $fish_function_path "$PWD/powerline/bindings/fish" +set fish_function_path "$PWD/powerline/bindings/fish" $fish_function_path powerline-setup set POWERLINE_COMMAND "$POWERLINE_COMMAND -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" From 6f550fce8b3e70166b54e471d61078a0a70f6e69 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 15:56:32 +0400 Subject: [PATCH 1209/1472] Write zero after writing current working directory in powerline.c --- client/powerline.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/powerline.c b/client/powerline.c index 0800f701..44819ffe 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -106,8 +106,10 @@ int main(int argc, char *argv[]) { wd = getcwd(NULL, 0); if (wd != NULL) { do_write(sd, wd, strlen(wd)); - free(wd); wd = NULL; + free(wd); + wd = NULL; } + do_write(sd, eof, 1); for(envp=environ; *envp; envp++) { do_write(sd, *envp, strlen(*envp)); From 99c9f730e09a0f63506fefedb039eb25f6d9a178 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 15:59:32 +0400 Subject: [PATCH 1210/1472] Do not omit running tests without ONLY_SHELL set --- tests/test_shells/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 3977d77f..3e1294be 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -126,7 +126,7 @@ export IPYTHONDIR="$PWD/tests/shell/ipython_home" unset ENV -if test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbb ; then +if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbb ; then powerline-daemon -k || true sleep 1s From 3c032405307167170728f7dd799237c41a58dc35 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 16:00:00 +0400 Subject: [PATCH 1211/1472] Remove tests/shell iff there are no failures --- tests/test_shells/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 3e1294be..a5e8f26e 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -183,5 +183,5 @@ if ! run_test ipython ipython ; then FAILED=1 fi -test "x$ONLY_SHELL" = "x" && rm -r tests/shell +test $FAILED -eq 0 && rm -r tests/shell exit $FAILED From f51134deef75c6eeafba41cb0a9870274579a3f3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 16:01:03 +0400 Subject: [PATCH 1212/1472] Run shell tests in an isolated environment --- tests/test_shells/input.tcsh | 4 +-- tests/test_shells/test.sh | 58 ++++++++++++++++++++++++++++++-- tests/test_shells/zsh.daemon.ok | Bin 11878 -> 12453 bytes 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/tests/test_shells/input.tcsh b/tests/test_shells/input.tcsh index 0bc6b78a..3b5b1eb0 100644 --- a/tests/test_shells/input.tcsh +++ b/tests/test_shells/input.tcsh @@ -1,10 +1,10 @@ source powerline/bindings/tcsh/powerline.tcsh -setenv POWERLINE_COMMAND "$POWERLINE_COMMAND -p "$PWD:q/powerline/config_files" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" +set POWERLINE_COMMAND=$POWERLINE_COMMAND:q" -p "$PWD:q/powerline/config_files" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" unsetenv VIRTUAL_ENV cd tests/shell/3rd cd .git cd .. -setenv VIRTUAL_ENV $HOME:q"/.virtenvs/some-virtual-environment" +setenv VIRTUAL_ENV "/home/foo/.virtenvs/some-virtual-environment" unsetenv VIRTUAL_ENV bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & false # Warning: currently tcsh bindings do not support job count diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index a5e8f26e..4aff414e 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -17,6 +17,25 @@ check_screen_log() { fi } +run() { + local local_path="$PWD/tests/shell/path:$PWD/scripts" + if test "x$SH" = "xfish" ; then + local_path="${local_path}:/usr/bin:/bin" + fi + env -i \ + PATH="$local_path" \ + TERM="${TERM}" \ + COLUMNS="${COLUMNS}" \ + LINES="${LINES}" \ + TEST_TYPE="${TEST_TYPE}" \ + SH="${SH}" \ + DIR1="${DIR1}" \ + DIR2="${DIR2}" \ + XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" \ + IPYTHONDIR="$PWD/tests/shell/ipython_home" \ + "$@" +} + run_test() { TEST_TYPE="$1" shift @@ -41,7 +60,7 @@ run_test() { export TEST_TYPE export SH - screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ + run screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH # Wait for screen to initialize @@ -121,8 +140,41 @@ mkdir tests/shell/3rd/'`echo`' mkdir tests/shell/fish_home cp -r tests/test_shells/ipython_home tests/shell -export XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" -export IPYTHONDIR="$PWD/tests/shell/ipython_home" + +mkdir tests/shell/path +ln -s "$(which "${PYTHON:-python}")" tests/shell/path/python +ln -s "$(which screen)" tests/shell/path +ln -s "$(which env)" tests/shell/path +ln -s "$(which sleep)" tests/shell/path +ln -s "$(which cat)" tests/shell/path +ln -s "$(which false)" tests/shell/path +ln -s "$(which true)" tests/shell/path +ln -s "$(which kill)" tests/shell/path +ln -s "$(which echo)" tests/shell/path +ln -s "$(which which)" tests/shell/path +ln -s "$(which dirname)" tests/shell/path +ln -s "$(which wc)" tests/shell/path +ln -s "$(which stty)" tests/shell/path +ln -s "$(which cut)" tests/shell/path +ln -s "$(which bc)" tests/shell/path +ln -s "$(which expr)" tests/shell/path +ln -s "$(which mktemp)" tests/shell/path +for pexe in powerline powerline-config ; do + if test -e scripts/$pexe ; then + ln -s "$PWD/scripts/$pexe" tests/shell/path + elif which $pexe ; then + ln -s "$(which $pexe)" tests/shell/path + else + echo "Executable $pexe was not found" + exit 1 + fi +done + +for exe in bash zsh bb busybox fish tcsh mksh dash ipython ; do + if which $exe >/dev/null ; then + ln -s "$(which $exe)" tests/shell/path + fi +done unset ENV diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 7469e23b61ee6790cceceee1aa8a298fed046535..3582bc706c3cb143b0a69653a315f1300642a859 100644 GIT binary patch delta 467 zcmaDBvovvop@NdNiG{VPwIPr+%ssTg(=XUHDCE!r>1YFMW2nSrU2cuZ8`+~LcPW3F z+^Z-pY7Eh1WNZl5*q4rP+VlPF}D&(<>oGBK}HfxCRIJyCErywNw5N}rAA$e zR4uQj2V;dw7*O~r6@gl0cR$dU9T()7e{8lR?~!wuhmcju{Yak8#7K` zu4OWLwhS-Rp#_^4s`D^T&d{=-T&QC(@iq@oXmTRYk;xl$>n1zsl>)IWqr&7TddVOb cmjVbR6{RL-PyVPE1{9K?%w`ZdxzNA_0LUCZE&u=k From b8049fab87769fea6bb156d27c5755e982393553 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 16:01:57 +0400 Subject: [PATCH 1213/1472] Add ext.*.components configuration support Also fixes various problems in shell bindings. Closes #969 --- docs/source/configuration/local.rst | 12 ++++----- docs/source/configuration/reference.rst | 22 ++++++++++++++++ powerline/__init__.py | 20 ++++++++++++++- powerline/bindings/bash/powerline.sh | 24 +++++++++-------- powerline/bindings/config.py | 27 ++++++++++++++++++-- powerline/bindings/fish/powerline-setup.fish | 20 +++++++++------ powerline/bindings/shell/powerline.sh | 23 +++++++++-------- powerline/bindings/tcsh/powerline.tcsh | 17 +++++++----- powerline/bindings/zsh/powerline.zsh | 20 +++++++++------ powerline/lint/__init__.py | 3 +++ powerline/vim.py | 19 ++++++++++---- scripts/powerline-config | 15 ++++++++++- 12 files changed, 163 insertions(+), 59 deletions(-) diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index 527f9650..13902514 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -115,9 +115,9 @@ Prompt command In addition to the above configuration options you can use ``$POWERLINE_COMMAND`` environment variable to tell shell or tmux to use -specific powerline implementation. This is mostly useful for putting powerline -into different directory or replacing ``powerline`` script with -``powerline-client`` for performance reasons. +specific powerline implementation and ``$POWERLINE_CONFIG`` to tell zsh or tmux +where ``powerline-config`` script is located. This is mostly useful for putting +powerline into different directory. .. note:: @@ -150,6 +150,6 @@ fish in order to support multiline prompt you should set .. note:: - Most supported shells’ configuration scripts check for additional - configuration variables being empty. But tcsh configuration script checks - for variables being *defined*, not empty. + Most supported shells’ configuration scripts check for ``$POWERLINE_CONFIG`` + and ``$POWERLINE_COMMAND`` configuration variables being empty. But tcsh + configuration script checks for variables being *defined*, not empty. diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 9a828d2f..8f043e43 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -123,6 +123,28 @@ Common configuration is a subdictionary that is a value of ``ext`` key in ``module_attribute`` should point to a function that returns boolean value indicating that current buffer has (not) matched conditions. +``components`` + Determines which extension components should be enabled. This key is highly + extension-specific, here is the table of extensions and corresponding + components: + + +---------+----------+-----------------------------------------------------+ + |Extension|Component |Description | + +---------+----------+-----------------------------------------------------+ + |vim |statusline|Makes Vim use powerline statusline. | + | +----------+-----------------------------------------------------+ + | |tabline |Makes Vim use powerline tabline. | + +---------+----------+-----------------------------------------------------+ + |shell |prompt |Makes shell display powerline prompt. | + | +----------+-----------------------------------------------------+ + | |tmux |Makes shell report its current working directory | + | | |and screen width to tmux for tmux powerline | + | | |bindings. | + | | |  | + +---------+----------+-----------------------------------------------------+ + + All components are enabled by default. + .. _config-colors: Color definitions diff --git a/powerline/__init__.py b/powerline/__init__.py index 08446057..ff568cf1 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -394,7 +394,15 @@ class Powerline(object): self.ext_config = config['ext'][self.ext] if self.ext_config != self.prev_ext_config: ext_config_differs = True - if not self.prev_ext_config or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes'): + if ( + not self.prev_ext_config + or self.ext_config.get('components') != self.prev_ext_config.get('components') + ): + self.setup_components(self.ext_config.get('components')) + if ( + not self.prev_ext_config + or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes') + ): self.renderer_options['local_themes'] = self.get_local_themes(self.ext_config.get('local_themes')) load_colorscheme = (load_colorscheme or not self.prev_ext_config @@ -439,6 +447,16 @@ class Powerline(object): else: self.renderer = renderer + def setup_components(self, components): + '''Run component-specific setup + + :param set components: + Set of the enabled componets or None. + + Should be overridden by subclasses. + ''' + pass + @staticmethod def get_config_paths(): '''Get configuration paths. diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 501b48aa..eb705a7c 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -35,7 +35,7 @@ _powerline_init_tmux_support() { _powerline_tmux_set_columns test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND/_powerline_tmux_set_pwd}" || - export PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n_powerline_tmux_set_pwd' + PROMPT_COMMAND="${PROMPT_COMMAND}"$'\n_powerline_tmux_set_pwd' fi } @@ -54,21 +54,23 @@ _powerline_set_prompt() { _powerline_setup_prompt() { VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-config &>/dev/null ; then - export POWERLINE_COMMAND="$(powerline-config shell command)" - else - # `$0` is set to `-bash` when using SSH so that won't work - local powerline_dir="$(dirname "$BASH_SOURCE")/../../.." - export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" - fi + POWERLINE_COMMAND="$("$POWERLINE_CONFIG" shell command)" fi test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || - export PROMPT_COMMAND=$'_powerline_set_prompt\n'"${PROMPT_COMMAND}" + PROMPT_COMMAND=$'_powerline_set_prompt\n'"${PROMPT_COMMAND}" } -if test -z "$POWERLINE_NO_BASH_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then +if test -z "${POWERLINE_CONFIG}" ; then + if which powerline-config >/dev/null ; then + POWERLINE_CONFIG=powerline-config + else + POWERLINE_CONFIG="$(dirname "$BASH_SOURCE")/../../../scripts/powerline-config" + fi +fi + +if "${POWERLINE_CONFIG}" shell --shell=bash uses prompt ; then _powerline_setup_prompt fi -if test -z "$POWERLINE_NO_BASH_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then +if "${POWERLINE_CONFIG}" shell --shell=bash uses tmux ; then _powerline_init_tmux_support fi diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index e63a6c00..fac81ae0 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -118,10 +118,14 @@ def source_tmux_files(pl, args): run_tmux_command('refresh-client') -def create_powerline_logger(args): +def get_main_config(args): find_config_files = generate_config_finder() config_loader = ConfigLoader(run_once=True) - config = load_config('config', find_config_files, config_loader) + return load_config('config', find_config_files, config_loader) + + +def create_powerline_logger(args): + config = get_main_config(args) common_config = finish_common_config(config['common']) logger = create_logger(common_config) return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config') @@ -166,3 +170,22 @@ def shell_command(pl, args): print(cmd) else: sys.exit(1) + + +def uses(pl, args): + component = args.component + if not component: + raise ValueError('Must specify component') + shell = args.shell + template = 'POWERLINE_NO_{shell}_{component}' + for sh in (shell, 'shell') if shell else ('shell'): + varname = template.format(shell=sh.upper(), component=component.upper()) + if os.environ.get(varname): + print ('HERE') + sys.exit(1) + config = get_main_config(args) + if component in config.get('ext', {}).get('shell', {}).get('components', ('tmux', 'prompt')): + sys.exit(0) + else: + print ('THERE') + sys.exit(1) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 89fe9778..56f6d52e 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -18,14 +18,17 @@ function powerline-setup end end - if test -z "$POWERLINE_NO_FISH_PROMPT$POWERLINE_NO_SHELL_PROMPT" + if test -z "$POWERLINE_CONFIG" + if which powerline-config >/dev/null + set -g POWERLINE_CONFIG powerline-config + else + set -g POWERLINE_CONFIG (dirname (status -f))/../../../scripts/powerline-config + end + end + + if eval $POWERLINE_CONFIG shell --shell=fish uses prompt if test -z "$POWERLINE_COMMAND" - if false ;and which powerline-config >/dev/null - set -g -x POWERLINE_COMMAND (powerline-config shell command) - else - set -l powerline_dir (dirname (status -f))/../../.. - set -g -x POWERLINE_COMMAND (eval $powerline_dir/scripts/powerline-config shell command) - end + set -g POWERLINE_COMMAND (eval $POWERLINE_CONFIG shell command) end function --on-variable POWERLINE_COMMAND _powerline_update set -l addargs "--last_exit_code=\$status" @@ -60,7 +63,7 @@ function powerline-setup end _powerline_update end - if test -z "$POWERLINE_NO_FISH_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" + if eval $POWERLINE_CONFIG shell --shell=fish uses tmux if test -n "$TMUX" if tmux refresh -S ^/dev/null function _powerline_tmux_setenv @@ -79,3 +82,4 @@ function powerline-setup end end end +# vim: ft=fish diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index ae5c035f..e78e3625 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -98,12 +98,7 @@ _powerline_set_set_jobs() { _powerline_set_command() { if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-config &>/dev/null ; then - export POWERLINE_COMMAND="$(powerline-config shell command)" - else - local powerline_dir="$(dirname "$POWERLINE_SOURCED")/../../.." - export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" - fi + POWERLINE_COMMAND="$("$POWERLINE_CONFIG" shell command)" fi } @@ -167,14 +162,22 @@ _powerline_init_tmux_support() { fi } +if test -z "${POWERLINE_CONFIG}" ; then + if which powerline-config >/dev/null ; then + POWERLINE_CONFIG=powerline-config + else + POWERLINE_CONFIG="$(dirname "$_POWERLINE_SOURCED")/../../../scripts/powerline-config" + fi +fi + # Strips the leading `-`: it may be present when shell is a login shell _POWERLINE_USED_SHELL=${0#-} _POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/usr} _POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/bin/} -if test -z "$POWERLINE_NO_BB_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then - _powerline_setup_prompt $_POWERLINE_USED_SHELL -fi -if test -z "$POWERLINE_NO_BB_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then +if "${POWERLINE_CONFIG}" shell uses tmux ; then _powerline_init_tmux_support $_POWERLINE_USED_SHELL fi +if "${POWERLINE_CONFIG}" shell --shell=bash uses prompt ; then + _powerline_setup_prompt $_POWERLINE_USED_SHELL +fi diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 69f34a2e..5ac0cf33 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -5,17 +5,20 @@ # Guess this relies on `$_` being set as to last argument to previous command # which must be `.` or `source` in this case set POWERLINE_SOURCED=($_) -if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) then +if ! $?POWERLINE_CONFIG then + if ( { which powerline-config > /dev/null } ) then + set POWERLINE_CONFIG="powerline-config" + else + set POWERLINE_CONFIG="$POWERLINE_SOURCED[2]:h:h:h:h/scripts/powerline-config" + endif +endif +if ( { $POWERLINE_CONFIG shell --shell=tcsh uses tmux } ) then alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" endif -if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then +if ( { $POWERLINE_CONFIG shell --shell=tcsh uses prompt } ) then if ! $?POWERLINE_COMMAND then - if ( { which powerline-config > /dev/null } ) then - setenv POWERLINE_COMMAND "`powerline-config shell command`" - else - setenv POWERLINE_COMMAND "`$POWERLINE_SOURCED[2]:h:h:h:h:q/scripts/powerline-config shell command`" - endif + set POWERLINE_COMMAND="`$POWERLINE_CONFIG:q shell command`" endif if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 5439167a..626ee58c 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -126,12 +126,7 @@ _powerline_setup_prompt() { zpython 'del _powerline_setup' else if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-config &>/dev/null ; then - export POWERLINE_COMMAND="$(powerline-config shell command)" - else - local powerline_dir="$POWERLINE_SOURCED:h:h:h:h" - export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" - fi + POWERLINE_COMMAND=( "$($POWERLINE_CONFIG shell command)" ) fi local add_args='--last_exit_code=$?' @@ -176,12 +171,21 @@ _powerline_add_widget() { fi } +if test -z "${POWERLINE_CONFIG}" ; then + if which powerline-config >/dev/null ; then + export POWERLINE_CONFIG=powerline-config + else + export POWERLINE_CONFIG="$_POWERLINE_SOURCED:h:h:h:h/scripts/powerline-config" + fi +fi + setopt promptpercent setopt promptsubst -if test -z "$POWERLINE_NO_ZSH_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then + +if ${POWERLINE_CONFIG} shell --shell=zsh uses prompt ; then _powerline_setup_prompt _powerline_init_modes_support fi -if test -z "$POWERLINE_NO_ZSH_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then +if ${POWERLINE_CONFIG} shell --shell=zsh uses tmux ; then _powerline_init_tmux_support fi diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index afb39dc1..7d894049 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -525,6 +525,7 @@ ext_spec = Spec( theme=ext_theme_spec(), top_theme=top_theme_spec().optional(), ).copy +gen_components_spec = (lambda *components: Spec().list(Spec().type(unicode).oneof(set(components)))) main_spec = (Spec( common=Spec( default_top_theme=top_theme_spec().optional(), @@ -556,6 +557,7 @@ main_spec = (Spec( ).context_message('Error while loading common configuration (key {key})'), ext=Spec( vim=ext_spec().update( + components=gen_components_spec('statusline', 'tabline').optional(), local_themes=Spec( __tabline__=ext_theme_spec(), ).unknown_spec( @@ -570,6 +572,7 @@ main_spec = (Spec( ), ).optional(), shell=ext_spec().update( + components=gen_components_spec('tmux', 'prompt').optional(), local_themes=Spec( continuation=ext_theme_spec(), select=ext_theme_spec(), diff --git a/powerline/vim.py b/powerline/vim.py index 8afae555..81b0b9ba 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -27,6 +27,7 @@ class VimPowerline(Powerline): def __init__(self, pyeval='PowerlinePyeval', **kwargs): super(VimPowerline, self).__init__('vim', **kwargs) self.last_window_id = 1 + self.pyeval = pyeval self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' def add_local_theme(self, key, config): @@ -159,6 +160,18 @@ class VimPowerline(Powerline): vim.command('return ' + json.dumps(eval(vim.eval('a:e'), __main__.__dict__))) + def setup_components(self, components): + if components is None: + components = ('statusline', 'tabline') + if 'statusline' in components: + # Is immediately changed after new_window function is run. Good for + # global value. + vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format( + pyeval=self.pyeval)) + if 'tabline' in components: + vim.command('set tabline=%!{pyeval}(\'powerline.tabline()\')'.format( + pyeval=self.pyeval)) + pycmd = None @@ -192,6 +205,7 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): pyeval = 'PowerlinePyeval' powerline = VimPowerline(pyeval) + powerline.update_renderer() __main__.powerline = powerline # Cannot have this in one line due to weird newline handling (in :execute @@ -202,8 +216,3 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd)) vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) vim.command('augroup END') - - # Is immediately changed after new_window function is run. Good for global - # value. - vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format(pyeval=pyeval)) - vim.command('set tabline=%!{pyeval}(\'powerline.tabline()\')'.format(pyeval=pyeval)) diff --git a/scripts/powerline-config b/scripts/powerline-config index 2e636437..0d82eba5 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -20,6 +20,7 @@ TMUX_ACTIONS = { SHELL_ACTIONS = { 'command': config.shell_command, + 'uses': config.uses, } @@ -40,7 +41,19 @@ if __name__ == '__main__': 'function', choices=tuple(SHELL_ACTIONS.values()), type=(lambda v: SHELL_ACTIONS.get(v)), - help='If action is "command" then preferred powerline command is output', + metavar='action', + help='If action is "command" then preferred powerline command is output, if it is “uses” then powerline-config script will exit with 1 if specified component is disabled and 0 otherwise.', + ) + shell_parser.add_argument( + 'component', + nargs='?', + choices=('tmux', 'prompt'), + metavar='component', + ) + shell_parser.add_argument( + '-s', '--shell', + nargs='?', + help='Shell for which query is run', ) args = parser.parse_args() From 9d1392fa0d6a81aefadf1818eabcc5476338ebff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 16:17:22 +0400 Subject: [PATCH 1214/1472] Fix vim emulation module --- tests/vim.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/vim.py b/tests/vim.py index 7867f563..29da4415 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -211,8 +211,15 @@ def command(cmd): if not aucmd.startswith(':python3 '): raise NotImplementedError _on_wipeout.append(aucmd.partition(' ')[2]) + elif cmd.startswith('set '): + if cmd.startswith('set statusline='): + options['statusline'] = cmd[len('set statusline='):] + elif cmd.startswith('set tabline='): + options['tabline'] = cmd[len('set tabline='):] + else: + raise NotImplementedError(cmd) else: - raise NotImplementedError + raise NotImplementedError(cmd) @_vim @@ -481,6 +488,7 @@ class _Tabpage(object): return win def _close(self): + global _tabpage while self.windows: self._close_window(1, False) tabpages._pop(self.number) @@ -532,7 +540,7 @@ class _Buffer(object): import os if type(name) is not bytes: name = name.encode('utf-8') - if b':/' in name: + if b':/' in name: self._name = name else: self._name = os.path.abspath(name) @@ -680,6 +688,7 @@ def _edit(name=None): @_vim def _tabnew(name=None): global windows + global _tabpage tabpage = tabpages._new() windows = tabpage.windows _tabpage = len(tabpages) From 5313e61fe6462d4fa5ec0efdd8693fca17a55efc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 16:32:27 +0400 Subject: [PATCH 1215/1472] Fix name conflict in vim bindings --- powerline/vim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/vim.py b/powerline/vim.py index 81b0b9ba..c60ee20c 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -155,7 +155,7 @@ class VimPowerline(Powerline): # requirements to __main__ globals to just one powerline object # (previously it required as well vim and json) @staticmethod - def pyeval(): + def do_pyeval(): import __main__ vim.command('return ' + json.dumps(eval(vim.eval('a:e'), __main__.__dict__))) @@ -199,7 +199,7 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): if not hasattr(vim, 'bindeval') and can_replace_pyeval: vim.command((''' function! PowerlinePyeval(e) - {pycmd} powerline.pyeval() + {pycmd} powerline.do_pyeval() endfunction ''').format(pycmd=pycmd)) pyeval = 'PowerlinePyeval' From 01185eb9c803cd7e23fbc484e7c6c3f4fedaf50a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 16:58:55 +0400 Subject: [PATCH 1216/1472] Use bgscript.sh in place of direct bash call --- tests/test_shells/bash.ok | 4 ++-- tests/test_shells/bb.ok | 4 ++-- tests/test_shells/bgscript.sh | 5 +++++ tests/test_shells/dash.ok | 4 ++-- tests/test_shells/input.bash | 2 +- tests/test_shells/input.bb | 2 +- tests/test_shells/input.dash | 2 +- tests/test_shells/input.fish | 2 +- tests/test_shells/input.mksh | 2 +- tests/test_shells/input.tcsh | 2 +- tests/test_shells/input.zsh | 2 +- tests/test_shells/mksh.ok | 2 +- tests/test_shells/test.sh | 1 + tests/test_shells/zsh.daemon.ok | Bin 12453 -> 12361 bytes tests/test_shells/zsh.nodaemon.ok | 4 ++-- 15 files changed, 22 insertions(+), 16 deletions(-) create mode 100755 tests/test_shells/bgscript.sh diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index 34ec242f..5fb00ea1 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -2,11 +2,11 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1]+ Terminated bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' +[1]+ Terminated bgscript.sh   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' diff --git a/tests/test_shells/bb.ok b/tests/test_shells/bb.ok index 8a439c05..350cf6e3 100644 --- a/tests/test_shells/bb.ok +++ b/tests/test_shells/bb.ok @@ -2,10 +2,10 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh &   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1]+ Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +[1]+ Terminated bgscript.sh   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' diff --git a/tests/test_shells/bgscript.sh b/tests/test_shells/bgscript.sh new file mode 100755 index 00000000..71886e62 --- /dev/null +++ b/tests/test_shells/bgscript.sh @@ -0,0 +1,5 @@ +#!/bin/sh +echo $$ > pid +while true ; do + sleep 0.1s +done diff --git a/tests/test_shells/dash.ok b/tests/test_shells/dash.ok index 2a42cd64..7ab16d04 100644 --- a/tests/test_shells/dash.ok +++ b/tests/test_shells/dash.ok @@ -2,10 +2,10 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh &   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1] + Terminated bash -c "echo \$\$>pid ; while true ; do sleep 0.1s ; done" +[1] + Terminated bgscript.sh cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1    HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index ff7ad797..3842c75f 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.bb b/tests/test_shells/input.bb index 2d4fbc42..bfe0f315 100644 --- a/tests/test_shells/input.bb +++ b/tests/test_shells/input.bb @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash index 2d4fbc42..bfe0f315 100644 --- a/tests/test_shells/input.dash +++ b/tests/test_shells/input.dash @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index d256c6fa..1ffc8f11 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -9,7 +9,7 @@ cd .git cd .. setenv VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" setenv VIRTUAL_ENV -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false kill (cat pid) ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh index a2f0a7dd..52c1e59f 100644 --- a/tests/test_shells/input.mksh +++ b/tests/test_shells/input.mksh @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false kill `cat pid` ; sleep 1 cd "$DIR1" diff --git a/tests/test_shells/input.tcsh b/tests/test_shells/input.tcsh index 3b5b1eb0..a888b10f 100644 --- a/tests/test_shells/input.tcsh +++ b/tests/test_shells/input.tcsh @@ -6,7 +6,7 @@ cd .git cd .. setenv VIRTUAL_ENV "/home/foo/.virtenvs/some-virtual-environment" unsetenv VIRTUAL_ENV -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false # Warning: currently tcsh bindings do not support job count kill `cat pid` ; sleep 1s cd $DIR1:q diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 51095f5f..2f967499 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -14,7 +14,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +bgscript.sh & false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/mksh.ok b/tests/test_shells/mksh.ok index 09307716..278d5442 100644 --- a/tests/test_shells/mksh.ok +++ b/tests/test_shells/mksh.ok @@ -3,7 +3,7 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 4aff414e..4a741aed 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -159,6 +159,7 @@ ln -s "$(which cut)" tests/shell/path ln -s "$(which bc)" tests/shell/path ln -s "$(which expr)" tests/shell/path ln -s "$(which mktemp)" tests/shell/path +ln -s ../../test_shells/bgscript.sh tests/shell/path for pexe in powerline powerline-config ; do if test -e scripts/$pexe ; then ln -s "$PWD/scripts/$pexe" tests/shell/path diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 3582bc706c3cb143b0a69653a315f1300642a859..592929cebc6cdd2c640cc3aa3bae3d3cbd9262bf 100644 GIT binary patch delta 48 ucmZ3Qcrsyw6DL=CadJ^+L5W^*#^x%{U>0QlK}H3}$qI~fHy`4EtN;MT-V#Ir delta 140 zcmX?^urzUl6Q@ODafX6!vVwYQaz?&_ii%x9W{QHfLU~4JPO3slQE4iWlajAcoRgYb kpkSb9SPbUprK)fC;S6RWU-v;q1;)t=jB_`q@jq4o06|MEKmY&$ diff --git a/tests/test_shells/zsh.nodaemon.ok b/tests/test_shells/zsh.nodaemon.ok index daa84f40..8f1394d9 100644 --- a/tests/test_shells/zsh.nodaemon.ok +++ b/tests/test_shells/zsh.nodaemon.ok @@ -3,11 +3,11 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1] + terminated bash -c 'echo $$>pid ; while true ; do sleep 0.1s ; done' +[1] + terminated bgscript.sh   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2"   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' From 7ade6d1fc028270b3bf9031e2271a630aab7ecc2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 28 Jul 2014 03:15:36 +0400 Subject: [PATCH 1217/1472] Use busybox as shell name in place of bb Conflicts: tests/test_shells/test.sh --- tests/test_shells/{bb.ok => busybox.ok} | 0 tests/test_shells/{input.bb => input.busybox} | 0 tests/test_shells/test.sh | 16 ++++------------ 3 files changed, 4 insertions(+), 12 deletions(-) rename tests/test_shells/{bb.ok => busybox.ok} (100%) rename tests/test_shells/{input.bb => input.busybox} (100%) diff --git a/tests/test_shells/bb.ok b/tests/test_shells/busybox.ok similarity index 100% rename from tests/test_shells/bb.ok rename to tests/test_shells/busybox.ok diff --git a/tests/test_shells/input.bb b/tests/test_shells/input.busybox similarity index 100% rename from tests/test_shells/input.bb rename to tests/test_shells/input.busybox diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 4a741aed..a1c1a61b 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -46,15 +46,7 @@ run_test() { test "x$ONLY_SHELL" = "x" || test "x$ONLY_SHELL" = "x$SH" || return 0 if ! which "${SH}" ; then - if test "x${SH}" = "xbb" ; then - if ! which busybox ; then - return 0 - fi - shift - ARGS=( busybox ash "$@" ) - else - return 0 - fi + return 0 fi export TEST_TYPE @@ -108,8 +100,8 @@ run_test() { dash) # ? ;; - bb) - bb --help + busybox) + busybox --help ;; *) ${SH} --version @@ -205,7 +197,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te FAILED=1 fi - if ! run_test $TEST_TYPE bb -i ; then + if ! run_test $TEST_TYPE busybox ash -i ; then FAILED=1 fi From fdd8f6df3cc47c0e10740e3c9aa68ab113d1e55c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 17:10:35 +0400 Subject: [PATCH 1218/1472] Wait for pid file to appear Should fix some rare bash test failures --- tests/test_shells/bash.ok | 2 +- tests/test_shells/busybox.ok | 2 +- tests/test_shells/dash.ok | 2 +- tests/test_shells/input.bash | 2 +- tests/test_shells/input.busybox | 2 +- tests/test_shells/input.dash | 2 +- tests/test_shells/input.fish | 2 +- tests/test_shells/input.mksh | 2 +- tests/test_shells/input.tcsh | 2 +- tests/test_shells/input.zsh | 2 +- tests/test_shells/mksh.ok | 2 +- tests/test_shells/test.sh | 2 ++ tests/test_shells/waitpid.sh | 4 ++++ tests/test_shells/zsh.daemon.ok | Bin 12361 -> 12372 bytes tests/test_shells/zsh.nodaemon.ok | 2 +- 15 files changed, 18 insertions(+), 12 deletions(-) create mode 100755 tests/test_shells/waitpid.sh diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok index 5fb00ea1..0ee4e9b6 100644 --- a/tests/test_shells/bash.ok +++ b/tests/test_shells/bash.ok @@ -2,7 +2,7 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s diff --git a/tests/test_shells/busybox.ok b/tests/test_shells/busybox.ok index 350cf6e3..760d7d03 100644 --- a/tests/test_shells/busybox.ok +++ b/tests/test_shells/busybox.ok @@ -2,7 +2,7 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bgscript.sh diff --git a/tests/test_shells/dash.ok b/tests/test_shells/dash.ok index 7ab16d04..2ad24b25 100644 --- a/tests/test_shells/dash.ok +++ b/tests/test_shells/dash.ok @@ -2,7 +2,7 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1] + Terminated bgscript.sh diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 3842c75f..47042cf9 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bgscript.sh & +bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.busybox b/tests/test_shells/input.busybox index bfe0f315..d5b11b12 100644 --- a/tests/test_shells/input.busybox +++ b/tests/test_shells/input.busybox @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bgscript.sh & +bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash index bfe0f315..d5b11b12 100644 --- a/tests/test_shells/input.dash +++ b/tests/test_shells/input.dash @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bgscript.sh & +bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 1ffc8f11..eda074e9 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -9,7 +9,7 @@ cd .git cd .. setenv VIRTUAL_ENV "$HOME/.virtenvs/some-virtual-environment" setenv VIRTUAL_ENV -bgscript.sh & +bgscript.sh & waitpid.sh false kill (cat pid) ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh index 52c1e59f..0b0b4904 100644 --- a/tests/test_shells/input.mksh +++ b/tests/test_shells/input.mksh @@ -8,7 +8,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bgscript.sh & +bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1 cd "$DIR1" diff --git a/tests/test_shells/input.tcsh b/tests/test_shells/input.tcsh index a888b10f..297cff9c 100644 --- a/tests/test_shells/input.tcsh +++ b/tests/test_shells/input.tcsh @@ -6,7 +6,7 @@ cd .git cd .. setenv VIRTUAL_ENV "/home/foo/.virtenvs/some-virtual-environment" unsetenv VIRTUAL_ENV -bgscript.sh & +bgscript.sh & waitpid.sh false # Warning: currently tcsh bindings do not support job count kill `cat pid` ; sleep 1s cd $DIR1:q diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 2f967499..228a9ca9 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -14,7 +14,7 @@ cd .git cd .. VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" VIRTUAL_ENV= -bgscript.sh & +bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s cd "$DIR1" diff --git a/tests/test_shells/mksh.ok b/tests/test_shells/mksh.ok index 278d5442..4770aff1 100644 --- a/tests/test_shells/mksh.ok +++ b/tests/test_shells/mksh.ok @@ -3,7 +3,7 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd ..   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment"   HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh [1] PID   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false   HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index a1c1a61b..b7302d89 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -78,6 +78,7 @@ run_test() { sleep 0.1s done ./tests/test_shells/postproc.py ${TEST_TYPE} ${SH} + rm -f tests/shell/3rd/pid if ! check_screen_log ${TEST_TYPE} ${SH} ; then echo '____________________________________________________________' # Repeat the diff to make it better viewable in travis output @@ -152,6 +153,7 @@ ln -s "$(which bc)" tests/shell/path ln -s "$(which expr)" tests/shell/path ln -s "$(which mktemp)" tests/shell/path ln -s ../../test_shells/bgscript.sh tests/shell/path +ln -s ../../test_shells/waitpid.sh tests/shell/path for pexe in powerline powerline-config ; do if test -e scripts/$pexe ; then ln -s "$PWD/scripts/$pexe" tests/shell/path diff --git a/tests/test_shells/waitpid.sh b/tests/test_shells/waitpid.sh new file mode 100755 index 00000000..8d98e216 --- /dev/null +++ b/tests/test_shells/waitpid.sh @@ -0,0 +1,4 @@ +#!/bin/sh +while ! test -e pid ; do + sleep 0.1s +done diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 592929cebc6cdd2c640cc3aa3bae3d3cbd9262bf..d57f21828c6a9bf88c39fb4aeac747aae4a596c6 100644 GIT binary patch delta 24 fcmX?^a3x`bA1Aj$d17WsL1v0xamHpp&fjVPef$Zw delta 12 TcmcbTa57 Date: Sun, 10 Aug 2014 17:37:08 +0400 Subject: [PATCH 1219/1472] Add client_id renderer argument to all shell bindings --- powerline/bindings/bash/powerline.sh | 7 ++++++- powerline/bindings/fish/powerline-setup.fish | 3 +++ powerline/bindings/shell/powerline.sh | 7 ++++++- powerline/bindings/tcsh/powerline.tcsh | 6 +++--- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index eb705a7c..b267c076 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -41,7 +41,12 @@ _powerline_init_tmux_support() { _powerline_prompt() { # Arguments: side, last_exit_code, jobnum - $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" -r bash_prompt --last_exit_code=$2 --jobnum=$3 + $POWERLINE_COMMAND shell $1 \ + -w "${COLUMNS:-$(_powerline_columns_fallback)}" \ + -r bash_prompt \ + --last_exit_code=$2 \ + --jobnum=$3 \ + --renderer_arg="client_id=$$" } _powerline_set_prompt() { diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 56f6d52e..6dc100c0 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -34,6 +34,9 @@ function powerline-setup set -l addargs "--last_exit_code=\$status" set -l addargs "$addargs --last_pipe_status=\$status" set -l addargs "$addargs --jobnum=(jobs -p | wc -l)" + # One random value has an 1/32767 = 0.0031% probability of having + # the same value in two shells + set -l addargs "$addargs --renderer_arg=client_id="(random) set -l addargs "$addargs --width=\$_POWERLINE_COLUMNS" set -l promptside set -l rpromptpast diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index e78e3625..89da8d8f 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -134,7 +134,12 @@ _powerline_set_jobs() { _powerline_prompt() { # Arguments: side, exit_code _powerline_set_jobs - $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" $_POWERLINE_RENDERER_ARG --last_exit_code=$2 --jobnum=$_POWERLINE_JOBS + $POWERLINE_COMMAND shell $1 \ + -w "${COLUMNS:-$(_powerline_columns_fallback)}" \ + $_POWERLINE_RENDERER_ARG \ + --renderer_arg="client_id=$$" \ + --last_exit_code=$2 \ + --jobnum=$_POWERLINE_JOBS } _powerline_setup_prompt() { diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 5ac0cf33..48155466 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -24,11 +24,11 @@ if ( { $POWERLINE_CONFIG shell --shell=tcsh uses prompt } ) then if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then alias _powerline_above true else - alias _powerline_above '$POWERLINE_COMMAND shell above --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' + alias _powerline_above '$POWERLINE_COMMAND shell above --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' endif - alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' - alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' From 3c243e1aa8a075b2d0f6dc1be5a81dfd77d97811 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 18:10:03 +0400 Subject: [PATCH 1220/1472] Rename top_theme_config to main_theme_config Reason: preventing name conflict (top-level theme is a theme from #783). --- powerline/renderers/ipython.py | 2 +- powerline/renderers/vim.py | 2 +- powerline/renderers/zsh_prompt.py | 2 +- powerline/theme.py | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index a2c071cc..cf31e119 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -21,7 +21,7 @@ class IpythonRenderer(ShellRenderer): except KeyError: match['theme'] = Theme( theme_config=match['config'], - top_theme_config=self.theme_config, + main_theme_config=self.theme_config, **self.theme_kwargs ) return match['theme'] diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 28ff2202..f4c2bf74 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -57,7 +57,7 @@ class VimRenderer(Renderer): try: return match['theme'] except KeyError: - match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) + match['theme'] = Theme(theme_config=match['config'], main_theme_config=self.theme_config, **self.theme_kwargs) return match['theme'] def get_theme(self, matcher_info): diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index fcbe3e1c..683d2364 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -51,7 +51,7 @@ class ZshPromptRenderer(ShellRenderer): except KeyError: match['theme'] = Theme( theme_config=match['config'], - top_theme_config=self.theme_config, + main_theme_config=self.theme_config, **self.theme_kwargs ) return match['theme'] diff --git a/powerline/theme.py b/powerline/theme.py index 9c61ddc7..dbb4f3f5 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -28,7 +28,7 @@ class Theme(object): theme_config, common_config, pl, - top_theme_config=None, + main_theme_config=None, run_once=False, shutdown_event=None): self.dividers = theme_config['dividers'] @@ -45,8 +45,8 @@ class Theme(object): } self.pl = pl theme_configs = [theme_config] - if top_theme_config: - theme_configs.append(top_theme_config) + if main_theme_config: + theme_configs.append(main_theme_config) get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module')) for segdict in itertools.chain((theme_config['segments'],), theme_config['segments'].get('above', ())): From 50e4c1d1e87ed64b8b87db88ec15ccb21367eca7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 18:15:14 +0400 Subject: [PATCH 1221/1472] Move ZshPromptRenderer.render method to ShellRenderer --- powerline/renderers/shell.py | 30 ++++++++++++++++++++++++++++++ powerline/renderers/zsh_prompt.py | 28 ---------------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 5358871c..dd9906a8 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -23,6 +23,36 @@ class ShellRenderer(Renderer): character_translations = Renderer.character_translations.copy() + def __init__(self, *args, **kwargs): + super(ShellRenderer, self).__init__(*args, **kwargs) + self.old_widths = {} + + def render(self, segment_info, *args, **kwargs): + client_id = segment_info.get('client_id') + key = (client_id, kwargs.get('side')) + kwargs = kwargs.copy() + width = kwargs.pop('width', None) + local_theme = segment_info.get('local_theme') + if client_id and local_theme: + output_raw = False + try: + width = self.old_widths[key] + except KeyError: + pass + else: + output_raw = True + ret = super(ShellRenderer, self).render( + output_raw=output_raw, + width=width, + matcher_info=local_theme, + segment_info=segment_info, + *args, **kwargs + ) + if output_raw: + self.old_widths[key] = len(ret[1]) + ret = ret[0] + return ret + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index 683d2364..b0bcede6 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -14,34 +14,6 @@ class ZshPromptRenderer(ShellRenderer): character_translations = ShellRenderer.character_translations.copy() character_translations[ord('%')] = '%%' - old_widths = {} - - def render(self, segment_info, *args, **kwargs): - client_id = segment_info.get('client_id') - key = (client_id, kwargs.get('side')) - kwargs = kwargs.copy() - width = kwargs.pop('width', None) - local_theme = segment_info.get('local_theme') - if client_id and local_theme: - output_raw = False - try: - width = self.old_widths[key] - except KeyError: - pass - else: - output_raw = True - ret = super(ShellRenderer, self).render( - output_raw=output_raw, - width=width, - matcher_info=local_theme, - segment_info=segment_info, - *args, **kwargs - ) - if output_raw: - self.old_widths[key] = len(ret[1]) - ret = ret[0] - return ret - def get_theme(self, matcher_info): if not matcher_info: return self.theme From fcc397100ef5a57e8df35c79cd9d332ccd31c48f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 18:19:48 +0400 Subject: [PATCH 1222/1472] Wait for screen to initialize, based on screen exit status Otherwise bash tests tend to fail randomly --- tests/test_shells/test.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index b7302d89..6e12d257 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -54,10 +54,14 @@ run_test() { run screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" - screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH + while ! screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH ; do + sleep 0.1s + done # Wait for screen to initialize sleep 1 - screen -S "$SESNAME" -p 0 -X width 300 1 + while ! screen -S "$SESNAME" -p 0 -X width 300 1 ; do + sleep 0.1s + done if test "x${SH}" = "xdash" ; then # If I do not use this hack for dash then output will look like # From 72b7744b98d97f10650535423beed6b7c688d6cf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 18:28:32 +0400 Subject: [PATCH 1223/1472] Split Renderer.render() into .render() and .do_render() --- powerline/renderer.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 6ee245aa..c7e55883 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -219,8 +219,20 @@ class Renderer(object): Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) + return self.do_render( + mode=mode, + width=width, + side=side, + line=line, + output_raw=output_raw, + segment_info=segment_info, + theme=theme, + ) + def do_render(self, mode, width, side, line, output_raw, segment_info, theme): + '''Like Renderer.render(), but accept theme in place of matcher_info + ''' + segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode segments = [ self._get_highlighting(segment, segment['mode'] or mode) @@ -252,8 +264,6 @@ class Renderer(object): }, } - length = self._render_length(theme, segments, divider_lengths) - # Create an ordered list of segments that can be dropped segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) for segment in segments_priority: From c97ab8055a26a23a29fabe0f69045de00ff99508 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 19:05:53 +0400 Subject: [PATCH 1224/1472] Add output_width used for various shells --- powerline/bindings/ipython/post_0_11.py | 11 ++-- powerline/bindings/ipython/pre_0_11.py | 15 +++--- powerline/renderer.py | 72 +++++++++++++++++-------- powerline/renderers/ipython.py | 4 ++ powerline/renderers/shell.py | 10 ++-- 5 files changed, 74 insertions(+), 38 deletions(-) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 92f0887c..f10006b9 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -27,15 +27,16 @@ class PowerlinePromptManager(PromptManager): powerline = self.non_prompt_powerline else: powerline = self.prompt_powerline - res, res_nocolor = powerline.render( - output_raw=True, + res = powerline.render( + output_width=True, + output_raw=not color, width=width, matcher_info=name, segment_info=self.powerline_segment_info, ) - self.txtwidth = len(res_nocolor) - self.width = self.txtwidth - ret = res if color else res_nocolor + self.txtwidth = res[-1] + self.width = res[-1] + ret = res[0] if color else res[1] if name == 'rewrite': return RewriteResult(ret) else: diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index a007cdb0..e10e4d5c 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -33,11 +33,14 @@ class PowerlinePrompt(BasePrompt): return string(self.p_str) def set_p_str(self, width=None): - self.p_str, self.p_str_nocolor = ( - self.powerline.render(output_raw=True, - segment_info=self.powerline_segment_info, - matcher_info=self.powerline_prompt_type, - width=width) + self.p_str, self.p_str_nocolor, self.powerline_prompt_width = ( + self.powerline.render( + output_raw=True, + output_width=True, + segment_info=self.powerline_segment_info, + matcher_info=self.powerline_prompt_type, + width=width + ) ) @staticmethod @@ -58,7 +61,7 @@ class PowerlinePrompt1(PowerlinePrompt): def set_p_str(self): super(PowerlinePrompt1, self).set_p_str() self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group()) - self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces + self.prompt_text_len = self.powerline_prompt_width - self.nrspaces self.powerline_last_in['nrspaces'] = self.nrspaces self.powerline_last_in['prompt_text_len'] = self.prompt_text_len diff --git a/powerline/renderer.py b/powerline/renderer.py index c7e55883..63bc1454 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -15,11 +15,15 @@ except ImportError: pass -def construct_returned_value(rendered_highlighted, segments, output_raw): - if output_raw: - return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) - else: +def construct_returned_value(rendered_highlighted, segments, width, output_raw, output_width): + if not (output_raw or output_width): return rendered_highlighted + else: + return ( + (rendered_highlighted,) + + ((''.join((segment['_rendered_raw'] for segment in segments)),) if output_raw else ()) + + ((width,) if output_width else ()) + ) class Renderer(object): @@ -188,7 +192,7 @@ class Renderer(object): for line in range(theme.get_line_number() - 1, 0, -1): yield self.render(side=None, line=line, **kwargs) - def render(self, mode=None, width=None, side=None, line=0, output_raw=False, segment_info=None, matcher_info=None): + def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -213,6 +217,11 @@ class Renderer(object): Changes the output: if this parameter is ``True`` then in place of one string this method outputs a pair ``(colored_string, colorless_string)``. + :param bool output_width: + Changes the output: if this parameter is ``True`` then in place of + one string this method outputs a pair ``(colored_string, + string_width)``. Returns a three-tuple if ``output_raw`` is also + ``True``: ``(colored_string, colorless_string, string_width)``. :param dict segment_info: Segment information. See also ``.get_segment_info()`` method. :param matcher_info: @@ -225,11 +234,24 @@ class Renderer(object): side=side, line=line, output_raw=output_raw, + output_width=output_width, segment_info=segment_info, theme=theme, ) - def do_render(self, mode, width, side, line, output_raw, segment_info, theme): + def compute_divider_widths(self, theme): + return { + 'left': { + 'hard': self.strwidth(theme.get_divider('left', 'hard')), + 'soft': self.strwidth(theme.get_divider('left', 'soft')), + }, + 'right': { + 'hard': self.strwidth(theme.get_divider('right', 'hard')), + 'soft': self.strwidth(theme.get_divider('right', 'soft')), + }, + } + + def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme): '''Like Renderer.render(), but accept theme in place of matcher_info ''' segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) @@ -246,35 +268,36 @@ class Renderer(object): ) ] + current_width = 0 + if not width: # No width specified, so we don't need to crop or pad anything + if output_width: + current_width = self._render_length(theme, segments, self.compute_divider_widths(theme)) return construct_returned_value(''.join([ segment['_rendered_hl'] for segment in self._render_segments(theme, segments) - ]) + self.hlstyle(), segments, output_raw) + ]) + self.hlstyle(), segments, current_width, output_raw, output_width) - divider_lengths = { - 'left': { - 'hard': self.strwidth(theme.get_divider('left', 'hard')), - 'soft': self.strwidth(theme.get_divider('left', 'soft')), - }, - 'right': { - 'hard': self.strwidth(theme.get_divider('right', 'hard')), - 'soft': self.strwidth(theme.get_divider('right', 'soft')), - }, - } + divider_widths = self.compute_divider_widths(theme) # Create an ordered list of segments that can be dropped segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) for segment in segments_priority: - if self._render_length(theme, segments, divider_lengths) <= width: + current_width = self._render_length(theme, segments, divider_widths) + if current_width <= width: break segments.remove(segment) # Distribute the remaining space on spacer segments segments_spacers = [segment for segment in segments if segment['width'] == 'auto'] if segments_spacers: - distribute_len, distribute_len_remainder = divmod(width - sum([segment['_len'] for segment in segments]), len(segments_spacers)) + if not segments_priority: + # Update segment['_len'] and current_width if not already done + # (is not done in shells where there is nothing to remove + # because “priority” key is not specified) + current_width = self._render_length(theme, segments, divider_widths) + distribute_len, distribute_len_remainder = divmod(width - current_width, len(segments_spacers)) for segment in segments_spacers: if segment['align'] == 'l': segment['_space_right'] += distribute_len @@ -285,12 +308,17 @@ class Renderer(object): segment['_space_left'] += space_side + space_side_remainder segment['_space_right'] += space_side segments_spacers[0]['_space_right'] += distribute_len_remainder + # `_len` key is not needed anymore, but current_width should have an + # actual value for various bindings. + current_width = width + elif output_width: + current_width = self._render_length(theme, segments, divider_widths) rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() - return construct_returned_value(rendered_highlighted, segments, output_raw) + return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width) - def _render_length(self, theme, segments, divider_lengths): + def _render_length(self, theme, segments, divider_widths): '''Update segments lengths and return them ''' segments_len = len(segments) @@ -316,7 +344,7 @@ class Renderer(object): draw_divider = segment['draw_' + divider_type + '_divider'] segment_len += segment['_space_left'] + segment['_space_right'] + outer_padding if draw_divider: - segment_len += divider_lengths[side][divider_type] + divider_spaces + segment_len += divider_widths[side][divider_type] + divider_spaces segment['_len'] = segment_len ret += segment_len diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index cf31e119..7d7e4c55 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -32,5 +32,9 @@ class IpythonRenderer(ShellRenderer): if 'theme' in match: match['theme'].shutdown() + def render(self, *args, **kwargs): + # XXX super(ShellRenderer), *not* super(IpythonRenderer) + return super(ShellRenderer, self).render(*args, **kwargs) + renderer = IpythonRenderer diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index dd9906a8..931c1178 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -34,22 +34,22 @@ class ShellRenderer(Renderer): width = kwargs.pop('width', None) local_theme = segment_info.get('local_theme') if client_id and local_theme: - output_raw = False + output_width = False try: width = self.old_widths[key] except KeyError: pass else: - output_raw = True + output_width = True ret = super(ShellRenderer, self).render( - output_raw=output_raw, + output_width=output_width, width=width, matcher_info=local_theme, segment_info=segment_info, *args, **kwargs ) - if output_raw: - self.old_widths[key] = len(ret[1]) + if output_width: + self.old_widths[key] = ret[1] ret = ret[0] return ret From 547306e746eac3a621b2c6eec6810953fb2d4f4a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 20:24:02 +0400 Subject: [PATCH 1225/1472] Add cursor_space and cursor_columns configuration options Fixes #815 --- docs/source/configuration/reference.rst | 10 +++++ powerline/bindings/bash/powerline.sh | 2 +- powerline/bindings/ipython/post_0_11.py | 12 ++--- powerline/bindings/ipython/pre_0_11.py | 26 ++++++----- powerline/bindings/shell/powerline.sh | 2 +- powerline/bindings/zsh/powerline.zsh | 6 +-- powerline/ipython.py | 7 ++- powerline/lint/__init__.py | 2 + powerline/renderers/shell.py | 60 +++++++++++++++++-------- powerline/theme.py | 6 +++ tests/test_provided_config_files.py | 4 +- 11 files changed, 93 insertions(+), 44 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 8f043e43..63181e44 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -283,6 +283,16 @@ ascii Theme without any unicode characters at all background colors, while the ``soft`` dividers are used to divide segments with the same background color. +.. _config-themes-cursor_space: + +``cursor_space`` + Space reserved for user input in shell bindings. It is measured in per + cents. + +``cursor_columns`` + Space reserved for user input in shell bindings. Unlike :ref:`cursor_space + ` it is measured in absolute amout of columns. + .. _config-themes-segment_data: ``segment_data`` diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index b267c076..93670555 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -42,7 +42,7 @@ _powerline_init_tmux_support() { _powerline_prompt() { # Arguments: side, last_exit_code, jobnum $POWERLINE_COMMAND shell $1 \ - -w "${COLUMNS:-$(_powerline_columns_fallback)}" \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ -r bash_prompt \ --last_exit_code=$2 \ --jobnum=$3 \ diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index f10006b9..ec3a4769 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -22,15 +22,14 @@ class PowerlinePromptManager(PromptManager): self.shell = shell def render(self, name, color=True, *args, **kwargs): - width = None if name == 'in' else self.width if name == 'out' or name == 'rewrite': powerline = self.non_prompt_powerline else: powerline = self.prompt_powerline res = powerline.render( + side='left', output_width=True, output_raw=not color, - width=width, matcher_info=name, segment_info=self.powerline_segment_info, ) @@ -44,12 +43,12 @@ class PowerlinePromptManager(PromptManager): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, ip, is_prompt): + def __init__(self, ip, is_prompt, old_widths): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) self.paths = config.get('paths') - super(ConfigurableIpythonPowerline, self).__init__(is_prompt) + super(ConfigurableIpythonPowerline, self).__init__(is_prompt, old_widths) old_prompt_manager = None @@ -59,8 +58,9 @@ def load_ipython_extension(ip): global old_prompt_manager old_prompt_manager = ip.prompt_manager - prompt_powerline = ConfigurableIpythonPowerline(ip, True) - non_prompt_powerline = ConfigurableIpythonPowerline(ip, False) + old_widths = {} + prompt_powerline = ConfigurableIpythonPowerline(ip, True, old_widths) + non_prompt_powerline = ConfigurableIpythonPowerline(ip, False, old_widths) ip.prompt_manager = PowerlinePromptManager( prompt_powerline=prompt_powerline, diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index e10e4d5c..ed6ad75e 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -32,14 +32,14 @@ class PowerlinePrompt(BasePrompt): self.set_p_str() return string(self.p_str) - def set_p_str(self, width=None): + def set_p_str(self): self.p_str, self.p_str_nocolor, self.powerline_prompt_width = ( self.powerline.render( + side='left', output_raw=True, output_width=True, segment_info=self.powerline_segment_info, matcher_info=self.powerline_prompt_type, - width=width ) ) @@ -61,20 +61,21 @@ class PowerlinePrompt1(PowerlinePrompt): def set_p_str(self): super(PowerlinePrompt1, self).set_p_str() self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group()) - self.prompt_text_len = self.powerline_prompt_width - self.nrspaces self.powerline_last_in['nrspaces'] = self.nrspaces - self.powerline_last_in['prompt_text_len'] = self.prompt_text_len def auto_rewrite(self): - return RewriteResult(self.other_powerline.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) - + (' ' * self.nrspaces)) + return RewriteResult(self.other_powerline.render( + side='left', + matcher_info='rewrite', + segment_info=self.powerline_segment_info) + (' ' * self.nrspaces) + ) class PowerlinePromptOut(PowerlinePrompt): powerline_prompt_type = 'out' def set_p_str(self): - super(PowerlinePromptOut, self).set_p_str(width=self.powerline_last_in['prompt_text_len']) + super(PowerlinePromptOut, self).set_p_str() spaces = ' ' * self.powerline_last_in['nrspaces'] self.p_str += spaces self.p_str_nocolor += spaces @@ -85,21 +86,22 @@ class PowerlinePrompt2(PowerlinePromptOut): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, is_prompt, config_overrides=None, theme_overrides={}, paths=None): + def __init__(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides self.paths = paths - super(ConfigurableIpythonPowerline, self).__init__(is_prompt) + super(ConfigurableIpythonPowerline, self).__init__(is_prompt, old_widths) def setup(**kwargs): ip = get_ipython() - prompt_powerline = ConfigurableIpythonPowerline(True, **kwargs) - non_prompt_powerline = ConfigurableIpythonPowerline(False, **kwargs) + old_widths = {} + prompt_powerline = ConfigurableIpythonPowerline(True, old_widths, **kwargs) + non_prompt_powerline = ConfigurableIpythonPowerline(False, old_widths, **kwargs) def late_startup_hook(): - last_in = {'nrspaces': 0, 'prompt_text_len': None} + last_in = {'nrspaces': 0} for attr, prompt_class, powerline, other_powerline in ( ('prompt1', PowerlinePrompt1, prompt_powerline, non_prompt_powerline), ('prompt2', PowerlinePrompt2, prompt_powerline, None), diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index 89da8d8f..d197c5be 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -135,7 +135,7 @@ _powerline_prompt() { # Arguments: side, exit_code _powerline_set_jobs $POWERLINE_COMMAND shell $1 \ - -w "${COLUMNS:-$(_powerline_columns_fallback)}" \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ $_POWERLINE_RENDERER_ARG \ --renderer_arg="client_id=$$" \ --last_exit_code=$2 \ diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 626ee58c..631a6752 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -133,9 +133,9 @@ _powerline_setup_prompt() { add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' - local new_args_2=' -R parser_state=${(%%):-%_}' - new_args_2+=' -R local_theme=continuation' - local add_args_3=$add_args' -R local_theme=select' + local new_args_2=' --renderer_arg="parser_state=${(%%):-%_}"' + new_args_2+=' --renderer_arg="local_theme=continuation"' + local add_args_3=$add_args' --renderer_arg="local_theme=select"' local add_args_2=$add_args$new_args_2 add_args+=' --width=$(( ${COLUMNS:-$(_powerline_columns_fallback)} - 1 ))' local add_args_r2=$add_args$new_args_2 diff --git a/powerline/ipython.py b/powerline/ipython.py index 80327e72..5fc66b34 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -23,12 +23,17 @@ class RewriteResult(object): class IpythonPowerline(Powerline): - def __init__(self, is_prompt): + def __init__(self, is_prompt, old_widths): super(IpythonPowerline, self).__init__( 'ipython', renderer_module=('ipython_prompt' if is_prompt else 'ipython'), use_daemon_threads=True ) + self.old_widths = old_widths + + def create_renderer(self, *args, **kwargs): + super(IpythonPowerline, self).create_renderer(*args, **kwargs) + self.renderer.old_widths = self.old_widths def get_config_paths(self): if self.paths: diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 7d894049..a1749790 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1279,6 +1279,8 @@ spaces_spec = Spec().unsigned().cmp( ).copy common_theme_spec = Spec( default_module=segment_module_spec().optional(), + cursor_space=Spec().type(int, float).cmp('le', 100).cmp('gt', 0).optional(), + cursor_columns=Spec().type(int).cmp('gt', 0).optional(), ).context_message('Error while loading theme').copy top_theme_spec = common_theme_spec().update( dividers=dividers_spec(), diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 931c1178..360acafc 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, unicode_literals, division, print_function from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE @@ -27,31 +27,55 @@ class ShellRenderer(Renderer): super(ShellRenderer, self).__init__(*args, **kwargs) self.old_widths = {} - def render(self, segment_info, *args, **kwargs): - client_id = segment_info.get('client_id') - key = (client_id, kwargs.get('side')) - kwargs = kwargs.copy() - width = kwargs.pop('width', None) + def render(self, segment_info, **kwargs): local_theme = segment_info.get('local_theme') - if client_id and local_theme: - output_width = False + return super(ShellRenderer, self).render( + matcher_info=local_theme, + segment_info=segment_info, + **kwargs + ) + + def do_render(self, output_width, segment_info, side, theme, width=None, **kwargs): + if isinstance(segment_info, dict): + client_id = segment_info.get('client_id') + else: + client_id = None + local_key = (client_id, side, None if theme is self.theme else id(theme)) + key = (client_id, side, None) + did_width = False + if local_key[-1] != key[-1] and side == 'left': try: width = self.old_widths[key] except KeyError: pass - else: - output_width = True - ret = super(ShellRenderer, self).render( - output_width=output_width, + else: + did_width = True + if not did_width: + if width is not None: + if theme.cursor_space_multiplier is not None: + width = int(width * theme.cursor_space_multiplier) + elif theme.cursor_columns: + width -= theme.cursor_columns + + if side == 'right': + try: + width -= self.old_widths[(client_id, 'left', local_key[-1])] + except KeyError: + pass + res = super(ShellRenderer, self).do_render( + output_width=True, width=width, - matcher_info=local_theme, + theme=theme, segment_info=segment_info, - *args, **kwargs + side=side, + **kwargs ) - if output_width: - self.old_widths[key] = ret[1] - ret = ret[0] - return ret + self.old_widths[local_key] = res[-1] + ret = res if output_width else res[:-1] + if len(ret) == 1: + return ret[0] + else: + return ret def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. diff --git a/powerline/theme.py b/powerline/theme.py index dbb4f3f5..d03b0a06 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import division from powerline.segment import gen_segment_getter, process_segment from powerline.lib.unicode import u @@ -37,6 +38,11 @@ class Theme(object): for k, v in val.items())) for key, val in self.dividers.items() )) + try: + self.cursor_space_multiplier = 1 - (theme_config['cursor_space'] / 100) + except KeyError: + self.cursor_space_multiplier = None + self.cursor_columns = theme_config.get('cursor_columns') self.spaces = theme_config['spaces'] self.segments = [] self.EMPTY_SEGMENT = { diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index e738301f..45cffcf6 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -118,11 +118,11 @@ class TestConfig(TestCase): segment_info = Args(prompt_count=1) - with IpyPowerline(True) as powerline: + with IpyPowerline(True, {}) as powerline: for prompt_type in ['in', 'in2']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) - with IpyPowerline(False) as powerline: + with IpyPowerline(False, {}) as powerline: for prompt_type in ['out', 'rewrite']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) From eeb3b5a1ce4155283b717e45526deb1b67e445ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 20:35:56 +0400 Subject: [PATCH 1226/1472] Do not repeat renderer module argument more then once --- powerline/bindings/zsh/powerline.zsh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 631a6752..0bc2c50e 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -129,7 +129,8 @@ _powerline_setup_prompt() { POWERLINE_COMMAND=( "$($POWERLINE_CONFIG shell command)" ) fi - local add_args='--last_exit_code=$?' + local add_args='-r zsh_prompt' + add_args+=' --last_exit_code=$?' add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' @@ -139,11 +140,11 @@ _powerline_setup_prompt() { local add_args_2=$add_args$new_args_2 add_args+=' --width=$(( ${COLUMNS:-$(_powerline_columns_fallback)} - 1 ))' local add_args_r2=$add_args$new_args_2 - PS1='$($POWERLINE_COMMAND shell aboveleft -r zsh_prompt '$add_args')' - RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' - PS2='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_2')' - RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args_r2')' - PS3='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_3')' + PS1='$($POWERLINE_COMMAND shell aboveleft '$add_args')' + RPS1='$($POWERLINE_COMMAND shell right '$add_args')' + PS2='$($POWERLINE_COMMAND shell left '$add_args_2')' + RPS2='$($POWERLINE_COMMAND shell right '$add_args_r2')' + PS3='$($POWERLINE_COMMAND shell left '$add_args_3')' fi } From 4f3a682de20b67262309e1e7dfc0d2285c8772a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Aug 2014 20:50:42 +0400 Subject: [PATCH 1227/1472] Add priorities to ipython and shell themes --- powerline/config_files/themes/ipython/in.json | 3 ++- .../config_files/themes/shell/default.json | 21 ++++++++++++------- .../themes/shell/default_leftonly.json | 21 ++++++++++++------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/powerline/config_files/themes/ipython/in.json b/powerline/config_files/themes/ipython/in.json index 3d471ec5..c0086509 100644 --- a/powerline/config_files/themes/ipython/in.json +++ b/powerline/config_files/themes/ipython/in.json @@ -3,7 +3,8 @@ "segments": { "left": [ { - "name": "virtualenv" + "name": "virtualenv", + "priority": 10 }, { "type": "string", diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 25867a30..56e3ce70 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -7,29 +7,36 @@ "name": "mode" }, { - "name": "hostname" + "name": "hostname", + "priority": 10 }, { - "name": "user" + "name": "user", + "priority": 30 }, { - "name": "virtualenv" + "name": "virtualenv", + "priority": 50 }, { - "name": "cwd" + "name": "cwd", + "priority": 10 }, { "module": "powerline.segments.shell", - "name": "jobnum" + "name": "jobnum", + "priority": 20 } ], "right": [ { "module": "powerline.segments.shell", - "name": "last_pipe_status" + "name": "last_pipe_status", + "priority": 10 }, { - "name": "branch" + "name": "branch", + "priority": 40 } ] } diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index d6c49de1..ca7dd15d 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -3,27 +3,34 @@ "segments": { "left": [ { - "name": "hostname" + "name": "hostname", + "priority": 10 }, { - "name": "user" + "name": "user", + "priority": 30 }, { - "name": "virtualenv" + "name": "virtualenv", + "priority": 50 }, { - "name": "branch" + "name": "branch", + "priority": 40 }, { - "name": "cwd" + "name": "cwd", + "priority": 10 }, { "module": "powerline.segments.shell", - "name": "jobnum" + "name": "jobnum", + "priority": 20 }, { + "module": "powerline.segments.shell", "name": "last_status", - "module": "powerline.segments.shell" + "priority": 10 } ] } From 89afac44bbee564aa1d09aff89b0be05fef5a67d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Aug 2014 07:54:34 +0400 Subject: [PATCH 1228/1472] Add support for PS2 and PS3 prompts outside of zsh --- docs/source/usage/shell-prompts.rst | 46 ++++++++++++++++++++++++++ powerline/bindings/bash/powerline.sh | 18 ++++++++++ powerline/bindings/shell/powerline.sh | 47 +++++++++++++++++++++++++++ powerline/renderers/shell.py | 15 +++++++++ powerline/renderers/zsh_prompt.py | 15 --------- powerline/segments/shell.py | 12 ++++--- tests/test_shells/bash.daemon.ok | 28 ++++++++++++++++ tests/test_shells/bash.nodaemon.ok | 28 ++++++++++++++++ tests/test_shells/bash.ok | 18 ---------- tests/test_shells/busybox.daemon.ok | 27 +++++++++++++++ tests/test_shells/busybox.nodaemon.ok | 27 +++++++++++++++ tests/test_shells/busybox.ok | 17 ---------- tests/test_shells/dash.daemon.ok | 26 +++++++++++++++ tests/test_shells/dash.nodaemon.ok | 26 +++++++++++++++ tests/test_shells/dash.ok | 17 ---------- tests/test_shells/input.bash | 6 ++++ tests/test_shells/input.busybox | 6 ++++ tests/test_shells/input.dash | 6 ++++ tests/test_shells/input.mksh | 7 ++++ tests/test_shells/mksh.daemon.ok | 30 +++++++++++++++++ tests/test_shells/mksh.nodaemon.ok | 30 +++++++++++++++++ tests/test_shells/mksh.ok | 19 ----------- tests/test_shells/postproc.py | 5 +++ tests/test_shells/test.sh | 14 ++++++-- 24 files changed, 398 insertions(+), 92 deletions(-) create mode 100644 tests/test_shells/bash.daemon.ok create mode 100644 tests/test_shells/bash.nodaemon.ok delete mode 100644 tests/test_shells/bash.ok create mode 100644 tests/test_shells/busybox.daemon.ok create mode 100644 tests/test_shells/busybox.nodaemon.ok delete mode 100644 tests/test_shells/busybox.ok create mode 100644 tests/test_shells/dash.daemon.ok create mode 100644 tests/test_shells/dash.nodaemon.ok delete mode 100644 tests/test_shells/dash.ok create mode 100644 tests/test_shells/mksh.daemon.ok create mode 100644 tests/test_shells/mksh.nodaemon.ok delete mode 100644 tests/test_shells/mksh.ok diff --git a/docs/source/usage/shell-prompts.rst b/docs/source/usage/shell-prompts.rst index e2dbc1d6..75fe7a3e 100644 --- a/docs/source/usage/shell-prompts.rst +++ b/docs/source/usage/shell-prompts.rst @@ -2,6 +2,16 @@ Shell prompts ************* +.. note:: + Powerline daemon is not run automatically by any of my bindings. It is + advised that you add + + .. code-block:: bash + + powerline-daemon -q + + before any other powerline-related code in your shell configuration file. + Bash prompt =========== @@ -12,6 +22,28 @@ the absolute path to your Powerline installation directory: . {repository_root}/powerline/bindings/bash/powerline.sh +.. note:: + Since without powerline daemon bash bindings are very slow PS2 + (continuation) and PS3 (select) prompts are not set up. Thus it is advised + to use + + .. code-block:: bash + + powerline-daemon -q + POWERLINE_BASH_CONTINUATION=1 + POWERLINE_BASH_SELECT=1 + . {repository_root}/powerline/bindings/bash/powerline.sh + + in your bash configuration file. Without ``POWERLINE_BASH_*`` variables PS2 + and PS3 prompts are computed exactly once at bash startup. + +.. warning:: + At maximum bash continuation PS2 and select PS3 prompts are computed each + time main PS1 prompt is computed. Do not expect it to work properly if you + e.g. put current time there. + + At minimum they are computed once on startup. + Zsh prompt ========== @@ -59,6 +91,20 @@ following in ``~/.profile``: will source (using ``.`` command) both former ``$ENV`` file and :file:`powerline.sh` files and set ``$ENV`` to the path of this new file. +.. warning:: + Mksh users have to set ``$POWERLINE_SHELL_CONTINUATION`` and + ``$POWERLINE_SHELL_SELECT`` to 1 to get PS2 and PS3 (continuation and + select) prompts support respectively: as command substitution is not + performed in these shells for these prompts they are updated once each time + PS1 prompt is displayed which may be slow. + + It is also known that while PS2 and PS3 update is triggered at PS1 update it + is *actually performed* only *next* time PS1 is displayed which means that + PS2 and PS3 prompts will be outdated and may be incorrect for this reason. + + Without these variables PS2 and PS3 prompts will be set once at startup. + This only touches mksh users: busybox and dash both have no such problem. + .. warning:: Job count is using some weird hack that uses signals and temporary files for interprocess communication. It may be wrong sometimes. Not the case in mksh. diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 93670555..e44d9da2 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -39,6 +39,16 @@ _powerline_init_tmux_support() { fi } +_powerline_local_prompt() { + # Arguments: side, renderer_module arg, last_exit_code, jobnum, local theme + $POWERLINE_COMMAND shell $1 \ + $2 \ + --last_exit_code=$3 \ + --jobnum=$4 \ + --renderer_arg="client_id=$$" \ + --renderer_arg="local_theme=$5" +} + _powerline_prompt() { # Arguments: side, last_exit_code, jobnum $POWERLINE_COMMAND shell $1 \ @@ -53,6 +63,12 @@ _powerline_set_prompt() { local last_exit_code=$? local jobnum="$(jobs -p|wc -l)" PS1="$(_powerline_prompt aboveleft $last_exit_code $jobnum)" + if test -n "$POWERLINE_SHELL_CONTINUATION$POWERLINE_BASH_CONTINUATION" ; then + PS2="$(_powerline_local_prompt left -rbash_prompt $last_exit_code $jobnum continuation)" + fi + if test -n "$POWERLINE_SHELL_SELECT$POWERLINE_BASH_SELECT" ; then + PS3="$(_powerline_local_prompt left '' $last_exit_code $jobnum select)" + fi return $last_exit_code } @@ -63,6 +79,8 @@ _powerline_setup_prompt() { fi test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || PROMPT_COMMAND=$'_powerline_set_prompt\n'"${PROMPT_COMMAND}" + PS2="$(_powerline_local_prompt left -rbash_prompt 0 0 continuation)" + PS3="$(_powerline_local_prompt left '' 0 0 select)" } if test -z "${POWERLINE_CONFIG}" ; then diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index d197c5be..3fc2b434 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -41,6 +41,8 @@ _powerline_set_append_trap() { if echo "$_powerline_traps" | grep -cm1 $2'$' >/dev/null ; then _powerline_traps="$(echo "$_powerline_traps" | sed "s/ $2/'\\n$1' $2/")" eval "$_powerline_traps" + else + trap "$1" $2 fi } else @@ -131,6 +133,17 @@ _powerline_set_jobs() { _powerline_set_jobs } +_powerline_local_prompt() { + # Arguments: side, exit_code, local theme + _powerline_set_jobs + $POWERLINE_COMMAND shell $1 \ + $_POWERLINE_RENDERER_ARG \ + --renderer_arg="client_id=$$" \ + --last_exit_code=$2 \ + --jobnum=$_POWERLINE_JOBS \ + --renderer_arg="local_theme=$3" +} + _powerline_prompt() { # Arguments: side, exit_code _powerline_set_jobs @@ -140,6 +153,37 @@ _powerline_prompt() { --renderer_arg="client_id=$$" \ --last_exit_code=$2 \ --jobnum=$_POWERLINE_JOBS + _powerline_update_psN +} + +_powerline_setup_psN() { + case "$1" in + mksh|ksh|bash) + _POWERLINE_PID=$$ + _powerline_update_psN() { + kill -USR1 $_POWERLINE_PID + } + # No command substitution in PS2 and PS3 + _powerline_set_psN() { + if test -n "$POWERLINE_SHELL_CONTINUATION" ; then + PS2="$(_powerline_local_prompt left $? continuation)" + fi + if test -n "$POWERLINE_SHELL_SELECT" ; then + PS3="$(_powerline_local_prompt left $? select)" + fi + } + _powerline_append_trap '_powerline_set_psN' USR1 + _powerline_set_psN + ;; + bb|ash|dash) + _powerline_update_psN() { + # Do nothing + return + } + PS2='$(_powerline_local_prompt left $? continuation)' + # No select support + ;; + esac } _powerline_setup_prompt() { @@ -149,6 +193,9 @@ _powerline_setup_prompt() { _powerline_set_command "$@" _powerline_set_renderer_arg "$@" PS1='$(_powerline_prompt aboveleft $?)' + PS2="$(_powerline_local_prompt left 0 continuation)" + PS3="$(_powerline_local_prompt left 0 select)" + _powerline_setup_psN "$@" } _powerline_init_tmux_support() { diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index 360acafc..fdffaa25 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals, division, print_function from powerline.renderer import Renderer +from powerline.theme import Theme from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE @@ -120,5 +121,19 @@ class ShellRenderer(Renderer): r = '\033P' + r.replace('\033', '\033\033') + '\033\\' return self.escape_hl_start + r + self.escape_hl_end + def get_theme(self, matcher_info): + if not matcher_info: + return self.theme + match = self.local_themes[matcher_info] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + main_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + renderer = ShellRenderer diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index b0bcede6..47e76cc3 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, unicode_literals from powerline.renderers.shell import ShellRenderer -from powerline.theme import Theme class ZshPromptRenderer(ShellRenderer): @@ -14,19 +13,5 @@ class ZshPromptRenderer(ShellRenderer): character_translations = ShellRenderer.character_translations.copy() character_translations[ord('%')] = '%%' - def get_theme(self, matcher_info): - if not matcher_info: - return self.theme - match = self.local_themes[matcher_info] - try: - return match['theme'] - except KeyError: - match['theme'] = Theme( - theme_config=match['config'], - main_theme_config=self.theme_config, - **self.theme_kwargs - ) - return match['theme'] - renderer = ZshPromptRenderer diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index 8afc20a1..a13fa09f 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -89,7 +89,11 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename Highlight groups used: ``continuation``, ``continuation:current``. ''' if not segment_info.get('parser_state'): - return None + return [{ + 'contents': '', + 'width': 'auto', + 'highlight_group': ['continuation:current', 'continuation'], + }] ret = [] for state in segment_info['parser_state'].split(): @@ -97,7 +101,7 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename if state: ret.append({ 'contents': state, - 'highlight_group': 'continuation', + 'highlight_group': ['continuation'], 'draw_inner_divider': True, }) @@ -111,8 +115,8 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename if right_align: ret[0].update(width='auto', align='r') - ret[-1]['highlight_group'] = 'continuation:current' + ret[-1]['highlight_group'] = ['continuation:current', 'continuation'] else: - ret[-1].update(width='auto', align='l', highlight_group='continuation:current') + ret[-1].update(width='auto', align='l', highlight_group=['continuation:current', 'continuation']) return ret diff --git a/tests/test_shells/bash.daemon.ok b/tests/test_shells/bash.daemon.ok new file mode 100644 index 00000000..1354b641 --- /dev/null +++ b/tests/test_shells/bash.daemon.ok @@ -0,0 +1,28 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bgscript.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" + USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo ' +                                     abc +                                     def +                                     ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/bash.nodaemon.ok b/tests/test_shells/bash.nodaemon.ok new file mode 100644 index 00000000..cb0f6b6d --- /dev/null +++ b/tests/test_shells/bash.nodaemon.ok @@ -0,0 +1,28 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bgscript.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" + USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo ' +   abc +   def +   ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/bash.ok b/tests/test_shells/bash.ok deleted file mode 100644 index 0ee4e9b6..00000000 --- a/tests/test_shells/bash.ok +++ /dev/null @@ -1,18 +0,0 @@ -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh -[1] PID -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1]+ Terminated bgscript.sh -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/busybox.daemon.ok b/tests/test_shells/busybox.daemon.ok new file mode 100644 index 00000000..4a5bf072 --- /dev/null +++ b/tests/test_shells/busybox.daemon.ok @@ -0,0 +1,27 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bgscript.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" + USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo ' +                                     abc +                                     def +                                     ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/busybox.nodaemon.ok b/tests/test_shells/busybox.nodaemon.ok new file mode 100644 index 00000000..1f6f7449 --- /dev/null +++ b/tests/test_shells/busybox.nodaemon.ok @@ -0,0 +1,27 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +[1]+ Terminated bgscript.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" + USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo ' +   abc +   def +   ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/busybox.ok b/tests/test_shells/busybox.ok deleted file mode 100644 index 760d7d03..00000000 --- a/tests/test_shells/busybox.ok +++ /dev/null @@ -1,17 +0,0 @@ -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1]+ Terminated bgscript.sh -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  cd "$DIR1" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/dash.daemon.ok b/tests/test_shells/dash.daemon.ok new file mode 100644 index 00000000..bbf3de59 --- /dev/null +++ b/tests/test_shells/dash.daemon.ok @@ -0,0 +1,26 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1   USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo ' +                                     abc +                                     def +                                     ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/dash.nodaemon.ok b/tests/test_shells/dash.nodaemon.ok new file mode 100644 index 00000000..3a81cfcc --- /dev/null +++ b/tests/test_shells/dash.nodaemon.ok @@ -0,0 +1,26 @@ +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1   USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo ' +   abc +   def +   ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/dash.ok b/tests/test_shells/dash.ok deleted file mode 100644 index 2ad24b25..00000000 --- a/tests/test_shells/dash.ok +++ /dev/null @@ -1,17 +0,0 @@ -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1s -[1] + Terminated bgscript.sh -cd "$DIR1" -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1    HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 47042cf9..e2d69dfa 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -11,6 +11,12 @@ VIRTUAL_ENV= bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +echo ' +abc +def +' cd "$DIR1" cd ../"$DIR2" cd ../'\[\]' diff --git a/tests/test_shells/input.busybox b/tests/test_shells/input.busybox index d5b11b12..b5e94004 100644 --- a/tests/test_shells/input.busybox +++ b/tests/test_shells/input.busybox @@ -11,6 +11,12 @@ VIRTUAL_ENV= bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +echo ' +abc +def +' cd "$DIR1" cd ../"$DIR2" cd ../'\[\]' diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash index d5b11b12..b5e94004 100644 --- a/tests/test_shells/input.dash +++ b/tests/test_shells/input.dash @@ -11,6 +11,12 @@ VIRTUAL_ENV= bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +echo ' +abc +def +' cd "$DIR1" cd ../"$DIR2" cd ../'\[\]' diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh index 0b0b4904..4ac08441 100644 --- a/tests/test_shells/input.mksh +++ b/tests/test_shells/input.mksh @@ -11,6 +11,13 @@ VIRTUAL_ENV= bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1 +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +echo -n +echo ' +abc +def +' cd "$DIR1" cd ../"$DIR2" cd ../'\[\]' diff --git a/tests/test_shells/mksh.daemon.ok b/tests/test_shells/mksh.daemon.ok new file mode 100644 index 00000000..569a62e2 --- /dev/null +++ b/tests/test_shells/mksh.daemon.ok @@ -0,0 +1,30 @@ + +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 +[1] + Terminated bash -c ... +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" + USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo -n +  BRANCH  ⋯  tests  shell  3rd  echo ' +                                     abc +                                     def +                                     ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/mksh.nodaemon.ok b/tests/test_shells/mksh.nodaemon.ok new file mode 100644 index 00000000..a6d94036 --- /dev/null +++ b/tests/test_shells/mksh.nodaemon.ok @@ -0,0 +1,30 @@ + +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh +[1] PID +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 +[1] + Terminated bash -c ... +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.display=false" + USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.user.display=false" +  BRANCH  ⋯  tests  shell  3rd  echo -n +  BRANCH  ⋯  tests  shell  3rd  echo ' +   abc +   def +   ' + +abc +def + +  BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" +  BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" +  BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' +  BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' +  BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' +  BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' +  BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' +  BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' +  BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/mksh.ok b/tests/test_shells/mksh.ok deleted file mode 100644 index 4770aff1..00000000 --- a/tests/test_shells/mksh.ok +++ /dev/null @@ -1,19 +0,0 @@ - -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd .git -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV="$HOME/.virtenvs/some-virtual-environment" -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd  VIRTUAL_ENV= -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  bgscript.sh & waitpid.sh -[1] PID -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  false -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1  1  kill `cat pid` ; sleep 1 -[1] + Terminated bash -c ... -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  cd "$DIR1" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m  cd ../"$DIR2" -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H  cd ../'\[\]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]  cd ../'%%' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%  cd ../'#[bold]' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  false diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index dd12f829..6866dfc1 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -58,4 +58,9 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: # command, in travis it is truncated just after `true`. if line.startswith('[1] + Terminated'): line = '[1] + Terminated bash -c ...\n' + elif shell == 'dash': + # Position of this line is not stable: it may go both before and + # after the next line + if line.startswith('[1] + Terminated'): + continue W.write(line) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 6e12d257..b9b46529 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -22,6 +22,11 @@ run() { if test "x$SH" = "xfish" ; then local_path="${local_path}:/usr/bin:/bin" fi + if test $TEST_TYPE = daemon ; then + local additional_prompts=1 + else + local additional_prompts= + fi env -i \ PATH="$local_path" \ TERM="${TERM}" \ @@ -33,6 +38,8 @@ run() { DIR2="${DIR2}" \ XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" \ IPYTHONDIR="$PWD/tests/shell/ipython_home" \ + POWERLINE_SHELL_CONTINUATION=$additional_prompts \ + POWERLINE_SHELL_SELECT=$additional_prompts \ "$@" } @@ -156,6 +163,9 @@ ln -s "$(which cut)" tests/shell/path ln -s "$(which bc)" tests/shell/path ln -s "$(which expr)" tests/shell/path ln -s "$(which mktemp)" tests/shell/path +ln -s "$(which grep)" tests/shell/path +ln -s "$(which sed)" tests/shell/path +ln -s "$(which rm)" tests/shell/path ln -s ../../test_shells/bgscript.sh tests/shell/path ln -s ../../test_shells/waitpid.sh tests/shell/path for pexe in powerline powerline-config ; do @@ -169,7 +179,7 @@ for pexe in powerline powerline-config ; do fi done -for exe in bash zsh bb busybox fish tcsh mksh dash ipython ; do +for exe in bash zsh busybox fish tcsh mksh dash ipython ; do if which $exe >/dev/null ; then ln -s "$(which $exe)" tests/shell/path fi @@ -177,7 +187,7 @@ done unset ENV -if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbb ; then +if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbusybox ; then powerline-daemon -k || true sleep 1s From 9e37648acf305e00a203c2335592062815406ac2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Aug 2014 08:11:15 +0400 Subject: [PATCH 1229/1472] Remove unneeded keyword argument to player function Fixes #977 --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 7587c063..84a32a15 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -917,7 +917,7 @@ class NowPlayingSegment(Segment): 'elapsed': None, 'total': None, } - func_stats = player_func(state_symbol=state_symbols, **kwargs) + func_stats = player_func(**kwargs) if not func_stats: return None stats.update(func_stats) From 7882583dcbe8222a3b34b1ba7a72909bb5797e51 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Aug 2014 08:16:26 +0400 Subject: [PATCH 1230/1472] Update continuation segment tests --- tests/test_segments.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index 345a6ccc..107d982c 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -62,13 +62,17 @@ class TestShell(TestCase): def test_continuation(self): pl = Pl() - self.assertEqual(shell.continuation(pl=pl, segment_info={}), None) + self.assertEqual(shell.continuation(pl=pl, segment_info={}), [{ + 'contents': '', + 'width': 'auto', + 'highlight_group': ['continuation:current', 'continuation'], + }]) segment_info = {'parser_state': 'if cmdsubst'} self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info), [ { 'contents': 'if', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'l', }, @@ -77,7 +81,7 @@ class TestShell(TestCase): { 'contents': 'if', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'r', }, @@ -86,12 +90,12 @@ class TestShell(TestCase): { 'contents': 'if', 'draw_inner_divider': True, - 'highlight_group': 'continuation', + 'highlight_group': ['continuation'], }, { 'contents': 'cmdsubst', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'l', }, @@ -100,21 +104,21 @@ class TestShell(TestCase): { 'contents': 'if', 'draw_inner_divider': True, - 'highlight_group': 'continuation', + 'highlight_group': ['continuation'], 'width': 'auto', 'align': 'r', }, { 'contents': 'cmdsubst', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], }, ]) self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=True, right_align=True), [ { 'contents': 'if', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'r', }, @@ -123,7 +127,7 @@ class TestShell(TestCase): { 'contents': 'IF', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'r', }, @@ -131,7 +135,7 @@ class TestShell(TestCase): self.assertEqual(shell.continuation(pl=pl, segment_info=segment_info, omit_cmdsubst=True, right_align=True, renames={'if': None}), [ { 'contents': '', - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'r', }, @@ -141,17 +145,17 @@ class TestShell(TestCase): { 'contents': 'then', 'draw_inner_divider': True, - 'highlight_group': 'continuation', + 'highlight_group': ['continuation'], }, { 'contents': 'then', 'draw_inner_divider': True, - 'highlight_group': 'continuation', + 'highlight_group': ['continuation'], }, { 'contents': 'then', 'draw_inner_divider': True, - 'highlight_group': 'continuation:current', + 'highlight_group': ['continuation:current', 'continuation'], 'width': 'auto', 'align': 'l', }, From da867b26a9a98f19119987d26433a6524dbd52e5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Aug 2014 20:33:03 +0400 Subject: [PATCH 1231/1472] Fix VimPowerline.add_local_theme After #783 it started to fail as it made Theme require defining dividers in theme and add_local_theme did not merge in other themes (__main__ and top). --- powerline/__init__.py | 35 +++++++++++++++++++++++++---------- powerline/vim.py | 11 ++++++++++- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index ff568cf1..f78b1135 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -389,9 +389,16 @@ class Powerline(object): if interval is not None and not self.config_loader.is_alive(): self.config_loader.start() - self.default_top_theme = self.common_config['default_top_theme'] - self.ext_config = config['ext'][self.ext] + + self.theme_levels = ( + os.path.join('themes', ( + self.ext_config.get('top_theme') + or self.common_config['default_top_theme'] + )), + os.path.join('themes', self.ext, '__main__'), + ) + if self.ext_config != self.prev_ext_config: ext_config_differs = True if ( @@ -468,8 +475,18 @@ class Powerline(object): ''' return get_config_paths() - def _load_config(self, cfg_path, cfg_type): - '''Load configuration and setup watches.''' + def load_config(self, cfg_path, cfg_type): + '''Load configuration and setup watches + + :param str cfg_path: + Path to the configuration file without any powerline configuration + directory or ``.json`` suffix. + :param str cfg_type: + Configuration type. May be one of ``main`` (for ``config.json`` + file), ``colors``, ``colorscheme``, ``theme``. + + :return: dictionary with loaded configuration. + ''' return load_config( cfg_path, self.find_config_files, @@ -487,7 +504,7 @@ class Powerline(object): :return: dictionary with :ref:`top-level configuration `. ''' - return self._load_config('config', 'main') + return self.load_config('config', 'main') def _load_hierarhical_config(self, cfg_type, levels, ignore_levels): '''Load and merge multiple configuration files @@ -509,7 +526,7 @@ class Powerline(object): exceptions = [] for i, cfg_path in enumerate(levels): try: - lvl_config = self._load_config(cfg_path, cfg_type) + lvl_config = self.load_config(cfg_path, cfg_type) except IOError as e: if sys.version_info < (3,): tb = sys.exc_info()[2] @@ -553,9 +570,7 @@ class Powerline(object): :return: dictionary with :ref:`theme configuration ` ''' - levels = ( - os.path.join('themes', self.ext_config.get('top_theme') or self.default_top_theme), - os.path.join('themes', self.ext, '__main__'), + levels = self.theme_levels + ( os.path.join('themes', self.ext, name), ) return self._load_hierarhical_config('theme', levels, (0, 1,)) @@ -565,7 +580,7 @@ class Powerline(object): :return: dictionary with :ref:`colors configuration `. ''' - return self._load_config('colors', 'colors') + return self.load_config('colors', 'colors') @staticmethod def get_local_themes(local_themes): diff --git a/powerline/vim.py b/powerline/vim.py index c60ee20c..29afa9b9 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -51,8 +51,17 @@ class VimPowerline(Powerline): ''' self.update_renderer() key = self.get_matcher(key) + theme_config = {} + for cfg_path in self.theme_levels: + try: + lvl_config = self.load_config(cfg_path, 'theme') + except IOError: + pass + else: + mergedicts(theme_config, lvl_config) + mergedicts(theme_config, config) try: - self.renderer.add_local_theme(key, {'config': config}) + self.renderer.add_local_theme(key, {'config': theme_config}) except KeyError: return False else: From 1afab26cecfe369f0d56a2b77dad0a2b4b7c1657 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Aug 2014 20:48:49 +0400 Subject: [PATCH 1232/1472] Test that VimPowerline.add_local_theme works --- powerline/vim.py | 3 ++- tests/matchers.py | 7 +++++++ tests/test_configuration.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/matchers.py diff --git a/powerline/vim.py b/powerline/vim.py index 29afa9b9..df009856 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -78,10 +78,11 @@ class VimPowerline(Powerline): 'powerline_theme_overrides__' + name) def get_local_themes(self, local_themes): + self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) + if not local_themes: return {} - self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) return dict(((None if key == '__tabline__' else self.get_matcher(key), {'config': self.load_theme_config(val)}) for key, val in local_themes.items())) diff --git a/tests/matchers.py b/tests/matchers.py new file mode 100644 index 00000000..3937f2f1 --- /dev/null +++ b/tests/matchers.py @@ -0,0 +1,7 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import (unicode_literals, division, absolute_import, print_function) + + +def always_true(matcher_info): + return True diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 6a64ce36..ec05dab4 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -402,6 +402,34 @@ class TestVim(TestCase): vim_module._environ['TEST'] = 'def' self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>') + def test_local_themes(self): + # Regression test: VimPowerline.add_local_theme did not work properly. + from powerline.vim import VimPowerline + import powerline as powerline_module + import vim + with swap_attributes(config, powerline_module): + with get_powerline_raw(config, VimPowerline) as powerline: + powerline.add_local_theme('tests.matchers.always_true', { + 'segment_data': { + 'foo': { + 'contents': '“bar”' + } + }, + 'segments': { + 'left': [ + { + 'type': 'string', + 'name': 'foo', + 'highlight_group': ['g1'] + } + ] + } + }) + window = vim_module.current.window + window_id = 1 + winnr = window.number + self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_5_12583104_6_32896_NONE#\xa0\u201cbar\u201d%#Pl_6_32896_NONE_None_NONE#>>') + def setUpModule(): sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) From 538a41b25a4aae3f98e32602880f2464723a0f9d Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 13 Aug 2014 17:37:11 +0200 Subject: [PATCH 1233/1472] docs: Fix building the docs without RTD theme. Signed-off-by: Andreas Schneider --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 70c104f6..5d0f2161 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,7 +14,7 @@ version = 'beta' release = 'beta' exclude_patterns = ['_build'] pygments_style = 'sphinx' -html_theme = 'sphinx_rtd_theme' +html_theme = 'default' html_static_path = ['_static'] html_show_copyright = False From 8523282a7b2c68ec3594fd971c1c24b2b01e0b92 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 18:01:17 +0400 Subject: [PATCH 1234/1472] Check whether $COLUMNS is empty or zero, not only empty Fixes #983 --- powerline/bindings/fish/powerline-setup.fish | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 6dc100c0..ff0d42bc 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -1,7 +1,7 @@ function powerline-setup function _powerline_columns_fallback - if which stty ^/dev/null - if stty size ^/dev/null + if which stty >/dev/null + if stty size >/dev/null stty size | cut -d' ' -f2 return 0 end @@ -11,7 +11,10 @@ function powerline-setup end function _powerline_columns - if test -z "$COLUMNS" + # Hack: `test "" -eq 0` is true, as well as `test 0 -eq 0` + # Note: at fish startup `$COLUMNS` is equal to zero, meaning that it may + # not be used. + if test "$COLUMNS" -eq 0 _powerline_columns_fallback else echo "$COLUMNS" From 6c187d94d80e632639176936f748ae6b08ea0377 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 18:00:09 +0400 Subject: [PATCH 1235/1472] Call powerline.new_window early under certain conditions Fixes #250 for the GUI. --- powerline/vim.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/powerline/vim.py b/powerline/vim.py index df009856..1e83b2ce 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -218,6 +218,24 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): powerline.update_renderer() __main__.powerline = powerline + if ( + bool(int(vim.eval("has('gui_running') && argc() == 0"))) + and not vim.current.buffer.name + and len(vim.windows) == 1 + ): + # Hack to show startup screen. Problems in GUI: + # - Defining local value of &statusline option while computing global + # value purges startup screen. + # - Defining highlight group while computing statusline purges startup + # screen. + # This hack removes the “while computing statusline” part: both things + # are defined, but they are defined right now. + # + # The above condition disables this hack if no GUI is running, Vim did + # not open any files and there is only one window. Without GUI + # everything works, in other cases startup screen is not shown. + powerline.new_window() + # Cannot have this in one line due to weird newline handling (in :execute # context newline is considered part of the command in just the same cases # when bar is considered part of the command (unless defining function From 63a376e91daa065cd19c63aeec9d03bcefc6bad8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Aug 2014 23:25:39 +0400 Subject: [PATCH 1236/1472] Alter plugin layout: move *.plugin to *.{ext}.plugin I do not see why vim plugins should receive such a generic name as `powerline.segments.plugin.gundo`: taking a name that clearly indicates that `gundo` is a *vim* plugin is better. --- powerline/config_files/config.json | 8 ++++---- powerline/config_files/themes/vim/default.json | 4 ++-- powerline/config_files/themes/vim/plugin_ctrlp.json | 2 +- powerline/config_files/themes/vim/plugin_nerdtree.json | 2 +- powerline/matcher.py | 2 +- powerline/matchers/{vim.py => vim/__init__.py} | 0 powerline/matchers/{ => vim}/plugin/__init__.py | 0 powerline/matchers/{ => vim}/plugin/ctrlp.py | 0 powerline/matchers/{ => vim}/plugin/gundo.py | 0 powerline/matchers/{ => vim}/plugin/nerdtree.py | 0 powerline/segment.py | 3 ++- powerline/segments/{vim.py => vim/__init__.py} | 0 powerline/segments/{ => vim}/plugin/__init__.py | 0 powerline/segments/{ => vim}/plugin/ctrlp.py | 0 powerline/segments/{ => vim}/plugin/nerdtree.py | 0 powerline/segments/{ => vim}/plugin/syntastic.py | 0 powerline/segments/{ => vim}/plugin/tagbar.py | 0 17 files changed, 11 insertions(+), 10 deletions(-) rename powerline/matchers/{vim.py => vim/__init__.py} (100%) rename powerline/matchers/{ => vim}/plugin/__init__.py (100%) rename powerline/matchers/{ => vim}/plugin/ctrlp.py (100%) rename powerline/matchers/{ => vim}/plugin/gundo.py (100%) rename powerline/matchers/{ => vim}/plugin/nerdtree.py (100%) rename powerline/segments/{vim.py => vim/__init__.py} (100%) rename powerline/segments/{ => vim}/plugin/__init__.py (100%) rename powerline/segments/{ => vim}/plugin/ctrlp.py (100%) rename powerline/segments/{ => vim}/plugin/nerdtree.py (100%) rename powerline/segments/{ => vim}/plugin/syntastic.py (100%) rename powerline/segments/{ => vim}/plugin/tagbar.py (100%) diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 4968fc6b..a5389b6c 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -34,10 +34,10 @@ "help": "help", "quickfix": "quickfix", - "powerline.matchers.plugin.nerdtree.nerdtree": "plugin_nerdtree", - "powerline.matchers.plugin.ctrlp.ctrlp": "plugin_ctrlp", - "powerline.matchers.plugin.gundo.gundo": "plugin_gundo", - "powerline.matchers.plugin.gundo.gundo_preview": "plugin_gundo-preview" + "powerline.matchers.vim.plugin.nerdtree.nerdtree": "plugin_nerdtree", + "powerline.matchers.vim.plugin.ctrlp.ctrlp": "plugin_ctrlp", + "powerline.matchers.vim.plugin.gundo.gundo": "plugin_gundo", + "powerline.matchers.vim.plugin.gundo.gundo_preview": "plugin_gundo-preview" } }, "wm": { diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 3998e187..1f9348bb 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -55,13 +55,13 @@ }, { "exclude_modes": ["nc"], - "module": "powerline.segments.plugin.syntastic", + "module": "powerline.segments.vim.plugin.syntastic", "name": "syntastic", "priority": 50 }, { "exclude_modes": ["nc"], - "module": "powerline.segments.plugin.tagbar", + "module": "powerline.segments.vim.plugin.tagbar", "name": "current_tag", "draw_soft_divider": false, "priority": 50 diff --git a/powerline/config_files/themes/vim/plugin_ctrlp.json b/powerline/config_files/themes/vim/plugin_ctrlp.json index 02015b78..ae814c30 100644 --- a/powerline/config_files/themes/vim/plugin_ctrlp.json +++ b/powerline/config_files/themes/vim/plugin_ctrlp.json @@ -1,5 +1,5 @@ { - "default_module": "powerline.segments.plugin.ctrlp", + "default_module": "powerline.segments.vim.plugin.ctrlp", "segments": { "left": [ { diff --git a/powerline/config_files/themes/vim/plugin_nerdtree.json b/powerline/config_files/themes/vim/plugin_nerdtree.json index 95495db1..ee142cde 100644 --- a/powerline/config_files/themes/vim/plugin_nerdtree.json +++ b/powerline/config_files/themes/vim/plugin_nerdtree.json @@ -1,5 +1,5 @@ { - "default_module": "powerline.segments.plugin.nerdtree", + "default_module": "powerline.segments.vim.plugin.nerdtree", "segments": { "left": [ { diff --git a/powerline/matcher.py b/powerline/matcher.py index 5578d282..37049e2f 100644 --- a/powerline/matcher.py +++ b/powerline/matcher.py @@ -13,7 +13,7 @@ def gen_matcher_getter(ext, import_paths): oldpath = sys.path sys.path = import_paths + sys.path try: - return getattr(__import__(match_module, fromlist=[match_function]), match_function) + return getattr(__import__(str(match_module), fromlist=[str(match_function)]), match_function) finally: sys.path = oldpath return get diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim/__init__.py similarity index 100% rename from powerline/matchers/vim.py rename to powerline/matchers/vim/__init__.py diff --git a/powerline/matchers/plugin/__init__.py b/powerline/matchers/vim/plugin/__init__.py similarity index 100% rename from powerline/matchers/plugin/__init__.py rename to powerline/matchers/vim/plugin/__init__.py diff --git a/powerline/matchers/plugin/ctrlp.py b/powerline/matchers/vim/plugin/ctrlp.py similarity index 100% rename from powerline/matchers/plugin/ctrlp.py rename to powerline/matchers/vim/plugin/ctrlp.py diff --git a/powerline/matchers/plugin/gundo.py b/powerline/matchers/vim/plugin/gundo.py similarity index 100% rename from powerline/matchers/plugin/gundo.py rename to powerline/matchers/vim/plugin/gundo.py diff --git a/powerline/matchers/plugin/nerdtree.py b/powerline/matchers/vim/plugin/nerdtree.py similarity index 100% rename from powerline/matchers/plugin/nerdtree.py rename to powerline/matchers/vim/plugin/nerdtree.py diff --git a/powerline/segment.py b/powerline/segment.py index 97538813..cde42398 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -58,8 +58,9 @@ def get_function(data, segment): oldpath = sys.path sys.path = data['path'] + sys.path segment_module = str(segment.get('module', data['default_module'])) + name = str(segment['name']) try: - return None, getattr(__import__(segment_module, fromlist=[segment['name']]), segment['name']), segment_module + return None, getattr(__import__(segment_module, fromlist=[name]), name), segment_module finally: sys.path = oldpath diff --git a/powerline/segments/vim.py b/powerline/segments/vim/__init__.py similarity index 100% rename from powerline/segments/vim.py rename to powerline/segments/vim/__init__.py diff --git a/powerline/segments/plugin/__init__.py b/powerline/segments/vim/plugin/__init__.py similarity index 100% rename from powerline/segments/plugin/__init__.py rename to powerline/segments/vim/plugin/__init__.py diff --git a/powerline/segments/plugin/ctrlp.py b/powerline/segments/vim/plugin/ctrlp.py similarity index 100% rename from powerline/segments/plugin/ctrlp.py rename to powerline/segments/vim/plugin/ctrlp.py diff --git a/powerline/segments/plugin/nerdtree.py b/powerline/segments/vim/plugin/nerdtree.py similarity index 100% rename from powerline/segments/plugin/nerdtree.py rename to powerline/segments/vim/plugin/nerdtree.py diff --git a/powerline/segments/plugin/syntastic.py b/powerline/segments/vim/plugin/syntastic.py similarity index 100% rename from powerline/segments/plugin/syntastic.py rename to powerline/segments/vim/plugin/syntastic.py diff --git a/powerline/segments/plugin/tagbar.py b/powerline/segments/vim/plugin/tagbar.py similarity index 100% rename from powerline/segments/plugin/tagbar.py rename to powerline/segments/vim/plugin/tagbar.py From df19fe2701c56e42028c65e9dd70b9560fdca6d4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Aug 2014 22:50:27 +0400 Subject: [PATCH 1237/1472] Mention `showtabline` option in Vim tips and tricks --- docs/source/tips-and-tricks.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/tips-and-tricks.rst b/docs/source/tips-and-tricks.rst index 9961b8eb..b2216b12 100644 --- a/docs/source/tips-and-tricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -14,6 +14,7 @@ statusline: .. code-block:: vim set laststatus=2 " Always display the statusline in all windows + set showtabline=2 " Always display the tabline, even if there is only one tab set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline) .. _tips-and-tricks-urxvt: From 86ddb38bf4585d9aeaa22278031ff18ffdf46060 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Aug 2014 22:54:34 +0400 Subject: [PATCH 1238/1472] Add information about writing segments Fixes #629 Ref #287 --- docs/source/configuration/reference.rst | 8 + docs/source/develop.rst | 9 + docs/source/develop/segments.rst | 276 ++++++++++++++++++++++++ docs/source/index.rst | 1 + powerline/__init__.py | 18 ++ powerline/segments/__init__.py | 9 +- 6 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 docs/source/develop.rst create mode 100644 docs/source/develop/segments.rst diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 63181e44..47419d32 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -34,6 +34,8 @@ Common configuration is a subdictionary that is a value of ``common`` key in letters, Cyrillic letters). Valid values: any positive integer; it is suggested that you only set it to 1 (default) or 2. +.. _config-common-watcher: + ``watcher`` Select filesystem watcher. Variants are @@ -65,6 +67,8 @@ Common configuration is a subdictionary that is a value of ``common`` key in :ref:`module segment option `. Paths defined here have priority when searching for modules. +.. _config-common-log: + ``log_file`` Defines path which will hold powerline logs. If not present, logging will be done to stderr. @@ -318,6 +322,8 @@ ascii Theme without any unicode characters at all .. note:: Top-level themes are out of equation here: they are merged before the above merging process happens. +.. _config-themes-segments: + ``segments`` A dict with a ``left`` and a ``right`` lists, consisting of segment dictionaries. Shell themes may also contain ``above`` list of dictionaries. @@ -424,6 +430,8 @@ ascii Theme without any unicode characters at all Segments are removed according to their priority, with low priority segments being removed first. + .. _config-themes-seg-draw_divider: + ``draw_hard_divider``, ``draw_soft_divider`` Whether to draw a divider between this and the adjacent segment. The adjacent segment is to the *right* for segments on the *left* side, and diff --git a/docs/source/develop.rst b/docs/source/develop.rst new file mode 100644 index 00000000..1074d588 --- /dev/null +++ b/docs/source/develop.rst @@ -0,0 +1,9 @@ +*************** +Developer guide +*************** + +.. toctree:: + :maxdepth: 2 + :glob: + + develop/segments diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst new file mode 100644 index 00000000..e25d1b17 --- /dev/null +++ b/docs/source/develop/segments.rst @@ -0,0 +1,276 @@ +**************** +Writing segments +**************** + +Each powerline segment is a callable object. It is supposed to be either +a Python function or :py:class:`powerline.segments.Segment` class. As a callable +object it should receive the following arguments: + +.. note:: All received arguments are keyword arguments. + +``pl`` + A :py:class:`powerline.PowerlineLogger` instance. It must be used every time + you need to log something. + +``segment_info`` + A dictionary. It is only received if callable has + ``powerline_requires_segment_info`` attribute. + + Refer to :ref:`segment_info detailed description ` for + further details. + +``create_watcher`` + Function that will create filesystem watcher once called. Which watcher will + be created exactly is controlled by :ref:`watcher configuration option + `. + +And also any other argument(s) specified by user in :ref:`args key +` (no additional arguments by default). + +This callable object should may return either a string (``unicode`` in Python2 +or ``str`` in Python3, *not* ``str`` in Python2 or ``bytes`` in Python3) object +or a list of dictionaries. String object is a short form of the following return +value: + +.. code-block:: python + + [{ + 'contents': original_return, + 'highlight_group': [segment_name], + }] + +Returned list is a list of segments treated independently, except for +:ref:`draw_inner_divider key `. + +All keys in segments returned by the function override those obtained from +:ref:`configuration ` and have the same meaning. + +Detailed description of used dictionary keys: + +``contents`` + Text displayed by segment. Should be a ``unicode`` (Python2) or ``str`` + (Python3) instance. + +``draw_hard_divider``, ``draw_soft_divider`` + Determines whether given divider should be drawn. Both have the same meaning + as :ref:`the similar keys in configuration + `. + +.. _dev-segments-draw_inner_divider: + +``draw_inner_divider`` + Determines whether *any* divider between segments returned by function + should be drawn. Defaults to ``False``. + +.. _dev-segments-highlight_group: + +``highlight_group`` + Determines segment highlighting. Refer to :ref:`themes documentation + ` for more details. + + Defaults to the name of the segment. + + .. note:: + If you want to include your segment in powerline you must specify all + highlighting groups used in the segment documentation in the form:: + + Highlight groups used: ``g1``[ or ``g2``]*[, ``g3`` (gradient)[ or ``g4``]*]*. + + I.e. use:: + + Highlight groups used: ``foo_gradient`` (gradient) or ``foo``, ``bar``. + + to specify that your segment uses *either* ``foo_gradient`` group or + ``foo`` group *and* ``bar`` group meaning that ``powerline-lint`` will + check that at least one of the first two groups is defined (and if + ``foo_gradient`` is defined it must use at least one gradient color) and + third group is defined as well. + + You must specify all groups on one line. + +``divider_highlight_group`` + Determines segment divider highlight group. Only applicable for soft + dividers: colors for hard dividers are determined by colors of adjacent + segments. + + .. note:: + If you want to include your segment in powerline you must specify used + groups in the segment documentation in the form:: + + Divider highlight group used: ``group``. + + This text must not wrap and you are supposed to end all divider + highlight group names with ``:divider``: e.g. ``cwd:divider``. + +``gradient_level`` + First and the only key that may not be specified in user configuration. It + determines which color should be used for this segment when one of the + highlighting groups specified by :ref:`highlight_group + ` was defined to use the color gradient. + + This key may have any value from 0 to 100 inclusive, value is supposed to be + an ``int`` or ``float`` instance. + + No error occurs if segment has this key, but no used highlight groups use + gradient color. + +Segments layout +=============== + +Powerline segments are all located in one of the ``powerline.segments`` +submodules. For extension-specific segments ``powerline.segments.{ext}`` module +should be used (e.g. ``powerline.segments.shell``), for extension-agnostic there +is ``powerline.segments.common``. + +Plugin-specific segments (currently only those that are specific to vim plugins) +should live in ``powerline.segments.{ext}.plugin.{plugin_name}``: e.g. +``powerline.segments.vim.plugin.gundo``. + +.. _dev-segments-info: + +Segment information used in various extensions +============================================== + +Each ``segment_info`` value should be a dictionary with at least the following +keys: + +``environ`` + Current environment, may be an alias to ``os.environ``. Is guaranteed to + have ``__getitem__`` and ``get`` methods and nothing more. + + .. warning:: + You must not ever use ``os.environ``. If your segment is run in daemon + you will get daemon’s environment which is not correct. If your segment + is run in Vim or in zsh with libzpython you will get Vim or zsh + environment at python startup. + +``getcwd`` + Function that returns current working directory being called with no + arguments. You must not use ``os.getcwd`` for the same reasons you must not + use ``os.environ``, except that current working directory is valid in Vim + and zsh (but not in daemon). + +``home`` + Current home directory. May be false. + +Vim +--- + +Vim ``segment_info`` argument is a dictionary with the following keys: + +``window`` + ``vim.Window`` object. You may obtain one using ``vim.current.window`` or + ``vim.windows[number - 1]``. May be a false object, in which case you should + not use any of this objects’ properties. + +``winnr`` + Window number. Same as ``segment_info['window'].number`` *assuming* Vim is + new enough for ``vim.Window`` object to have ``number`` attribute. + +``window_id`` + Internal powerline window id, unique for each newly created window. You + should assume that this ID is hashable and supports equality comparison, but + you must not use any other assumptions about it. Currently uses integer + numbers incremented each time window is created. + +``buffer`` + ``vim.Buffer`` object. You may obtain one using ``vim.current.buffer``, + ``segment_info['window'].buffer`` or ``vim.buffers[some_number]``. Note that + in the latter case depending on vim version ``some_number`` may be ``bufnr`` + or the internal Vim buffer index which is *not* buffer number. For this + reason to get ``vim.Buffer`` object other then stored in ``segment_info`` + dictionary you must iterate over ``vim.buffers`` and check their ``number`` + attributes. + +``bufnr`` + Buffer number. + +``tabpage`` + ``vim.Tabpage`` object. You may obtain one using ``vim.current.tabpage`` or + ``vim.tabpages[number - 1]``. May be a false object, in which case you + should not use any of this objects’ properties. + +``tabnr`` + Tabpage number. + +``mode`` + Current mode. + +.. note:: + Your segment generally should not assume that it is run for the current + window, current buffer or current tabpage. “Current window” and “current + buffer” restrictions may be ignored if you use ``window_cached`` decorator, + “current tabpage” restriction may be safely ignored if you do not plan to + ever see your segment in the tabline. + +.. warning:: + Powerline is being tested with vim-7.2 and will be tested with it until + travis changes used vim version. This means that you may not use most of the + functionality like ``vim.Window.number``, ``vim.*.vars``, ``vim.*.options`` + or even ``dir(vim object)`` if you want your segment to be included in + powerline. + +Shell +----- + +``args`` + Parsed shell arguments: a ``argparse.Namespace`` object. Check out + ``powerline-render --help`` for the list of all available arguments. + Currently it is expected to contain at least the following attributes: + + ``last_exit_code`` + Exit code returned by last shell command. + + ``last_pipe_status`` + List of exit codes returned by last programs in the pipe or some false + object. Only available in ``zsh``. + + ``jobnum`` + Number of background jobs. + + ``renderer_arg`` + Dictionary containing some keys that are additional arguments used by + shell bindings. *You must not use this attribute directly*: all + arguments from this dictionary are merged with ``segment_info`` + dictionary. Known to have at least the following keys: + + ``client_id`` + Identifier unique to one shell instance. Is used to record instance + state by powerline daemon. + + It is not guaranteed that existing client ID will not be retaken + when old shell with this ID quit: usually process PID is used as + a client ID. + + It is also not guaranteed that client ID will be process PID, number + or something else at all. It is guaranteed though that client ID + will be some hashable object which supports equality comparison. + + ``local_theme`` + Local theme that will be used by shell. One should not rely on the + existence of this key. + + Other keys, if any, are specific to segments. + +Ipython +------- + +``ipython`` + Some object which has ``prompt_count`` attribute. Currently it is guaranteed + to have only this attribute. + + Attribute ``prompt_count`` contains the so-called “history count” + (equivalent to ``\N`` in ``in_template``). + +Segment class +============= + +.. autoclass:: powerline.segments.Segment + :members: + +PowerlineLogger class +===================== + +.. autoclass:: powerline.PowerlineLogger + :members: + :undoc-members: diff --git a/docs/source/index.rst b/docs/source/index.rst index bab34ecb..e97a17ad 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,6 +10,7 @@ Powerline installation usage configuration + develop troubleshooting tips-and-tricks license-and-credits diff --git a/powerline/__init__.py b/powerline/__init__.py index f78b1135..7f8932dd 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -36,6 +36,24 @@ def _find_config_files(search_paths, config_file, config_loader=None, loader_cal class PowerlineLogger(object): + '''Proxy class for logging.Logger instance + + It emits messages in format ``{ext}:{prefix}:{message}`` where + + ``{ext}`` + is a used powerline extension (e.g. “vim”, “shell”, “ipython”). + ``{prefix}`` + is a local prefix, usually a segment name. + ``{message}`` + is the original message passed to one of the logging methods. + + Each of the methods (``critical``, ``exception``, ``info``, ``error``, + ``warn``, ``debug``) expects to receive message in an ``str.format`` format, + not in printf-like format. + + Log is saved to the location :ref:`specified by user `. + ''' + def __init__(self, use_daemon_threads, logger, ext): self.logger = logger self.ext = ext diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py index 3c2da39c..8199b3cc 100644 --- a/powerline/segments/__init__.py +++ b/powerline/segments/__init__.py @@ -13,7 +13,14 @@ __path__ = extend_path(__path__, __name__) class Segment(object): '''Base class for any segment that is not a function - Required for powerline.lint.inspect to work properly. + Required for powerline.lint.inspect to work properly: it defines methods for + omitting existing or adding new arguments. + + .. note:: + Until python-3.4 ``inspect.getargspec`` does not support querying + callable classes for arguments of their ``__call__`` method, requiring + to use this method directly (i.e. before 3.4 you should write + ``getargspec(obj.__call__)`` in place of ``getargspec(obj)``). ''' if sys.version_info < (3, 4): def argspecobjs(self): From 1a1a8410d2a929a6d30939f3631fab7992ee41bf Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Aug 2014 23:33:44 +0400 Subject: [PATCH 1239/1472] Add information about shell and ipython local themes --- docs/source/configuration/reference.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 47419d32..e882a79a 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -125,7 +125,16 @@ Common configuration is a subdictionary that is a value of ``ext`` key in is either ``matcher_module.module_attribute`` or ``module_attribute`` (``matcher_module`` defaults to ``powerline.matchers.vim``) and ``module_attribute`` should point to a function that returns boolean value - indicating that current buffer has (not) matched conditions. + indicating that current buffer has (not) matched conditions. There is an + exception for ``matcher_name`` though: if it is ``__tabline__`` no functions + are loaded. This special theme is used for ``tabline`` Vim option. + + For shell and ipython it is a simple ``{prompt_type : theme_name}``, where + ``prompt_type`` is a string with no special meaning (specifically it does + not refer to any Python function). Shell has ``continuation``, and + ``select`` prompts with rather self-explanatory names, IPython has ``in2``, + ``out`` and ``rewrite`` prompts (refer to IPython documentation for more + details) while ``in`` prompt is the default. ``components`` Determines which extension components should be enabled. This key is highly From f0ccb2dcb8a565fea58f014eb9ce6f410f8ef6c5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 14 Aug 2014 23:51:07 +0400 Subject: [PATCH 1240/1472] Fix path to __main__ theme --- docs/source/configuration/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index e882a79a..9d166292 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -252,7 +252,7 @@ Themes ====== :Location: :file:`powerline/themes/{top_theme}.json`, - :file:`powerline/themes/__main__.json`, + :file:`powerline/themes/{extension}/__main__.json`, :file:`powerline/themes/{extension}/{name}.json` Theme files are processed in order given: definitions from each next file From b5b91afcaecba49512182cc0c1b7eeeb8afa5fc2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 18:41:55 +0400 Subject: [PATCH 1241/1472] Document how to create Vim local themes --- docs/source/develop.rst | 1 + docs/source/develop/local-themes.rst | 59 ++++++++++++++++++++++++++++ docs/source/develop/segments.rst | 2 + 3 files changed, 62 insertions(+) create mode 100644 docs/source/develop/local-themes.rst diff --git a/docs/source/develop.rst b/docs/source/develop.rst index 1074d588..7651b77f 100644 --- a/docs/source/develop.rst +++ b/docs/source/develop.rst @@ -7,3 +7,4 @@ Developer guide :glob: develop/segments + develop/local-themes diff --git a/docs/source/develop/local-themes.rst b/docs/source/develop/local-themes.rst new file mode 100644 index 00000000..e1ca6483 --- /dev/null +++ b/docs/source/develop/local-themes.rst @@ -0,0 +1,59 @@ +************ +Local themes +************ + +From the user point of view local themes are the regular themes with a specific +scope where they are applied (i.e. specific vim window or specific kind of +prompt). Used themes are defined in :ref:`local_themes key +`. + +Vim local themes +================ + +Vim is the only available extension that has a wide variaty of options for local +themes. It is the only extension where local theme key refers to a function as +described in :ref:`local_themes value documentation `. + +This function always takes a single value named ``matcher_info`` which is the +same dictionary as :ref:`segment_info dictionary `. Unlike +segments it takes this single argument as a *positional* argument, not as +a keyword one. + +Matcher function should return a boolean value: ``True`` if theme applies for +the given ``matcher_info`` dictionary or ``False`` if it is not. When one of the +matcher functions returns ``True`` powerline takes the corresponding theme at +uses it for the given window. Matchers are not tested in any particular order. + +In addition to :ref:`local_themes configuration key ` +developer of some plugin which wishes to support powerline without including his +code in powerline tree may use +:py:meth:`powerline.vim.VimPowerline.add_local_theme` method. It accepts two +arguments: matcher name (same as in :ref:`local_themes +`) and dictionary with theme. This dictionary is merged +with :ref:`top theme ` and +:file:`powerline/themes/vim/__main__.json`. Note that if user already specified +your matcher in his configuration file ``KeyError`` is raised. + +Other local themes +================== + +Except for Vim only IPython and shells have local themes. Unlike Vim these +themes are names with no special meaning (they do not refer to or cause loading +of any Python functions): + ++---------+------------+-------------------------------------------------------+ +|Extension|Theme name |Description | ++---------+------------+-------------------------------------------------------+ +|Shell |continuation|Shown for unfinished command (unclosed quote, | +| | |unfinished cycle). | +| +------------+-------------------------------------------------------+ +| |select |Shown for ``select`` command available in some shells. | ++---------+------------+-------------------------------------------------------+ +|IPython |in2 |Continuation prompt: shown for unfinished (multiline) | +| | |expression, unfinished class or function definition. | +| +------------+-------------------------------------------------------+ +| |out |Displayed before the result. | +| +------------+-------------------------------------------------------+ +| |rewrite |Displayed before the actually executed code when | +| | |``autorewrite`` IPython feature is enabled.  | ++---------+------------+-------------------------------------------------------+ diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index e25d1b17..592f3052 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -153,6 +153,8 @@ keys: ``home`` Current home directory. May be false. +.. _dev-segment_info-vim: + Vim --- From 4a8b81e68b85d378fc3c38631d3ba3b68466a410 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 19:15:49 +0400 Subject: [PATCH 1242/1472] Rearrange renderers: move *_prompt modules one level up Renames powerline.renderers.zsh_prompt to powerline.renderers.shell.zsh, same for other *sh_prompt modules. --- powerline/__init__.py | 21 ++++++++++++------- powerline/bindings/bash/powerline.sh | 6 +++--- powerline/bindings/shell/powerline.sh | 4 ++-- powerline/bindings/tcsh/powerline.tcsh | 4 ++-- powerline/bindings/zsh/__init__.py | 2 +- powerline/bindings/zsh/powerline.zsh | 2 +- powerline/ipython.py | 2 +- powerline/lint/__init__.py | 6 +++--- .../{ipython.py => ipython/__init__.py} | 0 .../{ipython_prompt.py => ipython/prompt.py} | 0 .../renderers/{shell.py => shell/__init__.py} | 0 .../{bash_prompt.py => shell/bash.py} | 0 .../renderers/{ksh_prompt.py => shell/ksh.py} | 0 .../{tcsh_prompt.py => shell/tcsh.py} | 2 +- .../renderers/{zsh_prompt.py => shell/zsh.py} | 0 powerline/shell.py | 2 +- tests/test_cmdline.py | 10 ++++----- tests/test_provided_config_files.py | 4 ++-- 18 files changed, 36 insertions(+), 29 deletions(-) rename powerline/renderers/{ipython.py => ipython/__init__.py} (100%) rename powerline/renderers/{ipython_prompt.py => ipython/prompt.py} (100%) rename powerline/renderers/{shell.py => shell/__init__.py} (100%) rename powerline/renderers/{bash_prompt.py => shell/bash.py} (100%) rename powerline/renderers/{ksh_prompt.py => shell/ksh.py} (100%) rename powerline/renderers/{tcsh_prompt.py => shell/tcsh.py} (87%) rename powerline/renderers/{zsh_prompt.py => shell/zsh.py} (100%) diff --git a/powerline/__init__.py b/powerline/__init__.py index 7f8932dd..af0600a4 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -279,12 +279,14 @@ class Powerline(object): colorschemes, render module (``powerline.renders.{ext}``). :param str renderer_module: Overrides renderer module (defaults to ``ext``). Should be the name of - the package imported like this: ``powerline.renders.{render_module}``. + the package imported like this: ``powerline.renderers.{render_module}``. If this parameter contains a dot, ``powerline.renderers.`` is not prepended. There is also a special case for renderers defined in toplevel modules: ``foo.`` (note: dot at the end) tries to get renderer from module ``foo`` (because ``foo`` (without dot) tries to get renderer - from module ``powerline.renderers.foo``). + from module ``powerline.renderers.foo``). When ``.foo`` (with leading + dot) variant is used ``renderer_module`` will be + ``powerline.renderers.{ext}{renderer_module}``. :param bool run_once: Determines whether .renderer.render() method will be run only once during python session. @@ -307,15 +309,20 @@ class Powerline(object): shutdown_event=None, config_loader=None): self.ext = ext - self.renderer_module = renderer_module or ext self.run_once = run_once self.logger = logger self.use_daemon_threads = use_daemon_threads - if '.' not in self.renderer_module: - self.renderer_module = 'powerline.renderers.' + self.renderer_module - elif self.renderer_module[-1] == '.': - self.renderer_module = self.renderer_module[:-1] + if not renderer_module: + self.renderer_module = 'powerline.renderers.' + ext + elif '.' not in renderer_module: + self.renderer_module = 'powerline.renderers.' + renderer_module + elif renderer_module.startswith('.'): + self.renderer_module = 'powerline.renderers.' + ext + renderer_module + elif renderer_module.endswith('.'): + self.renderer_module = renderer_module[:-1] + else: + self.renderer_module = renderer_module self.find_config_files = generate_config_finder(self.get_config_paths) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index e44d9da2..1a5ab763 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -53,7 +53,7 @@ _powerline_prompt() { # Arguments: side, last_exit_code, jobnum $POWERLINE_COMMAND shell $1 \ --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ - -r bash_prompt \ + -r.bash \ --last_exit_code=$2 \ --jobnum=$3 \ --renderer_arg="client_id=$$" @@ -64,7 +64,7 @@ _powerline_set_prompt() { local jobnum="$(jobs -p|wc -l)" PS1="$(_powerline_prompt aboveleft $last_exit_code $jobnum)" if test -n "$POWERLINE_SHELL_CONTINUATION$POWERLINE_BASH_CONTINUATION" ; then - PS2="$(_powerline_local_prompt left -rbash_prompt $last_exit_code $jobnum continuation)" + PS2="$(_powerline_local_prompt left -r.bash $last_exit_code $jobnum continuation)" fi if test -n "$POWERLINE_SHELL_SELECT$POWERLINE_BASH_SELECT" ; then PS3="$(_powerline_local_prompt left '' $last_exit_code $jobnum select)" @@ -79,7 +79,7 @@ _powerline_setup_prompt() { fi test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || PROMPT_COMMAND=$'_powerline_set_prompt\n'"${PROMPT_COMMAND}" - PS2="$(_powerline_local_prompt left -rbash_prompt 0 0 continuation)" + PS2="$(_powerline_local_prompt left -r.bash 0 0 continuation)" PS3="$(_powerline_local_prompt left '' 0 0 select)" } diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index 3fc2b434..8940c028 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -122,8 +122,8 @@ _powerline_tmux_set_columns() { _powerline_set_renderer_arg() { case "$1" in - bb|ash) _POWERLINE_RENDERER_ARG="-rbash_prompt" ;; - mksh|ksh) _POWERLINE_RENDERER_ARG="-rksh_prompt" ;; + bb|ash) _POWERLINE_RENDERER_ARG="-r .bash" ;; + mksh|ksh) _POWERLINE_RENDERER_ARG="-r .ksh" ;; bash|dash) _POWERLINE_RENDERER_ARG= ;; esac } diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 48155466..ac93341b 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -27,8 +27,8 @@ if ( { $POWERLINE_CONFIG shell --shell=tcsh uses prompt } ) then alias _powerline_above '$POWERLINE_COMMAND shell above --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' endif - alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' - alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r .tcsh --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r .tcsh --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 444490b5..b6ccde1a 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -26,7 +26,7 @@ def get_var_config(var): class Args(object): __slots__ = ('last_pipe_status', 'last_exit_code') ext = ['shell'] - renderer_module = 'zsh_prompt' + renderer_module = '.zsh' @property def config(self): diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 0bc2c50e..8a7078f1 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -129,7 +129,7 @@ _powerline_setup_prompt() { POWERLINE_COMMAND=( "$($POWERLINE_CONFIG shell command)" ) fi - local add_args='-r zsh_prompt' + local add_args='-r .zsh' add_args+=' --last_exit_code=$?' add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' diff --git a/powerline/ipython.py b/powerline/ipython.py index 5fc66b34..bc244d6d 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -26,7 +26,7 @@ class IpythonPowerline(Powerline): def __init__(self, is_prompt, old_widths): super(IpythonPowerline, self).__init__( 'ipython', - renderer_module=('ipython_prompt' if is_prompt else 'ipython'), + renderer_module=('.prompt' if is_prompt else None), use_daemon_threads=True ) self.old_widths = old_widths diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index a1749790..891a7d8f 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -432,7 +432,7 @@ def check_matcher_func(ext, match_name, data, context, echoerr): match_function = match_name with WithPath(import_paths): try: - func = getattr(__import__(match_module, fromlist=[match_function]), unicode(match_function)) + func = getattr(__import__(str(match_module), fromlist=[str(match_function)]), str(match_function)) except ImportError: echoerr(context='Error while loading matcher functions', problem='failed to load module {0}'.format(match_module), @@ -823,7 +823,7 @@ def check_key_compatibility(segment, data, context, echoerr): def check_segment_module(module, data, context, echoerr): with WithPath(data['import_paths']): try: - __import__(unicode(module)) + __import__(str(module)) except ImportError as e: if echoerr.logger.level >= logging.DEBUG: echoerr.logger.exception(e) @@ -875,7 +875,7 @@ def import_segment(name, data, context, echoerr, module=None): with WithPath(data['import_paths']): try: - func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name)) + func = getattr(__import__(str(module), fromlist=[str(name)]), str(name)) except ImportError: echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), problem='failed to import module {0}'.format(module), diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython/__init__.py similarity index 100% rename from powerline/renderers/ipython.py rename to powerline/renderers/ipython/__init__.py diff --git a/powerline/renderers/ipython_prompt.py b/powerline/renderers/ipython/prompt.py similarity index 100% rename from powerline/renderers/ipython_prompt.py rename to powerline/renderers/ipython/prompt.py diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell/__init__.py similarity index 100% rename from powerline/renderers/shell.py rename to powerline/renderers/shell/__init__.py diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/shell/bash.py similarity index 100% rename from powerline/renderers/bash_prompt.py rename to powerline/renderers/shell/bash.py diff --git a/powerline/renderers/ksh_prompt.py b/powerline/renderers/shell/ksh.py similarity index 100% rename from powerline/renderers/ksh_prompt.py rename to powerline/renderers/shell/ksh.py diff --git a/powerline/renderers/tcsh_prompt.py b/powerline/renderers/shell/tcsh.py similarity index 87% rename from powerline/renderers/tcsh_prompt.py rename to powerline/renderers/shell/tcsh.py index adbb91c9..261b081a 100644 --- a/powerline/renderers/tcsh_prompt.py +++ b/powerline/renderers/shell/tcsh.py @@ -2,7 +2,7 @@ from __future__ import absolute_import, unicode_literals -from powerline.renderers.zsh_prompt import ZshPromptRenderer +from powerline.renderers.shell.zsh import ZshPromptRenderer class TcshPromptRenderer(ZshPromptRenderer): diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/shell/zsh.py similarity index 100% rename from powerline/renderers/zsh_prompt.py rename to powerline/renderers/shell/zsh.py diff --git a/powerline/shell.py b/powerline/shell.py index 513a6974..25d07258 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -50,7 +50,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('ext', nargs=1, help='Extension: application for which powerline command is launched (usually `shell\' or `tmux\')') p.add_argument('side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'), help='Side: `left\' and `right\' represent left and right side respectively, `above\' emits lines that are supposed to be printed just above the prompt and `aboveleft\' is like concatenating `above\' with `left\' with the exception that only one Python instance is used in this case.') p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str, - help='Renderer module. Usually something like `bash_prompt\' or `zsh_prompt\', is supposed to be set only in shell-specific bindings file.') + help='Renderer module. Usually something like `.bash\' or `.zsh\', is supposed to be set only in shell-specific bindings file.') p.add_argument('-w', '--width', type=int, help='Maximum prompt with. Triggers truncation of some segments') p.add_argument('--last_exit_code', metavar='INT', type=int, help='Last exit code') p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()], help='Like above, but is supposed to contain space-separated array of statuses, representing exit statuses of commands in one pipe.') diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index f1ff683a..5af43eee 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -42,7 +42,7 @@ class TestParser(TestCase): (['shell', '--config_path'], 'expected one argument'), (['shell', '--renderer_arg'], 'expected one argument'), (['shell', '--jobnum'], 'expected one argument'), - (['-r', 'zsh_prompt'], 'too few arguments|the following arguments are required: ext'), + (['-r', '.zsh'], 'too few arguments|the following arguments are required: ext'), (['shell', '--last_exit_code', 'i'], 'invalid int value'), (['shell', '--last_pipe_status', '1 i'], 'invalid value'), ]: @@ -57,12 +57,12 @@ class TestParser(TestCase): err = StrIO() with replace_attr(sys, 'stdout', out, 'stderr', err): for argv, expargs in [ - (['shell'], {'ext': ['shell']}), - (['shell', '-r', 'zsh_prompt'], {'ext': ['shell'], 'renderer_module': 'zsh_prompt'}), + (['shell'], {'ext': ['shell']}), + (['shell', '-r', '.zsh'], {'ext': ['shell'], 'renderer_module': '.zsh'}), ([ 'shell', 'left', - '-r', 'zsh_prompt', + '-r', '.zsh', '--last_exit_code', '10', '--last_pipe_status', '10 20 30', '--jobnum=10', @@ -76,7 +76,7 @@ class TestParser(TestCase): ], { 'ext': ['shell'], 'side': 'left', - 'renderer_module': 'zsh_prompt', + 'renderer_module': '.zsh', 'last_exit_code': 10, 'last_pipe_status': [10, 20, 30], 'jobnum': 10, diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 45cffcf6..da160ab1 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -86,7 +86,7 @@ class TestConfig(TestCase): def test_zsh(self): from powerline.shell import ShellPowerline - args = Args(last_pipe_status=[1, 0], jobnum=0, ext=['shell'], renderer_module='zsh_prompt') + args = Args(last_pipe_status=[1, 0], jobnum=0, ext=['shell'], renderer_module='.zsh') segment_info = {'args': args} with ShellPowerline(args, run_once=False) as powerline: powerline.render(segment_info=segment_info) @@ -102,7 +102,7 @@ class TestConfig(TestCase): def test_bash(self): from powerline.shell import ShellPowerline - args = Args(last_exit_code=1, jobnum=0, ext=['shell'], renderer_module='bash_prompt', config={'ext': {'shell': {'theme': 'default_leftonly'}}}) + args = Args(last_exit_code=1, jobnum=0, ext=['shell'], renderer_module='.bash', config={'ext': {'shell': {'theme': 'default_leftonly'}}}) with ShellPowerline(args, run_once=False) as powerline: powerline.render(segment_info={'args': args}) with ShellPowerline(args, run_once=False) as powerline: From 33562ab4b8eea7458cc1d4383d886077848f2219 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 19:41:15 +0400 Subject: [PATCH 1243/1472] Improve documentation --- powerline/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index af0600a4..9587fb6c 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -280,7 +280,7 @@ class Powerline(object): :param str renderer_module: Overrides renderer module (defaults to ``ext``). Should be the name of the package imported like this: ``powerline.renderers.{render_module}``. - If this parameter contains a dot, ``powerline.renderers.`` is not + If this parameter contains a dot ``powerline.renderers.`` is not prepended. There is also a special case for renderers defined in toplevel modules: ``foo.`` (note: dot at the end) tries to get renderer from module ``foo`` (because ``foo`` (without dot) tries to get renderer @@ -288,16 +288,17 @@ class Powerline(object): dot) variant is used ``renderer_module`` will be ``powerline.renderers.{ext}{renderer_module}``. :param bool run_once: - Determines whether .renderer.render() method will be run only once + Determines whether :py:meth:`render` method will be run only once during python session. :param Logger logger: - If present, no new logger will be created and this logger will be used. + If present no new logger will be created and the provided logger will be + used. :param bool use_daemon_threads: - Use daemon threads for. + When creating threads make them daemon ones. :param Event shutdown_event: - Use this Event as shutdown_event. + Use this Event as shutdown_event instead of creating new event. :param ConfigLoader config_loader: - Class that manages (re)loading of configuration. + Instance of the class that manages (re)loading of the configuration. ''' def __init__(self, From 0848f7d8011d8982e7dcd985f8350598ba54b4b7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 19:44:27 +0400 Subject: [PATCH 1244/1472] Document how to create extensions Fixes #287 --- docs/source/develop.rst | 1 + docs/source/develop/extensions.rst | 47 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 docs/source/develop/extensions.rst diff --git a/docs/source/develop.rst b/docs/source/develop.rst index 7651b77f..e7b29cc7 100644 --- a/docs/source/develop.rst +++ b/docs/source/develop.rst @@ -8,3 +8,4 @@ Developer guide develop/segments develop/local-themes + develop/extensions diff --git a/docs/source/develop/extensions.rst b/docs/source/develop/extensions.rst new file mode 100644 index 00000000..9f4437d6 --- /dev/null +++ b/docs/source/develop/extensions.rst @@ -0,0 +1,47 @@ +******************************** +Creating new powerline extension +******************************** + +Powerline extension is a code that tells powerline how to highlight and display +segments in some set of applications. Specifically this means + +#. Creating a :py:class:`powerline.Powerline` subclass that knows how to obtain + :ref:`local configuration overrides `. It also + knows how to load local themes, but not when to apply them. + + Instance of this class is the only instance that interacts directly with + bindings code, so it has a proxy :py:meth:`powerline.Powerline.render` and + :py:meth:`powerline.Powerline.shutdown` methods and other methods which may + be useful for bindings. + + This subclass must be placed directly in :file:`powerline` directory (e.g. in + :file:`powerline/vim.py`) and named like ``VimPowerline`` (version of the + file name without directory and extension and first capital letter + + ``Powerline``). There is no technical reason for naming classes like this. +#. Creating a :py:class:`powerline.renderer.Renderer` subclass that knows how to + highlight a segment or reset highlighting to the default value (only makes + sense in prompts). It is also responsible for selecting local themes and + computing text width. + + This subclass must be placed directly in :file:`powerline/renderers` + directory (if you are creating powerline extension for a set of applications + use :file:`powerline/renderers/{ext}/*.py`) and named like ``ExtRenderer`` or + ``AppPromptRenderer``. For technical reasons the class itself must be + referenced in ``renderer`` module attribute thus allowing only one renderer + per one module. +#. Creating an extension bindings. These are to be placed in + :file:`powerline/bindings/{ext}` and may contain virtually anything which may + be required for powerline to work inside given applications, assuming it does + not fit in other places. + +Powerline class +=============== + +.. autoclass:: powerline.Powerline + :members: + +Renderer class +============== + +.. autoclass:: powerline.renderer.Renderer + :members: From 2f3d56887fb04854a13011d3c2cef3d655228364 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 20:01:07 +0400 Subject: [PATCH 1245/1472] Drop unneeded ignores --- .local.vimrc | 2 +- CONTRIBUTING.rst | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.local.vimrc b/.local.vimrc index ac676144..090a8331 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -4,5 +4,5 @@ " " [1]: http://www.vim.org/scripts/script.php?script_id=3393 " [2]: https://github.com/thinca/vim-localrc -let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128,E225,W291,E126' +let g:syntastic_python_flake8_args = '--ignore=W191,E501,E128,W291,E126' let b:syntastic_checkers = ['flake8'] diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 96ebab7f..0e5ed7c1 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -60,10 +60,9 @@ Programming style * The project uses *tabs for indentation* and *spaces for alignment*, this is also included in a vim modeline on top of every script file. -* Run your code through ``flake8 - --ignore=W191,E501,E121,E122,E123,E128,E225`` to fix any style errors. Use - common sense regarding whitespace warnings, not all ``flake8`` warnings - need to be fixed. +* Run your code through ``flake8 --ignore=W191,E501,E128,W291,E126`` to fix any + style errors. Use common sense regarding whitespace warnings, not all + ``flake8`` warnings need to be fixed. * Trailing whitespace to indicate a continuing paragraph is OK in comments, documentation and commit messages. From c316ef8e767f67c237f231212c2a215606340e92 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 20:01:33 +0400 Subject: [PATCH 1246/1472] Fix some style problems --- powerline/lib/vcs/__init__.py | 2 +- powerline/lint/__init__.py | 2 +- powerline/segments/common.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 1029bc3f..58a4883d 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -194,7 +194,7 @@ class TreeStatusCache(dict): if self.tw(key, logger=logger, ignore_event=getattr(repo, 'ignore_event', None)): self.pop(key, None) except OSError as e: - logger.warn('Failed to check %s for changes, with error: %s'% key, e) + logger.warn('Failed to check %s for changes, with error: %s' % key, e) return self.cache_and_get(key, repo.status) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 891a7d8f..7ce152fb 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1252,7 +1252,7 @@ segment_spec = Spec( ).func(check_full_segment_data) sub_segments_spec.optional().list(segment_spec) segments_spec = Spec().optional().list(segment_spec).copy -segdict_spec=Spec( +segdict_spec = Spec( left=segments_spec().context_message('Error while loading segments from left side (key {key})'), right=segments_spec().context_message('Error while loading segments from right side (key {key})'), ).func( diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 84a32a15..eaef499f 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1130,7 +1130,7 @@ def _get_battery(pl): what, dbus_interface=devinterface ) - if int(devget('Type'))!= 2: + if int(devget('Type')) != 2: pl.debug('Not using DBUS+UPower with {0}: invalid type', devpath) continue if not bool(devget('IsPresent')): From fa93d0a9295f1c31960b9f2d0895c548faae4e75 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 20:04:12 +0400 Subject: [PATCH 1247/1472] =?UTF-8?q?Ignore=20E101=20(=E2=80=9Cindentation?= =?UTF-8?q?=20contains=20mixed=20spaces=20and=20tabs=E2=80=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .local.vimrc | 2 +- CONTRIBUTING.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.local.vimrc b/.local.vimrc index 090a8331..edf53ee8 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -4,5 +4,5 @@ " " [1]: http://www.vim.org/scripts/script.php?script_id=3393 " [2]: https://github.com/thinca/vim-localrc -let g:syntastic_python_flake8_args = '--ignore=W191,E501,E128,W291,E126' +let g:syntastic_python_flake8_args = '--ignore=W191,E501,E128,W291,E126,E101' let b:syntastic_checkers = ['flake8'] diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 0e5ed7c1..f324bce8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -60,8 +60,8 @@ Programming style * The project uses *tabs for indentation* and *spaces for alignment*, this is also included in a vim modeline on top of every script file. -* Run your code through ``flake8 --ignore=W191,E501,E128,W291,E126`` to fix any - style errors. Use common sense regarding whitespace warnings, not all +* Run your code through ``flake8 --ignore=W191,E501,E128,W291,E126,E101`` to fix + any style errors. Use common sense regarding whitespace warnings, not all ``flake8`` warnings need to be fixed. * Trailing whitespace to indicate a continuing paragraph is OK in comments, documentation and commit messages. From cbfa64c7cbdcb9905920236aee110dc999a62eef Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 20:06:40 +0400 Subject: [PATCH 1248/1472] Mention long lines in CONTRIBUTING.rst --- CONTRIBUTING.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f324bce8..e31718f8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -18,7 +18,7 @@ Getting started already exist. * Clearly describe the issue. - * If the issue is a bug: Make sure you include steps to reproduce, and + * If the issue is a bug: make sure you include steps to reproduce, and include the earliest revision that you know has the issue. * Fork the repository on GitHub. @@ -65,6 +65,8 @@ Programming style ``flake8`` warnings need to be fixed. * Trailing whitespace to indicate a continuing paragraph is OK in comments, documentation and commit messages. +* It is allowed to have too long lines. It is advised though to avoid lines + wider then a hundred of characters. Submitting changes ================== From ae92d83eae5142322ff20b9aa81eb53b0b363575 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 20:58:19 +0400 Subject: [PATCH 1249/1472] Fix powerline style Specifically I searched for all lines that are more then one tab off compared to the previous line with BufGrep /\(^\t\+\)\S.*\n\1\t\t\+/ and replaced them with something more appropriate. Most of time this resulted in a few more newlines, but there are cases when I used mixed tabs/spaces indentation+alignment. --- .local.vimrc | 3 + powerline/__init__.py | 42 ++++--- powerline/bindings/vim/__init__.py | 17 ++- powerline/lib/inotify.py | 7 +- powerline/lib/shell.py | 11 +- powerline/lib/tree_watcher.py | 14 ++- powerline/lib/vcs/git.py | 11 +- powerline/lint/__init__.py | 148 ++++++++++++----------- powerline/lint/markedjson/composer.py | 30 +++-- powerline/lint/markedjson/constructor.py | 77 +++++++----- powerline/lint/markedjson/error.py | 30 +++-- powerline/lint/markedjson/events.py | 24 ++-- powerline/lint/markedjson/markedvalue.py | 12 +- powerline/lint/markedjson/nodes.py | 30 +++-- powerline/lint/markedjson/parser.py | 78 +++++++----- powerline/lint/markedjson/reader.py | 19 +-- powerline/lint/markedjson/resolver.py | 19 ++- powerline/lint/markedjson/scanner.py | 59 +++++---- powerline/lint/markedjson/tokens.py | 15 ++- powerline/renderer.py | 14 +-- powerline/renderers/vim.py | 14 ++- powerline/segments/common.py | 8 +- powerline/segments/vim/__init__.py | 8 +- powerline/shell.py | 12 +- powerline/theme.py | 16 +-- powerline/vim.py | 27 +++-- tests/lib/__init__.py | 6 +- tests/test_segments.py | 118 ++++++++++-------- tests/vim.py | 6 +- tools/colors_find.py | 6 +- tools/generate_gradients.py | 18 ++- 31 files changed, 516 insertions(+), 383 deletions(-) diff --git a/.local.vimrc b/.local.vimrc index edf53ee8..c8e1ef38 100644 --- a/.local.vimrc +++ b/.local.vimrc @@ -6,3 +6,6 @@ " [2]: https://github.com/thinca/vim-localrc let g:syntastic_python_flake8_args = '--ignore=W191,E501,E128,W291,E126,E101' let b:syntastic_checkers = ['flake8'] +unlet! g:python_space_error_highlight +let g:pymode_syntax_indent_errors = 0 +let g:pymode_syntax_space_errors = 0 diff --git a/powerline/__init__.py b/powerline/__init__.py index 9587fb6c..f2a7a166 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -255,11 +255,13 @@ def finish_common_config(common_config): if sys.version_info < (3,): # `raise exception[0], None, exception[1]` is a SyntaxError in python-3* # Not using ('''…''') because this syntax does not work in python-2.6 - exec(('def reraise(exception):\n' - ' if type(exception) is tuple:\n' - ' raise exception[0], None, exception[1]\n' - ' else:\n' - ' raise exception\n')) + exec(( + 'def reraise(exception):\n' + ' if type(exception) is tuple:\n' + ' raise exception[0], None, exception[1]\n' + ' else:\n' + ' raise exception\n' + )) else: def reraise(exception): if type(exception) is tuple: @@ -302,13 +304,13 @@ class Powerline(object): ''' def __init__(self, - ext, - renderer_module=None, - run_once=False, - logger=None, - use_daemon_threads=True, - shutdown_event=None, - config_loader=None): + ext, + renderer_module=None, + run_once=False, + logger=None, + use_daemon_threads=True, + shutdown_event=None, + config_loader=None): self.ext = ext self.run_once = run_once self.logger = logger @@ -437,12 +439,16 @@ class Powerline(object): or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes') ): self.renderer_options['local_themes'] = self.get_local_themes(self.ext_config.get('local_themes')) - load_colorscheme = (load_colorscheme - or not self.prev_ext_config - or self.prev_ext_config['colorscheme'] != self.ext_config['colorscheme']) - load_theme = (load_theme - or not self.prev_ext_config - or self.prev_ext_config['theme'] != self.ext_config['theme']) + load_colorscheme = ( + load_colorscheme + or not self.prev_ext_config + or self.prev_ext_config['colorscheme'] != self.ext_config['colorscheme'] + ) + load_theme = ( + load_theme + or not self.prev_ext_config + or self.prev_ext_config['theme'] != self.ext_config['theme'] + ) self.prev_ext_config = self.ext_config create_renderer = load_colors or load_colorscheme or load_theme or common_config_differs or ext_config_differs diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index d030bd43..f3f9783a 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -86,8 +86,9 @@ else: if not buffer or buffer.number == vim.current.buffer.number: return int(vim.eval('exists("b:{0}")'.format(varname))) else: - return int(vim.eval('has_key(getbufvar({0}, ""), {1})' - .format(buffer.number, varname))) + return int(vim.eval( + 'has_key(getbufvar({0}, ""), {1})'.format(buffer.number, varname) + )) def vim_getwinvar(segment_info, varname): # NOQA result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname)) @@ -210,9 +211,15 @@ class VimEnviron(object): @staticmethod def __setitem__(key, value): - return vim.command('let $' + key + '="' - + value.replace('"', '\\"').replace('\\', '\\\\').replace('\n', '\\n').replace('\0', '') - + '"') + return vim.command( + 'let ${0}="{1}"'.format( + key, + value.replace('"', '\\"') + .replace('\\', '\\\\') + .replace('\n', '\\n') + .replace('\0', '') + ) + ) if sys.version_info < (3,): diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index c2b25444..48fe6ae7 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -97,9 +97,10 @@ class INotify(object): ONESHOT = 0x80000000 # Only send event once. # All events which a program can wait on. - ALL_EVENTS = (ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | - OPEN | MOVED_FROM | MOVED_TO | CREATE | DELETE | - DELETE_SELF | MOVE_SELF) + ALL_EVENTS = ( + ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | OPEN | + MOVED_FROM | MOVED_TO | CREATE | DELETE | DELETE_SELF | MOVE_SELF + ) # See CLOEXEC = 0x80000 diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index 4418c3d9..19b25c8b 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -82,8 +82,11 @@ except ImportError: # Additionally check that `file` is not a directory, as on Windows # directories pass the os.access check. def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) - and not os.path.isdir(fn)) + return ( + os.path.exists(fn) + and os.access(fn, mode) + and not os.path.isdir(fn) + ) # If we're given a path with a directory part, look it up directly rather # than referring to PATH directories. This includes checking relative to the @@ -101,7 +104,7 @@ except ImportError: if sys.platform == "win32": # The current directory takes precedence on Windows. - if not os.curdir in path: + if os.curdir not in path: path.insert(0, os.curdir) # PATHEXT is necessary to check on Windows. @@ -122,7 +125,7 @@ except ImportError: seen = set() for dir in path: normdir = os.path.normcase(dir) - if not normdir in seen: + if normdir not in seen: seen.add(normdir) for thefile in files: name = os.path.join(dir, thefile) diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index c0ff4c3c..6a439dd7 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -94,13 +94,15 @@ class INotifyTreeWatcher(INotify): def add_watch(self, path): import ctypes bpath = path if isinstance(path, bytes) else path.encode(self.fenc) - wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), - # Ignore symlinks and watch only directories - self.DONT_FOLLOW | self.ONLYDIR | + wd = self._add_watch( + self._inotify_fd, ctypes.c_char_p(bpath), + # Ignore symlinks and watch only directories + self.DONT_FOLLOW | self.ONLYDIR | - self.MODIFY | self.CREATE | self.DELETE | - self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | - self.ATTRIB | self.DELETE_SELF) + self.MODIFY | self.CREATE | self.DELETE | + self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | + self.ATTRIB | self.DELETE_SELF + ) if wd == -1: eno = ctypes.get_errno() if eno == errno.ENOTDIR: diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 27a1e71a..42bdd321 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -139,13 +139,14 @@ try: untracked_column = 'U' continue - if status & (git.GIT_STATUS_WT_DELETED - | git.GIT_STATUS_WT_MODIFIED): + if status & (git.GIT_STATUS_WT_DELETED | git.GIT_STATUS_WT_MODIFIED): wt_column = 'D' - if status & (git.GIT_STATUS_INDEX_NEW - | git.GIT_STATUS_INDEX_MODIFIED - | git.GIT_STATUS_INDEX_DELETED): + if status & ( + git.GIT_STATUS_INDEX_NEW + | git.GIT_STATUS_INDEX_MODIFIED + | git.GIT_STATUS_INDEX_DELETED + ): index_column = 'I' r = wt_column + index_column + untracked_column return r if r != ' ' else None diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 7ce152fb..995bc1f6 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -128,14 +128,16 @@ class Spec(object): def check_type(self, value, context_mark, data, context, echoerr, types): if type(value.value) not in types: - echoerr(context=self.cmsg.format(key=context_key(context)), - context_mark=context_mark, - problem='{0!r} must be a {1} instance, not {2}'.format( - value, - list_sep.join((t.__name__ for t in types)), - type(value.value).__name__ - ), - problem_mark=value.mark) + echoerr( + context=self.cmsg.format(key=context_key(context)), + context_mark=context_mark, + problem='{0!r} must be a {1} instance, not {2}'.format( + value, + list_sep.join((t.__name__ for t in types)), + type(value.value).__name__ + ), + problem_mark=value.mark + ) return False, True return True, False @@ -143,9 +145,9 @@ class Spec(object): proceed, echo, hadproblem = func(value, data, context, echoerr) if echo and hadproblem: echoerr(context=self.cmsg.format(key=context_key(context)), - context_mark=context_mark, - problem=msg_func(value), - problem_mark=value.mark) + context_mark=context_mark, + problem=msg_func(value), + problem_mark=value.mark) return proceed, hadproblem def check_list(self, value, context_mark, data, context, echoerr, item_func, msg_func): @@ -165,9 +167,9 @@ class Spec(object): proceed, echo, fhadproblem = item_func(item, data, context, echoerr) if echo and fhadproblem: echoerr(context=self.cmsg.format(key=context_key(context) + '/list item ' + unicode(i)), - context_mark=value.mark, - problem=msg_func(item), - problem_mark=item.mark) + context_mark=value.mark, + problem=msg_func(item), + problem_mark=item.mark) if fhadproblem: hadproblem = True if not proceed: @@ -376,9 +378,9 @@ class Spec(object): if not valspec.isoptional: hadproblem = True echoerr(context=self.cmsg.format(key=context_key(context)), - context_mark=None, - problem='required key is missing: {0}'.format(key), - problem_mark=value.mark) + context_mark=None, + problem='required key is missing: {0}'.format(key), + problem_mark=value.mark) for key in value.keys(): if key not in self.keys: for keyfunc, vali in self.uspecs: @@ -405,9 +407,9 @@ class Spec(object): hadproblem = True if self.ufailmsg: echoerr(context=self.cmsg.format(key=context_key(context)), - context_mark=None, - problem=self.ufailmsg(key), - problem_mark=key.mark) + context_mark=None, + problem=self.ufailmsg(key), + problem_mark=key.mark) return True, hadproblem @@ -435,19 +437,19 @@ def check_matcher_func(ext, match_name, data, context, echoerr): func = getattr(__import__(str(match_module), fromlist=[str(match_function)]), str(match_function)) except ImportError: echoerr(context='Error while loading matcher functions', - problem='failed to load module {0}'.format(match_module), - problem_mark=match_name.mark) + problem='failed to load module {0}'.format(match_module), + problem_mark=match_name.mark) return True, True except AttributeError: echoerr(context='Error while loading matcher functions', - problem='failed to load matcher function {0}'.format(match_function), - problem_mark=match_name.mark) + problem='failed to load matcher function {0}'.format(match_function), + problem_mark=match_name.mark) return True, True if not callable(func): echoerr(context='Error while loading matcher functions', - problem='loaded "function" {0} is not callable'.format(match_function), - problem_mark=match_name.mark) + problem='loaded "function" {0} is not callable'.format(match_function), + problem_mark=match_name.mark) return True, True if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'): @@ -470,15 +472,15 @@ def check_ext(ext, data, context, echoerr): if ext not in data['lists']['exts']: hadproblem = True echoerr(context='Error while loading {0} extension configuration'.format(ext), - context_mark=ext.mark, - problem='extension configuration does not exist') + context_mark=ext.mark, + problem='extension configuration does not exist') else: for typ in ('themes', 'colorschemes'): if ext not in data['configs'][typ] and not data['configs']['top_' + typ]: hadproblem = True echoerr(context='Error while loading {0} extension configuration'.format(ext), - context_mark=ext.mark, - problem='{0} configuration does not exist'.format(typ)) + context_mark=ext.mark, + problem='{0} configuration does not exist'.format(typ)) else: hadsomedirs = True return hadsomedirs, hadproblem @@ -492,14 +494,16 @@ def check_config(d, theme, data, context, echoerr): ext = context[-3][0] if ext not in data['lists']['exts']: echoerr(context='Error while loading {0} extension configuration'.format(ext), - context_mark=ext.mark, - problem='extension configuration does not exist') + context_mark=ext.mark, + problem='extension configuration does not exist') return True, False, True - if ((ext not in data['configs'][d] or theme not in data['configs'][d][ext]) - and theme not in data['configs']['top_' + d]): + if ( + (ext not in data['configs'][d] or theme not in data['configs'][d][ext]) + and theme not in data['configs']['top_' + d] + ): echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext), - problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme), - problem_mark=theme.mark) + problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme), + problem_mark=theme.mark) return True, False, True return True, False, False @@ -507,9 +511,9 @@ def check_config(d, theme, data, context, echoerr): def check_top_theme(theme, data, context, echoerr): if theme not in data['configs']['top_themes']: echoerr(context='Error while checking extension configuration (key {key})'.format(key=context_key(context)), - context_mark=context[-2][0].mark, - problem='failed to find top theme {0}'.format(theme), - problem_mark=theme.mark) + context_mark=context[-2][0].mark, + problem='failed to find top theme {0}'.format(theme), + problem_mark=theme.mark) return True, False, True return True, False, False @@ -778,8 +782,8 @@ def check_key_compatibility(segment, data, context, echoerr): if segment_type not in type_keys: echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='found segment with unknown type {0}'.format(segment_type), - problem_mark=segment_type.mark) + problem='found segment with unknown type {0}'.format(segment_type), + problem_mark=segment_type.mark) return False, False, True hadproblem = False @@ -828,8 +832,8 @@ def check_segment_module(module, data, context, echoerr): if echoerr.logger.level >= logging.DEBUG: echoerr.logger.exception(e) echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='failed to import module {0}'.format(module), - problem_mark=module.mark) + problem='failed to import module {0}'.format(module), + problem_mark=module.mark) return True, False, True return True, False, False @@ -878,19 +882,19 @@ def import_segment(name, data, context, echoerr, module=None): func = getattr(__import__(str(module), fromlist=[str(name)]), str(name)) except ImportError: echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='failed to import module {0}'.format(module), - problem_mark=module.mark) + problem='failed to import module {0}'.format(module), + problem_mark=module.mark) return None except AttributeError: echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), - problem='failed to load function {0} from module {1}'.format(name, module), - problem_mark=name.mark) + problem='failed to load function {0} from module {1}'.format(name, module), + problem_mark=name.mark) return None if not callable(func): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='imported "function" {0} from module {1} is not callable'.format(name, module), - problem_mark=module.mark) + problem='imported "function" {0} from module {1} is not callable'.format(name, module), + problem_mark=module.mark) return None return func @@ -933,11 +937,15 @@ def check_segment_name(name, data, context, echoerr): if hl_groups: greg = re.compile(r'``([^`]+)``( \(gradient\))?') - hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] - for s in (list_sep.join(hl_groups)).split(', ')] + hl_groups = [ + [greg.match(subs).groups() for subs in s.split(' or ')] + for s in (list_sep.join(hl_groups)).split(', ') + ] for required_pack in hl_groups: - rs = [hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) - for hl_group, gradient in required_pack] + rs = [ + hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) + for hl_group, gradient in required_pack + ] if all(rs): echoerr( context='Error while checking theme (key {key})'.format(key=context_key(context)), @@ -983,8 +991,8 @@ def check_segment_name(name, data, context, echoerr): and not any(((name in theme.get('segment_data', {})) for theme in data['top_themes'].values())) ): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), - problem='found useless use of name key (such name is not present in theme/segment_data)', - problem_mark=name.mark) + problem='found useless use of name key (such name is not present in theme/segment_data)', + problem_mark=name.mark) return True, False, False @@ -1025,14 +1033,14 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): r.append(colorscheme) continue if allow_gradients == 'force' and not hadgradient: - echoerr( - context='Error while checking highlight group in theme (key {key})'.format( - key=context_key(context)), - context_mark=getattr(hl_group, 'mark', None), - problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), - problem_mark=group_config.mark - ) - r.append(colorscheme) + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context_key(context)), + context_mark=getattr(hl_group, 'mark', None), + problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), + problem_mark=group_config.mark + ) + r.append(colorscheme) return r @@ -1109,8 +1117,8 @@ def check_segment_data_key(key, data, context, echoerr): else: if data['theme_type'] != 'top': echoerr(context='Error while checking segment data', - problem='found key {0} that cannot be associated with any segment'.format(key), - problem_mark=key.mark) + problem='found key {0} that cannot be associated with any segment'.format(key), + problem_mark=key.mark) return True, False, True return True, False, False @@ -1141,9 +1149,9 @@ def check_args_variant(func, args, data, context, echoerr): if not all_args >= present_args: echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), - context_mark=args.mark, - problem='found unknown keys: {0}'.format(list_sep.join(present_args - all_args)), - problem_mark=next(iter(present_args - all_args)).mark) + context_mark=args.mark, + problem='found unknown keys: {0}'.format(list_sep.join(present_args - all_args)), + problem_mark=next(iter(present_args - all_args)).mark) hadproblem = True if isinstance(func, ThreadedSegment): @@ -1179,8 +1187,8 @@ def check_args(get_functions, args, data, context, echoerr): new_echoerr.echo_all() else: echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)), - context_mark=context[-2][1].mark, - problem='no suitable segments found') + context_mark=context[-2][1].mark, + problem='no suitable segments found') return True, False, hadproblem diff --git a/powerline/lint/markedjson/composer.py b/powerline/lint/markedjson/composer.py index 303e6f23..25e60109 100644 --- a/powerline/lint/markedjson/composer.py +++ b/powerline/lint/markedjson/composer.py @@ -38,9 +38,12 @@ class Composer: # Ensure that the stream contains no more documents. if not self.check_event(StreamEndEvent): event = self.get_event() - raise ComposerError("expected a single document in the stream", - document.start_mark, "but found another document", - event.start_mark) + raise ComposerError( + "expected a single document in the stream", + document.start_mark, + "but found another document", + event.start_mark + ) # Drop the STREAM-END event. self.get_event() @@ -75,8 +78,7 @@ class Composer: tag = event.tag if tag is None or tag == '!': tag = self.resolve(ScalarNode, event.value, event.implicit, event.start_mark) - node = ScalarNode(tag, event.value, - event.start_mark, event.end_mark, style=event.style) + node = ScalarNode(tag, event.value, event.start_mark, event.end_mark, style=event.style) return node def compose_sequence_node(self): @@ -84,9 +86,7 @@ class Composer: tag = start_event.tag if tag is None or tag == '!': tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) + node = SequenceNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) index = 0 while not self.check_event(SequenceEndEvent): node.value.append(self.compose_node(node, index)) @@ -100,17 +100,15 @@ class Composer: tag = start_event.tag if tag is None or tag == '!': tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) + node = MappingNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) while not self.check_event(MappingEndEvent): - #key_event = self.peek_event() + # key_event = self.peek_event() item_key = self.compose_node(node, None) - #if item_key in node.value: - # raise ComposerError("while composing a mapping", start_event.start_mark, - # "found duplicate key", key_event.start_mark) + # if item_key in node.value: + # raise ComposerError("while composing a mapping", start_event.start_mark, + # "found duplicate key", key_event.start_mark) item_value = self.compose_node(node, item_key) - #node.value[item_key] = item_value + # node.value[item_key] = item_value node.value.append((item_key, item_value)) end_event = self.get_event() node.end_mark = end_event.end_mark diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index bdc5c6e3..f887d9e2 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -95,39 +95,53 @@ class BaseConstructor: @marked def construct_scalar(self, node): if not isinstance(node, ScalarNode): - raise ConstructorError(None, None, - "expected a scalar node, but found %s" % node.id, - node.start_mark) + raise ConstructorError( + None, None, + "expected a scalar node, but found %s" % node.id, + node.start_mark + ) return node.value def construct_sequence(self, node, deep=False): if not isinstance(node, SequenceNode): - raise ConstructorError(None, None, - "expected a sequence node, but found %s" % node.id, - node.start_mark) - return [self.construct_object(child, deep=deep) - for child in node.value] + raise ConstructorError( + None, None, + "expected a sequence node, but found %s" % node.id, + node.start_mark + ) + return [ + self.construct_object(child, deep=deep) + for child in node.value + ] @marked def construct_mapping(self, node, deep=False): if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) + raise ConstructorError( + None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark + ) mapping = {} for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) if not isinstance(key, collections.Hashable): - self.echoerr('While constructing a mapping', node.start_mark, - 'found unhashable key', key_node.start_mark) + self.echoerr( + 'While constructing a mapping', node.start_mark, + 'found unhashable key', key_node.start_mark + ) continue elif type(key.value) != unicode: - self.echoerr('Error while constructing a mapping', node.start_mark, - 'found key that is not a string', key_node.start_mark) + self.echoerr( + 'Error while constructing a mapping', node.start_mark, + 'found key that is not a string', key_node.start_mark + ) continue elif key in mapping: - self.echoerr('Error while constructing a mapping', node.start_mark, - 'found duplicate key', key_node.start_mark) + self.echoerr( + 'Error while constructing a mapping', node.start_mark, + 'found duplicate key', key_node.start_mark + ) continue value = self.construct_object(value_node, deep=deep) mapping[key] = value @@ -135,7 +149,7 @@ class BaseConstructor: @classmethod def add_constructor(cls, tag, constructor): - if not 'yaml_constructors' in cls.__dict__: + if 'yaml_constructors' not in cls.__dict__: cls.yaml_constructors = cls.yaml_constructors.copy() cls.yaml_constructors[tag] = constructor @@ -162,19 +176,24 @@ class Constructor(BaseConstructor): submerge = [] for subnode in value_node.value: if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found %s" - % subnode.id, subnode.start_mark) + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found %s" % subnode.id, + subnode.start_mark + ) self.flatten_mapping(subnode) submerge.append(subnode.value) submerge.reverse() for value in submerge: merge.extend(value) else: - raise ConstructorError("while constructing a mapping", node.start_mark, - "expected a mapping or list of mappings for merging, but found %s" - % value_node.id, value_node.start_mark) + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + ("expected a mapping or list of mappings for merging, but found %s" % value_node.id), + value_node.start_mark + ) elif key_node.tag == 'tag:yaml.org,2002:value': key_node.tag = 'tag:yaml.org,2002:str' index += 1 @@ -237,9 +256,11 @@ class Constructor(BaseConstructor): data.update(value) def construct_undefined(self, node): - raise ConstructorError(None, None, - "could not determine a constructor for the tag %r" % node.tag, - node.start_mark) + raise ConstructorError( + None, None, + "could not determine a constructor for the tag %r" % node.tag, + node.start_mark + ) Constructor.add_constructor( diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index 781b912b..1e1c7214 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -53,13 +53,15 @@ class Mark: break snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer + 1:end]] snippet = [strtrans(s) for s in snippet] - return (' ' * indent + head + ''.join(snippet) + tail + '\n' - + ' ' * (indent + len(head) + len(snippet[0])) + '^') + return ( + ' ' * indent + head + ''.join(snippet) + tail + '\n' + + ' ' * (indent + len(head) + len(snippet[0])) + '^' + ) def __str__(self): snippet = self.get_snippet() - where = (" in \"%s\", line %d, column %d" - % (self.name, self.line + 1, self.column + 1)) + where = (" in \"%s\", line %d, column %d" % ( + self.name, self.line + 1, self.column + 1)) if snippet is not None: where += ":\n" + snippet if type(where) is str: @@ -77,11 +79,15 @@ def format_error(context=None, context_mark=None, problem=None, problem_mark=Non lines = [] if context is not None: lines.append(context) - if context_mark is not None \ - and (problem is None or problem_mark is None - or context_mark.name != problem_mark.name - or context_mark.line != problem_mark.line - or context_mark.column != problem_mark.column): + if ( + context_mark is not None + and ( + problem is None or problem_mark is None + or context_mark.name != problem_mark.name + or context_mark.line != problem_mark.line + or context_mark.column != problem_mark.column + ) + ): lines.append(str(context_mark)) if problem is not None: lines.append(problem) @@ -93,7 +99,5 @@ def format_error(context=None, context_mark=None, problem=None, problem_mark=Non class MarkedError(Exception): - def __init__(self, context=None, context_mark=None, - problem=None, problem_mark=None, note=None): - Exception.__init__(self, format_error(context, context_mark, problem, - problem_mark, note)) + def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None): + Exception.__init__(self, format_error(context, context_mark, problem, problem_mark, note)) diff --git a/powerline/lint/markedjson/events.py b/powerline/lint/markedjson/events.py index 47e2667f..587c5ae4 100644 --- a/powerline/lint/markedjson/events.py +++ b/powerline/lint/markedjson/events.py @@ -7,10 +7,14 @@ class Event(object): self.end_mark = end_mark def __repr__(self): - attributes = [key for key in ['implicit', 'value'] - if hasattr(self, key)] - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) + attributes = [ + key for key in ['implicit', 'value'] + if hasattr(self, key) + ] + arguments = ', '.join([ + '%s=%r' % (key, getattr(self, key)) + for key in attributes + ]) return '%s(%s)' % (self.__class__.__name__, arguments) @@ -21,8 +25,7 @@ class NodeEvent(Event): class CollectionStartEvent(NodeEvent): - def __init__(self, implicit, start_mark=None, end_mark=None, - flow_style=None): + def __init__(self, implicit, start_mark=None, end_mark=None, flow_style=None): self.tag = None self.implicit = implicit self.start_mark = start_mark @@ -49,8 +52,7 @@ class StreamEndEvent(Event): class DocumentStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None, version=None, tags=None): + def __init__(self, start_mark=None, end_mark=None, explicit=None, version=None, tags=None): self.start_mark = start_mark self.end_mark = end_mark self.explicit = explicit @@ -59,8 +61,7 @@ class DocumentStartEvent(Event): class DocumentEndEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None): + def __init__(self, start_mark=None, end_mark=None, explicit=None): self.start_mark = start_mark self.end_mark = end_mark self.explicit = explicit @@ -71,8 +72,7 @@ class AliasEvent(NodeEvent): class ScalarEvent(NodeEvent): - def __init__(self, implicit, value, - start_mark=None, end_mark=None, style=None): + def __init__(self, implicit, value, start_mark=None, end_mark=None, style=None): self.tag = None self.implicit = implicit self.value = value diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 457b66f6..6c619c56 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -110,11 +110,15 @@ def gen_marked_value(value, mark, use_special_classes=True): elif func not in set(('__init__', '__new__', '__getattribute__')): if func in set(('__eq__',)): # HACK to make marked dictionaries always work - exec (('def {0}(self, *args):\n' - ' return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args])').format(func)) + exec (( + 'def {0}(self, *args):\n' + ' return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args])' + ).format(func)) else: - exec (('def {0}(self, *args, **kwargs):\n' - ' return self.value.{0}(*args, **kwargs)\n').format(func)) + exec (( + 'def {0}(self, *args, **kwargs):\n' + ' return self.value.{0}(*args, **kwargs)\n' + ).format(func)) classcache[value.__class__] = Marked return Marked(value, mark) diff --git a/powerline/lint/markedjson/nodes.py b/powerline/lint/markedjson/nodes.py index 11ebb3ea..9325a64c 100644 --- a/powerline/lint/markedjson/nodes.py +++ b/powerline/lint/markedjson/nodes.py @@ -7,18 +7,18 @@ class Node(object): def __repr__(self): value = self.value - #if isinstance(value, list): - # if len(value) == 0: - # value = '' - # elif len(value) == 1: - # value = '<1 item>' - # else: - # value = '<%d items>' % len(value) - #else: - # if len(value) > 75: - # value = repr(value[:70]+u' ... ') - # else: - # value = repr(value) + # if isinstance(value, list): + # if len(value) == 0: + # value = '' + # elif len(value) == 1: + # value = '<1 item>' + # else: + # value = '<%d items>' % len(value) + # else: + # if len(value) > 75: + # value = repr(value[:70]+u' ... ') + # else: + # value = repr(value) value = repr(value) return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) @@ -26,8 +26,7 @@ class Node(object): class ScalarNode(Node): id = 'scalar' - def __init__(self, tag, value, - start_mark=None, end_mark=None, style=None): + def __init__(self, tag, value, start_mark=None, end_mark=None, style=None): self.tag = tag self.value = value self.start_mark = start_mark @@ -36,8 +35,7 @@ class ScalarNode(Node): class CollectionNode(Node): - def __init__(self, tag, value, - start_mark=None, end_mark=None, flow_style=None): + def __init__(self, tag, value, start_mark=None, end_mark=None, flow_style=None): self.tag = tag self.value = value self.start_mark = start_mark diff --git a/powerline/lint/markedjson/parser.py b/powerline/lint/markedjson/parser.py index 998de6db..aa5f7ade 100644 --- a/powerline/lint/markedjson/parser.py +++ b/powerline/lint/markedjson/parser.py @@ -58,8 +58,7 @@ class Parser: def parse_stream_start(self): # Parse the stream start. token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, - encoding=token.encoding) + event = StreamStartEvent(token.start_mark, token.end_mark, encoding=token.encoding) # Prepare the next state. self.state = self.parse_implicit_document_start @@ -86,9 +85,10 @@ class Parser: # Parse an explicit document. if not self.check_token(StreamEndToken): token = self.peek_token() - self.echoerr(None, None, - "expected '', but found %r" % token.id, - token.start_mark) + self.echoerr( + None, None, + ("expected '', but found %r" % token.id), token.start_mark + ) return StreamEndEvent(token.start_mark, token.end_mark) else: # Parse the end of the stream. @@ -127,24 +127,23 @@ class Parser: implicit = (True, False) else: implicit = (False, True) - event = ScalarEvent(implicit, token.value, - start_mark, end_mark, style=token.style) + event = ScalarEvent(implicit, token.value, start_mark, end_mark, style=token.style) self.state = self.states.pop() elif self.check_token(FlowSequenceStartToken): end_mark = self.peek_token().end_mark - event = SequenceStartEvent(implicit, - start_mark, end_mark, flow_style=True) + event = SequenceStartEvent(implicit, start_mark, end_mark, flow_style=True) self.state = self.parse_flow_sequence_first_entry elif self.check_token(FlowMappingStartToken): end_mark = self.peek_token().end_mark - event = MappingStartEvent(implicit, - start_mark, end_mark, flow_style=True) + event = MappingStartEvent(implicit, start_mark, end_mark, flow_style=True) self.state = self.parse_flow_mapping_first_key else: token = self.peek_token() - raise ParserError("while parsing a flow node", start_mark, - "expected the node content, but found %r" % token.id, - token.start_mark) + raise ParserError( + "while parsing a flow node", start_mark, + "expected the node content, but found %r" % token.id, + token.start_mark + ) return event def parse_flow_sequence_first_entry(self): @@ -159,12 +158,16 @@ class Parser: self.get_token() if self.check_token(FlowSequenceEndToken): token = self.peek_token() - self.echoerr("While parsing a flow sequence", self.marks[-1], - "expected sequence value, but got %r" % token.id, token.start_mark) + self.echoerr( + "While parsing a flow sequence", self.marks[-1], + ("expected sequence value, but got %r" % token.id), token.start_mark + ) else: token = self.peek_token() - raise ParserError("while parsing a flow sequence", self.marks[-1], - "expected ',' or ']', but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow sequence", self.marks[-1], + ("expected ',' or ']', but got %r" % token.id), token.start_mark + ) if not self.check_token(FlowSequenceEndToken): self.states.append(self.parse_flow_sequence_entry) @@ -192,22 +195,27 @@ class Parser: self.get_token() if self.check_token(FlowMappingEndToken): token = self.peek_token() - self.echoerr("While parsing a flow mapping", self.marks[-1], - "expected mapping key, but got %r" % token.id, token.start_mark) + self.echoerr( + "While parsing a flow mapping", self.marks[-1], + ("expected mapping key, but got %r" % token.id), token.start_mark + ) else: token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ',' or '}', but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow mapping", self.marks[-1], + ("expected ',' or '}', but got %r" % token.id), token.start_mark + ) if self.check_token(KeyToken): token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowMappingEndToken): + if not self.check_token(ValueToken, FlowEntryToken, FlowMappingEndToken): self.states.append(self.parse_flow_mapping_value) return self.parse_node() else: token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected value, but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow mapping", self.marks[-1], + ("expected value, but got %r" % token.id), token.start_mark + ) elif not self.check_token(FlowMappingEndToken): token = self.peek_token() expect_key = self.check_token(ValueToken, FlowEntryToken) @@ -216,12 +224,16 @@ class Parser: expect_key = self.check_token(ValueToken) if expect_key: - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected string key, but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow mapping", self.marks[-1], + ("expected string key, but got %r" % token.id), token.start_mark + ) else: token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ':', but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow mapping", self.marks[-1], + ("expected ':', but got %r" % token.id), token.start_mark + ) token = self.get_token() event = MappingEndEvent(token.start_mark, token.end_mark) self.state = self.states.pop() @@ -236,5 +248,7 @@ class Parser: return self.parse_node() token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected mapping value, but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow mapping", self.marks[-1], + ("expected mapping value, but got %r" % token.id), token.start_mark + ) diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index f59605ee..32f5d7b6 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -89,9 +89,11 @@ class Reader(object): match = NON_PRINTABLE.search(data) if match: self.update_pointer(match.start()) - raise ReaderError('while reading from stream', None, - 'found special characters which are not allowed', - Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer)) + raise ReaderError( + 'while reading from stream', None, + 'found special characters which are not allowed', + Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer) + ) def update(self, length): if self.raw_buffer is None: @@ -102,8 +104,7 @@ class Reader(object): if not self.eof: self.update_raw() try: - data, converted = self.raw_decode(self.raw_buffer, - 'strict', self.eof) + data, converted = self.raw_decode(self.raw_buffer, 'strict', self.eof) except UnicodeDecodeError as exc: character = self.raw_buffer[exc.start] position = self.stream_pointer - len(self.raw_buffer) + exc.start @@ -112,9 +113,11 @@ class Reader(object): self.full_buffer += data + '<' + str(ord(character)) + '>' self.raw_buffer = self.raw_buffer[converted:] self.update_pointer(exc.start - 1) - raise ReaderError('while reading from stream', None, - 'found character #x%04x that cannot be decoded by UTF-8 codec' % ord(character), - Mark(self.name, self.line, self.column, self.full_buffer, position)) + raise ReaderError( + 'while reading from stream', None, + 'found character #x%04x that cannot be decoded by UTF-8 codec' % ord(character), + Mark(self.name, self.line, self.column, self.full_buffer, position) + ) self.buffer += data self.full_buffer += data self.raw_buffer = self.raw_buffer[converted:] diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py index e453f61e..0196c70b 100644 --- a/powerline/lint/markedjson/resolver.py +++ b/powerline/lint/markedjson/resolver.py @@ -24,7 +24,7 @@ class BaseResolver: @classmethod def add_implicit_resolver(cls, tag, regexp, first): - if not 'yaml_implicit_resolvers' in cls.__dict__: + if 'yaml_implicit_resolvers' not in cls.__dict__: cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() if first is None: first = [None] @@ -39,8 +39,7 @@ class BaseResolver: if current_node: depth = len(self.resolver_prefix_paths) for path, kind in self.resolver_prefix_paths[-1]: - if self.check_resolver_prefix(depth, path, kind, - current_node, current_index): + if self.check_resolver_prefix(depth, path, kind, current_node, current_index): if len(path) > depth: prefix_paths.append((path, kind)) else: @@ -60,8 +59,7 @@ class BaseResolver: self.resolver_exact_paths.pop() self.resolver_prefix_paths.pop() - def check_resolver_prefix(self, depth, path, kind, - current_node, current_index): + def check_resolver_prefix(self, depth, path, kind, current_node, current_index): node_check, index_check = path[depth - 1] if isinstance(node_check, str): if current_node.tag != node_check: @@ -75,8 +73,7 @@ class BaseResolver: and current_index is None): return if isinstance(index_check, str): - if not (isinstance(current_index, ScalarNode) - and index_check == current_index.value): + if not (isinstance(current_index, ScalarNode) and index_check == current_index.value): return elif isinstance(index_check, int) and not isinstance(index_check, bool): if index_check != current_index: @@ -94,9 +91,11 @@ class BaseResolver: if regexp.match(value): return tag else: - self.echoerr('While resolving plain scalar', None, - 'expected floating-point value, integer, null or boolean, but got %r' % value, - mark) + self.echoerr( + 'While resolving plain scalar', None, + 'expected floating-point value, integer, null or boolean, but got %r' % value, + mark + ) return self.DEFAULT_SCALAR_TAG if kind is ScalarNode: return self.DEFAULT_SCALAR_TAG diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index 2183f651..e4defc26 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -48,9 +48,9 @@ class Scanner: # input data to Unicode. It also adds NUL to the end. # # Reader supports the following methods - # self.peek(i=0) # peek the next i-th character - # self.prefix(l=1) # peek the next l characters - # self.forward(l=1) # read the next l characters and move the pointer. + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. # Had we reached the end of the stream? self.done = False @@ -83,7 +83,7 @@ class Scanner: # Keep track of possible simple keys. This is a dictionary. The key # is `flow_level`; there can be no more that one possible simple key # for each level. The value is a SimpleKey record: - # (token_number, index, line, column, mark) + # (token_number, index, line, column, mark) # A simple key may start with SCALAR(flow), '[', or '{' tokens. self.possible_simple_keys = {} @@ -179,9 +179,11 @@ class Scanner: return self.fetch_plain() # No? It's an error. Let's produce a nice error message. - raise ScannerError("while scanning for the next token", None, - "found character %r that cannot start any token" % ch, - self.get_mark()) + raise ScannerError( + "while scanning for the next token", None, + "found character %r that cannot start any token" % ch, + self.get_mark() + ) # Simple keys treatment. @@ -189,10 +191,10 @@ class Scanner: # Return the number of the nearest possible simple key. Actually we # don't need to loop through the whole dictionary. We may replace it # with the following code: - # if not self.possible_simple_keys: - # return None - # return self.possible_simple_keys[ - # min(self.possible_simple_keys.keys())].token_number + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number min_token_number = None for level in self.possible_simple_keys: key = self.possible_simple_keys[level] @@ -214,15 +216,14 @@ class Scanner: def save_possible_simple_key(self): # The next token may start a simple key. We check if it's possible # and save its position. This function is called for - # SCALAR(flow), '[', and '{'. + # SCALAR(flow), '[', and '{'. # The next token might be a simple key. Let's save it's number and # position. if self.allow_simple_key: self.remove_possible_simple_key() token_number = self.tokens_taken + len(self.tokens) - key = SimpleKey(token_number, - self.index, self.line, self.column, self.get_mark()) + key = SimpleKey(token_number, self.index, self.line, self.column, self.get_mark()) self.possible_simple_keys[self.flow_level] = key def remove_possible_simple_key(self): @@ -311,8 +312,7 @@ class Scanner: # Add KEY. key = self.possible_simple_keys[self.flow_level] del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number - self.tokens_taken, - KeyToken(key.mark, key.mark)) + self.tokens.insert(key.token_number - self.tokens_taken, KeyToken(key.mark, key.mark)) # There cannot be two simple keys one after another. self.allow_simple_key = False @@ -423,15 +423,20 @@ class Scanner: self.forward() for k in range(length): if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexdecimal numbers, but found %r" % - (length, self.peek(k)), self.get_mark()) + raise ScannerError( + "while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexdecimal numbers, but found %r" % ( + length, self.peek(k)), + self.get_mark() + ) code = int(self.prefix(length), 16) chunks.append(chr(code)) self.forward(length) else: - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "found unknown escape character %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a double-quoted scalar", start_mark, + ("found unknown escape character %r" % ch), self.get_mark() + ) else: return chunks @@ -445,11 +450,15 @@ class Scanner: self.forward(length) ch = self.peek() if ch == '\0': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected end of stream", self.get_mark()) + raise ScannerError( + "while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark() + ) elif ch == '\n': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected line end", self.get_mark()) + raise ScannerError( + "while scanning a quoted scalar", start_mark, + "found unexpected line end", self.get_mark() + ) else: chunks.append(whitespaces) return chunks diff --git a/powerline/lint/markedjson/tokens.py b/powerline/lint/markedjson/tokens.py index 8c5b38c8..15b1836c 100644 --- a/powerline/lint/markedjson/tokens.py +++ b/powerline/lint/markedjson/tokens.py @@ -4,19 +4,22 @@ class Token(object): self.end_mark = end_mark def __repr__(self): - attributes = [key for key in self.__dict__ - if not key.endswith('_mark')] + attributes = [ + key for key in self.__dict__ + if not key.endswith('_mark') + ] attributes.sort() - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) + arguments = ', '.join([ + '%s=%r' % (key, getattr(self, key)) + for key in attributes + ]) return '%s(%s)' % (self.__class__.__name__, arguments) class StreamStartToken(Token): id = '' - def __init__(self, start_mark=None, end_mark=None, - encoding=None): + def __init__(self, start_mark=None, end_mark=None, encoding=None): self.start_mark = start_mark self.end_mark = end_mark self.encoding = encoding diff --git a/powerline/renderer.py b/powerline/renderer.py index 63bc1454..ec84e198 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -90,13 +90,13 @@ class Renderer(object): ''' def __init__(self, - theme_config, - local_themes, - theme_kwargs, - colorscheme, - pl, - ambiwidth=1, - **options): + theme_config, + local_themes, + theme_kwargs, + colorscheme, + pl, + ambiwidth=1, + **options): self.__dict__.update(options) self.theme_config = theme_config theme_kwargs['pl'] = pl diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index f4c2bf74..50586194 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -154,12 +154,14 @@ class VimRenderer(Renderer): hl_group['attr'].append('italic') if attr & ATTR_UNDERLINE: hl_group['attr'].append('underline') - hl_group['name'] = ('Pl_' + - str(hl_group['ctermfg']) + '_' + - str(hl_group['guifg']) + '_' + - str(hl_group['ctermbg']) + '_' + - str(hl_group['guibg']) + '_' + - ''.join(hl_group['attr'])) + hl_group['name'] = ( + 'Pl_' + + str(hl_group['ctermfg']) + '_' + + str(hl_group['guifg']) + '_' + + str(hl_group['ctermbg']) + '_' + + str(hl_group['guibg']) + '_' + + ''.join(hl_group['attr']) + ) self.hl_groups[(fg, bg, attr)] = hl_group vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( group=hl_group['name'], diff --git a/powerline/segments/common.py b/powerline/segments/common.py index eaef499f..da07c7a0 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -368,9 +368,11 @@ class WeatherSegment(ThreadedSegment): # only in .update() if not self.location: location_data = json.loads(urllib_read('http://freegeoip.net/json/')) - self.location = ','.join([location_data['city'], - location_data['region_code'], - location_data['country_code']]) + self.location = ','.join(( + location_data['city'], + location_data['region_code'], + location_data['country_code'] + )) query_data = { 'q': 'use "https://raw.githubusercontent.com/yql/yql-tables/master/weather/weather.bylocation.xml" as we;' diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index b8ebb286..4a1c4706 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -244,8 +244,10 @@ def file_directory(pl, segment_info, remove_scheme=True, shorten_user=True, shor name = name[len(match.group(0)) + 1:] # Remove scheme and colon file_directory = vim_funcs['fnamemodify'](name, ':h') else: - file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') - + (':.' if shorten_cwd else '') + ':h') + file_directory = vim_funcs['fnamemodify']( + name, + (':~' if shorten_user else '') + (':.' if shorten_cwd else '') + ':h' + ) if not file_directory: return None if shorten_home and file_directory.startswith('/home/'): @@ -507,7 +509,7 @@ def file_vcs_status(pl, segment_info, create_watcher): ret.append({ 'contents': status, 'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'], - }) + }) return ret diff --git a/powerline/shell.py b/powerline/shell.py index 25d07258..2d4152d2 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -38,8 +38,10 @@ class ShellPowerline(Powerline): if not local_themes: return {} - return dict(((key, {'config': self.load_theme_config(val)}) - for key, val in local_themes.items())) + return dict(( + (key, {'config': self.load_theme_config(val)}) + for key, val in local_themes.items() + )) def get_argparser(parser=None, *args, **kwargs): @@ -49,8 +51,10 @@ def get_argparser(parser=None, *args, **kwargs): p = parser(*args, **kwargs) p.add_argument('ext', nargs=1, help='Extension: application for which powerline command is launched (usually `shell\' or `tmux\')') p.add_argument('side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'), help='Side: `left\' and `right\' represent left and right side respectively, `above\' emits lines that are supposed to be printed just above the prompt and `aboveleft\' is like concatenating `above\' with `left\' with the exception that only one Python instance is used in this case.') - p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str, - help='Renderer module. Usually something like `.bash\' or `.zsh\', is supposed to be set only in shell-specific bindings file.') + p.add_argument( + '-r', '--renderer_module', metavar='MODULE', type=str, + help='Renderer module. Usually something like `.bash\' or `.zsh\', is supposed to be set only in shell-specific bindings file.' + ) p.add_argument('-w', '--width', type=int, help='Maximum prompt with. Triggers truncation of some segments') p.add_argument('--last_exit_code', metavar='INT', type=int, help='Last exit code') p.add_argument('--last_pipe_status', metavar='LIST', default='', type=lambda s: [int(status) for status in s.split()], help='Like above, but is supposed to contain space-separated array of statuses, representing exit statuses of commands in one pipe.') diff --git a/powerline/theme.py b/powerline/theme.py index d03b0a06..378f8495 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -25,13 +25,13 @@ def new_empty_segment_line(): class Theme(object): def __init__(self, - ext, - theme_config, - common_config, - pl, - main_theme_config=None, - run_once=False, - shutdown_event=None): + ext, + theme_config, + common_config, + pl, + main_theme_config=None, + run_once=False, + shutdown_event=None): self.dividers = theme_config['dividers'] self.dividers = dict(( (key, dict((k, u(v)) @@ -55,7 +55,7 @@ class Theme(object): theme_configs.append(main_theme_config) get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module')) for segdict in itertools.chain((theme_config['segments'],), - theme_config['segments'].get('above', ())): + theme_config['segments'].get('above', ())): self.segments.append(new_empty_segment_line()) for side in ['left', 'right']: for segment in segdict.get(side, []): diff --git a/powerline/vim.py b/powerline/vim.py index 1e83b2ce..2676ac29 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -74,8 +74,10 @@ class VimPowerline(Powerline): # Note: themes with non-[a-zA-Z0-9_] names are impossible to override # (though as far as I know exists() won’t throw). Won’t fix, use proper # theme names. - return _override_from(super(VimPowerline, self).load_theme_config(name), - 'powerline_theme_overrides__' + name) + return _override_from( + super(VimPowerline, self).load_theme_config(name), + 'powerline_theme_overrides__' + name + ) def get_local_themes(self, local_themes): self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) @@ -83,9 +85,13 @@ class VimPowerline(Powerline): if not local_themes: return {} - return dict(((None if key == '__tabline__' else self.get_matcher(key), - {'config': self.load_theme_config(val)}) - for key, val in local_themes.items())) + return dict(( + ( + (None if key == '__tabline__' else self.get_matcher(key)), + {'config': self.load_theme_config(val)} + ) + for key, val in local_themes.items()) + ) def get_config_paths(self): try: @@ -167,8 +173,7 @@ class VimPowerline(Powerline): @staticmethod def do_pyeval(): import __main__ - vim.command('return ' + json.dumps(eval(vim.eval('a:e'), - __main__.__dict__))) + vim.command('return ' + json.dumps(eval(vim.eval('a:e'), __main__.__dict__))) def setup_components(self, components): if components is None: @@ -208,10 +213,10 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): # pyeval() and vim.bindeval were both introduced in one patch if not hasattr(vim, 'bindeval') and can_replace_pyeval: vim.command((''' - function! PowerlinePyeval(e) - {pycmd} powerline.do_pyeval() - endfunction - ''').format(pycmd=pycmd)) + function! PowerlinePyeval(e) + {pycmd} powerline.do_pyeval() + endfunction + ''').format(pycmd=pycmd)) pyeval = 'PowerlinePyeval' powerline = VimPowerline(pyeval) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 8447ab2f..ff340982 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -13,8 +13,10 @@ class Pl(object): self.use_daemon_threads = True for meth in ('error', 'warn', 'debug', 'exception'): - exec (('def {0}(self, msg, *args, **kwargs):\n' - ' self.{0}s.append((kwargs.get("prefix") or self.prefix, msg, args, kwargs))\n').format(meth)) + exec (( + 'def {0}(self, msg, *args, **kwargs):\n' + ' self.{0}s.append((kwargs.get("prefix") or self.prefix, msg, args, kwargs))\n' + ).format(meth)) class Args(object): diff --git a/tests/test_segments.py b/tests/test_segments.py index 107d982c..60d05574 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -29,8 +29,9 @@ class TestShell(TestCase): def test_last_status(self): pl = Pl() segment_info = {'args': Args(last_exit_code=10)} - self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), - [{'contents': '10', 'highlight_group': 'exit_fail'}]) + self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), [ + {'contents': '10', 'highlight_group': 'exit_fail'} + ]) segment_info['args'].last_exit_code = 0 self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), None) segment_info['args'].last_exit_code = None @@ -222,18 +223,23 @@ class TestCommon(TestCase): branch = partial(common.branch, pl=pl, create_watcher=create_watcher) with replace_attr(common, 'guess', get_dummy_guess(status=lambda: None, directory='/tmp/tests')): with replace_attr(common, 'tree_status', lambda repo, pl: None): - self.assertEqual(branch(segment_info=segment_info, status_colors=False), - [{'highlight_group': ['branch'], 'contents': 'tests'}]) - self.assertEqual(branch(segment_info=segment_info, status_colors=True), - [{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}]) + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ + {'highlight_group': ['branch'], 'contents': 'tests'} + ]) + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [ + {'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']} + ]) with replace_attr(common, 'guess', get_dummy_guess(status=lambda: 'D ', directory='/tmp/tests')): with replace_attr(common, 'tree_status', lambda repo, pl: 'D '): - self.assertEqual(branch(segment_info=segment_info, status_colors=False), - [{'highlight_group': ['branch'], 'contents': 'tests'}]) - self.assertEqual(branch(segment_info=segment_info, status_colors=True), - [{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}]) - self.assertEqual(branch(segment_info=segment_info, status_colors=False), - [{'highlight_group': ['branch'], 'contents': 'tests'}]) + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ + {'highlight_group': ['branch'], 'contents': 'tests'} + ]) + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [ + {'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']} + ]) + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ + {'highlight_group': ['branch'], 'contents': 'tests'} + ]) with replace_attr(common, 'guess', lambda path, create_watcher: None): self.assertEqual(branch(segment_info=segment_info, status_colors=False), None) @@ -322,8 +328,9 @@ class TestCommon(TestCase): ose = OSError() ose.errno = 2 cwd[0] = ose - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), - [{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd'], 'draw_inner_divider': True}]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [ + {'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd'], 'draw_inner_divider': True} + ]) cwd[0] = OSError() self.assertRaises(OSError, common.cwd, pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2) cwd[0] = ValueError() @@ -415,14 +422,16 @@ class TestCommon(TestCase): pl = Pl() with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)): with replace_attr(common, '_cpu_count', lambda: 2): - self.assertEqual(common.system_load(pl=pl), - [{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, - {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 0}]) - self.assertEqual(common.system_load(pl=pl, format='{avg:.0f}', threshold_good=0, threshold_bad=1), - [{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, - {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}]) + self.assertEqual(common.system_load(pl=pl), [ + {'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}, + {'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 0} + ]) + self.assertEqual(common.system_load(pl=pl, format='{avg:.0f}', threshold_good=0, threshold_bad=1), [ + {'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 100}, + {'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0} + ]) def test_cpu_load_percent(self): pl = Pl() @@ -700,10 +709,12 @@ class TestVim(TestCase): pl = Pl() segment_info = vim_module._get_segment_info() self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), None) - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True), - [{'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']}]) - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True, no_file_text='X'), - [{'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']}]) + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True), [ + {'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']} + ]) + self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True, no_file_text='X'), [ + {'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']} + ]) with vim_module._with('buffer', '/tmp/abc') as segment_info: self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), 'abc') with vim_module._with('buffer', '/tmp/’’') as segment_info: @@ -721,14 +732,17 @@ class TestVim(TestCase): def test_file_opts(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_format(pl=pl, segment_info=segment_info), - [{'divider_highlight_group': 'background:divider', 'contents': 'unix'}]) - self.assertEqual(vim.file_encoding(pl=pl, segment_info=segment_info), - [{'divider_highlight_group': 'background:divider', 'contents': 'utf-8'}]) + self.assertEqual(vim.file_format(pl=pl, segment_info=segment_info), [ + {'divider_highlight_group': 'background:divider', 'contents': 'unix'} + ]) + self.assertEqual(vim.file_encoding(pl=pl, segment_info=segment_info), [ + {'divider_highlight_group': 'background:divider', 'contents': 'utf-8'} + ]) self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), None) with vim_module._with('bufoptions', filetype='python'): - self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), - [{'divider_highlight_group': 'background:divider', 'contents': 'python'}]) + self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), [ + {'divider_highlight_group': 'background:divider', 'contents': 'python'} + ]) def test_window_title(self): pl = Pl() @@ -745,8 +759,9 @@ class TestVim(TestCase): self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '1') vim_module._set_cursor(50, 0) self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '50') - self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info, gradient=True), - [{'contents': '50', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 50 * 100.0 / 101}]) + self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info, gradient=True), [ + {'contents': '50', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 50 * 100.0 / 101} + ]) finally: vim_module._bw(segment_info['bufnr']) @@ -768,8 +783,9 @@ class TestVim(TestCase): segment_info['buffer'][0:-1] = [str(i) for i in range(99)] vim_module._set_cursor(49, 0) self.assertEqual(vim.position(pl=pl, segment_info=segment_info), '50%') - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), - [{'contents': '50%', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 50.0}]) + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [ + {'contents': '50%', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 50.0} + ]) vim_module._set_cursor(0, 0) self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'Top') vim_module._set_cursor(97, 0) @@ -777,8 +793,9 @@ class TestVim(TestCase): segment_info['buffer'][0:-1] = [str(i) for i in range(2)] vim_module._set_cursor(0, 0) self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Todo') - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), - [{'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0}]) + self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [ + {'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0} + ]) finally: vim_module._bw(segment_info['bufnr']) @@ -805,16 +822,20 @@ class TestVim(TestCase): with vim_module._with('buffer', '/foo') as segment_info: with replace_attr(vim, 'guess', get_dummy_guess(status=lambda: None)): with replace_attr(vim, 'tree_status', lambda repo, pl: None): - self.assertEqual(branch(segment_info=segment_info, status_colors=False), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(branch(segment_info=segment_info, status_colors=True), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}]) + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ + {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'} + ]) + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [ + {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'} + ]) with replace_attr(vim, 'guess', get_dummy_guess(status=lambda: 'DU')): with replace_attr(vim, 'tree_status', lambda repo, pl: 'DU'): - self.assertEqual(branch(segment_info=segment_info, status_colors=False), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}]) - self.assertEqual(branch(segment_info=segment_info, status_colors=True), - [{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}]) + self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ + {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'} + ]) + self.assertEqual(branch(segment_info=segment_info, status_colors=True), [ + {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'} + ]) def test_file_vcs_status(self): pl = Pl() @@ -822,8 +843,9 @@ class TestVim(TestCase): file_vcs_status = partial(vim.file_vcs_status, pl=pl, create_watcher=create_watcher) with vim_module._with('buffer', '/foo') as segment_info: with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')): - self.assertEqual(file_vcs_status(segment_info=segment_info), - [{'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'}]) + self.assertEqual(file_vcs_status(segment_info=segment_info), [ + {'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'} + ]) with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: None)): self.assertEqual(file_vcs_status(segment_info=segment_info), None) with vim_module._with('buffer', '/bar') as segment_info: diff --git a/tests/vim.py b/tests/vim.py index 29da4415..f7e0f703 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -153,8 +153,10 @@ def _construct_result(r): elif isinstance(r, list): return [_construct_result(i) for i in r] elif isinstance(r, dict): - return dict(((_construct_result(k), _construct_result(v)) - for k, v in r.items())) + return dict(( + (_construct_result(k), _construct_result(v)) + for k, v in r.items() + )) return r diff --git a/tools/colors_find.py b/tools/colors_find.py index cf1ba1c5..cdc01e10 100755 --- a/tools/colors_find.py +++ b/tools/colors_find.py @@ -8,8 +8,10 @@ from colormath.color_diff import delta_e_cie2000 def get_lab(name, rgb): - rgb = sRGBColor(int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16), - is_upscaled=True) + rgb = sRGBColor( + int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16), + is_upscaled=True + ) lab = convert_color(rgb, LabColor) return name, lab diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index a667f301..b3c94615 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -26,8 +26,10 @@ def num2(s): def rgbint_to_lab(rgbint): - rgb = sRGBColor((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF, - is_upscaled=True) + rgb = sRGBColor( + (rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF, + is_upscaled=True + ) return convert_color(rgb, LabColor) @@ -52,8 +54,10 @@ def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset): def lab_gradient(slab, elab, soff, eoff, off): svals = slab.get_value_tuple() evals = elab.get_value_tuple() - return LabColor(*[linear_gradient(start_value, end_value, soff, eoff, off) - for start_value, end_value in zip(svals, evals)]) + return LabColor(*[ + linear_gradient(start_value, end_value, soff, eoff, off) + for start_value, end_value in zip(svals, evals) + ]) def generate_gradient_function(DATA): @@ -185,8 +189,10 @@ if __name__ == '__main__': steps = compute_steps(args.gradient, args.weights) - data = [(weight, args.gradient[i - 1], args.gradient[i]) - for weight, i in zip(steps, range(1, len(args.gradient)))] + data = [ + (weight, args.gradient[i - 1], args.gradient[i]) + for weight, i in zip(steps, range(1, len(args.gradient))) + ] gr_func = generate_gradient_function(data) gradient = [gr_func(y) for y in range(0, m)] From e2ffc8069b68ff5f80e05e445e2ab269501ef330 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 21:12:14 +0400 Subject: [PATCH 1250/1472] Add weather icons to various themes --- powerline/config_files/themes/ascii.json | 18 ++++++++++++++++++ powerline/config_files/themes/powerline.json | 18 ++++++++++++++++++ powerline/config_files/themes/unicode.json | 18 ++++++++++++++++++ .../config_files/themes/unicode_terminus.json | 18 ++++++++++++++++++ .../themes/unicode_terminus_condensed.json | 18 ++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index af0df089..ff7c6d76 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -61,6 +61,24 @@ "powerline.segments.common.hostname": { "before": "H " }, + "powerline.segments.common.weather": { + "args": { + "icons": { + "day": "DAY", + "blustery": "WIND", + "rainy": "RAIN", + "cloudy": "CLOUDS", + "snowy": "SNOW", + "stormy": "STORM", + "foggy": "FOG", + "sunny": "SUN", + "night": "NIGHT", + "windy": "WINDY", + "not_available": "NA", + "unknown": "UKN" + } + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index 0122a3d7..c854fc5f 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -61,6 +61,24 @@ "powerline.segments.common.hostname": { "before": " " }, + "powerline.segments.common.weather": { + "args": { + "icons": { + "day": "〇", + "blustery": "⚑", + "rainy": "☔", + "cloudy": "☁", + "snowy": "❅", + "stormy": "☈", + "foggy": "〰", + "sunny": "☼", + "night": "☾", + "windy": "☴", + "not_available": "�", + "unknown": "⚠" + } + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json index 0179f984..d4d2a35f 100644 --- a/powerline/config_files/themes/unicode.json +++ b/powerline/config_files/themes/unicode.json @@ -61,6 +61,24 @@ "powerline.segments.common.hostname": { "before": "⌂ " }, + "powerline.segments.common.weather": { + "args": { + "icons": { + "day": "〇", + "blustery": "⚑", + "rainy": "☔", + "cloudy": "☁", + "snowy": "❅", + "stormy": "☈", + "foggy": "〰", + "sunny": "☼", + "night": "☾", + "windy": "☴", + "not_available": "�", + "unknown": "⚠" + } + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json index c1310fca..79c21dae 100644 --- a/powerline/config_files/themes/unicode_terminus.json +++ b/powerline/config_files/themes/unicode_terminus.json @@ -61,6 +61,24 @@ "powerline.segments.common.hostname": { "before": "⌂ " }, + "powerline.segments.common.weather": { + "args": { + "icons": { + "day": "DAY", + "blustery": "WIND", + "rainy": "RAIN", + "cloudy": "CLOUDS", + "snowy": "SNOW", + "stormy": "STORM", + "foggy": "FOG", + "sunny": "SUN", + "night": "NIGHT", + "windy": "WINDY", + "not_available": "NA", + "unknown": "UKN" + } + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline/config_files/themes/unicode_terminus_condensed.json index e56309ac..ee759316 100644 --- a/powerline/config_files/themes/unicode_terminus_condensed.json +++ b/powerline/config_files/themes/unicode_terminus_condensed.json @@ -62,6 +62,24 @@ "powerline.segments.common.hostname": { "before": "⌂" }, + "powerline.segments.common.weather": { + "args": { + "icons": { + "day": "D", + "blustery": "W", + "rainy": "R", + "cloudy": "c", + "snowy": "*", + "stormy": "S", + "foggy": "f", + "sunny": "s", + "night": "N", + "windy": "w", + "not_available": "-", + "unknown": "!" + } + } + }, "powerline.segments.vim.mode": { "args": { From 79a36298e9581dad859bebf977295d953f6a9c41 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 15 Aug 2014 21:14:40 +0400 Subject: [PATCH 1251/1472] Replace foggy weather symbol with IDENTICAL TO Closes #799 --- powerline/config_files/themes/powerline.json | 2 +- powerline/config_files/themes/unicode.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index c854fc5f..6fa43e81 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -70,7 +70,7 @@ "cloudy": "☁", "snowy": "❅", "stormy": "☈", - "foggy": "〰", + "foggy": "≡", "sunny": "☼", "night": "☾", "windy": "☴", diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json index d4d2a35f..484ac5c2 100644 --- a/powerline/config_files/themes/unicode.json +++ b/powerline/config_files/themes/unicode.json @@ -70,7 +70,7 @@ "cloudy": "☁", "snowy": "❅", "stormy": "☈", - "foggy": "〰", + "foggy": "≡", "sunny": "☼", "night": "☾", "windy": "☴", From 65dfc9f2208f39828b9cfa34bb4bd1b4e3d1fd76 Mon Sep 17 00:00:00 2001 From: Jesse Date: Fri, 15 Aug 2014 23:17:00 -0400 Subject: [PATCH 1252/1472] another workaround for _powerline_tmux_setenv() issue with bash-4.2.45 --- powerline/bindings/bash/powerline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 1a5ab763..751e65f8 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -23,7 +23,7 @@ _powerline_tmux_set_pwd() { } _powerline_tmux_set_columns() { - _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" + _powerline_tmux_setenv COLUMNS "${COLUMNS:-`_powerline_columns_fallback`}" } _powerline_init_tmux_support() { From 642ced97bcb2d0fbfd5798d04c680fcd3cfc5db9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 12:39:27 +0400 Subject: [PATCH 1253/1472] Move listers to powerline.listers.vim module --- .../config_files/themes/vim/tabline.json | 1 + powerline/listers/__init__.py | 0 powerline/listers/vim.py | 104 +++++++++++++++++ powerline/segments/vim/__init__.py | 105 ++---------------- 4 files changed, 112 insertions(+), 98 deletions(-) create mode 100644 powerline/listers/__init__.py create mode 100644 powerline/listers/vim.py diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index a51da75f..d252b073 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -4,6 +4,7 @@ "left": [ { "type": "segment_list", + "module": "powerline.listers.vim", "name": "tabbuflister", "segments": [ { diff --git a/powerline/listers/__init__.py b/powerline/listers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py new file mode 100644 index 00000000..b8f3eaa3 --- /dev/null +++ b/powerline/listers/vim.py @@ -0,0 +1,104 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import unicode_literals, absolute_import, division + +try: + import vim +except ImportError: + vim = {} # NOQA + +from powerline.theme import requires_segment_info +from powerline.bindings.vim import (current_tabpage, list_tabpages) + + +def tabpage_updated_segment_info(segment_info, tabpage): + segment_info = segment_info.copy() + window = tabpage.window + buffer = window.buffer + segment_info.update( + tabpage=tabpage, + tabnr=tabpage.number, + window=window, + winnr=window.number, + window_id=int(window.vars.get('powerline_window_id', -1)), + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info + + +@requires_segment_info +def tablister(pl, segment_info): + '''List all tab pages in segment_info format + + Specifically generates a list of segment info dictionaries with ``window``, + ``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local + ones and additional ``tabpage`` and ``tabnr`` keys. + + Sets segment ``mode`` to either ``tab`` (for current tab page) or ``nc`` + (for all other tab pages). + + Works best with vim-7.4 or later: earlier versions miss tabpage object and + thus window objects are not available as well. + ''' + cur_tabpage = current_tabpage() + cur_tabnr = cur_tabpage.number + + def add_multiplier(tabpage, dct): + dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr)) + return dct + + return [ + ( + tabpage_updated_segment_info(segment_info, tabpage), + add_multiplier(tabpage, {'mode': ('tab' if tabpage == cur_tabpage else 'nc')}) + ) + for tabpage in list_tabpages() + ] + + +def buffer_updated_segment_info(segment_info, buffer): + segment_info = segment_info.copy() + segment_info.update( + window=None, + winnr=None, + window_id=None, + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info + + +@requires_segment_info +def bufferlister(pl, segment_info): + '''List all buffers in segment_info format + + Specifically generates a list of segment info dictionaries with ``buffer`` + and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and + ``window_id`` keys unset. + + Sets segment ``mode`` to either ``buf`` (for current buffer) or ``nc`` + (for all other buffers). + ''' + cur_buffer = vim.current.buffer + cur_bufnr = cur_buffer.number + + def add_multiplier(buffer, dct): + dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr)) + return dct + + return [ + ( + buffer_updated_segment_info(segment_info, buffer), + add_multiplier(buffer, {'mode': ('tab' if buffer == cur_buffer else 'nc')}) + ) + for buffer in vim.buffers + ] + + +@requires_segment_info +def tabbuflister(*args, **kwargs): + if len(list_tabpages()) == 1: + return bufferlister(*args, **kwargs) + else: + return tablister(*args, **kwargs) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 4a1c4706..3e991e4f 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -4,15 +4,13 @@ from __future__ import unicode_literals, absolute_import, division import os import re + try: import vim except ImportError: vim = {} # NOQA -try: - from __builtin__ import xrange as range -except ImportError: - pass +from collections import defaultdict from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar, @@ -23,7 +21,11 @@ from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib import wraps_saveargs as wraps -from collections import defaultdict + +try: + from __builtin__ import xrange as range +except ImportError: + pass vim_funcs = { @@ -629,96 +631,3 @@ def single_tab(pl, single_text='Bufs', multiple_text='Tabs'): 'contents': multiple_text, 'highlight_group': ['many_tabs'], }] - - -def tabpage_updated_segment_info(segment_info, tabpage): - segment_info = segment_info.copy() - window = tabpage.window - buffer = window.buffer - segment_info.update( - tabpage=tabpage, - tabnr=tabpage.number, - window=window, - winnr=window.number, - window_id=int(window.vars.get('powerline_window_id', -1)), - buffer=buffer, - bufnr=buffer.number, - ) - return segment_info - - -@requires_segment_info -def tablister(pl, segment_info): - '''List all tab pages in segment_info format - - Specifically generates a list of segment info dictionaries with ``window``, - ``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local - ones and additional ``tabpage`` and ``tabnr`` keys. - - Sets segment ``mode`` to either ``tab`` (for current tab page) or ``nc`` - (for all other tab pages). - - Works best with vim-7.4 or later: earlier versions miss tabpage object and - thus window objects are not available as well. - ''' - cur_tabpage = current_tabpage() - cur_tabnr = cur_tabpage.number - - def add_multiplier(tabpage, dct): - dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr)) - return dct - - return [ - ( - tabpage_updated_segment_info(segment_info, tabpage), - add_multiplier(tabpage, {'mode': ('tab' if tabpage == cur_tabpage else 'nc')}) - ) - for tabpage in list_tabpages() - ] - - -def buffer_updated_segment_info(segment_info, buffer): - segment_info = segment_info.copy() - segment_info.update( - window=None, - winnr=None, - window_id=None, - buffer=buffer, - bufnr=buffer.number, - ) - return segment_info - - -@requires_segment_info -def bufferlister(pl, segment_info): - '''List all buffers in segment_info format - - Specifically generates a list of segment info dictionaries with ``buffer`` - and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and - ``window_id`` keys unset. - - Sets segment ``mode`` to either ``buf`` (for current buffer) or ``nc`` - (for all other buffers). - ''' - cur_buffer = vim.current.buffer - cur_bufnr = cur_buffer.number - - def add_multiplier(buffer, dct): - dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr)) - return dct - - return [ - ( - buffer_updated_segment_info(segment_info, buffer), - add_multiplier(buffer, {'mode': ('tab' if buffer == cur_buffer else 'nc')}) - ) - for buffer in vim.buffers - ] - - -@requires_segment_info -def tabbuflister(*args, **kwargs): - if len(list_tabpages()) == 1: - return bufferlister(*args, **kwargs) - else: - return tablister(*args, **kwargs) From 61fc73d93dd4d461b05940dc95f45588193779a4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 12:42:25 +0400 Subject: [PATCH 1254/1472] Make listers accept any number of keyword arguments --- powerline/listers/vim.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index b8f3eaa3..e0b3a92a 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -28,7 +28,7 @@ def tabpage_updated_segment_info(segment_info, tabpage): @requires_segment_info -def tablister(pl, segment_info): +def tablister(pl, segment_info, **kwargs): '''List all tab pages in segment_info format Specifically generates a list of segment info dictionaries with ``window``, @@ -70,7 +70,7 @@ def buffer_updated_segment_info(segment_info, buffer): @requires_segment_info -def bufferlister(pl, segment_info): +def bufferlister(pl, segment_info, **kwargs): '''List all buffers in segment_info format Specifically generates a list of segment info dictionaries with ``buffer`` @@ -97,8 +97,8 @@ def bufferlister(pl, segment_info): @requires_segment_info -def tabbuflister(*args, **kwargs): +def tabbuflister(**kwargs): if len(list_tabpages()) == 1: - return bufferlister(*args, **kwargs) + return bufferlister(**kwargs) else: - return tablister(*args, **kwargs) + return tablister(**kwargs) From 54f0537d298368a8a7f2baa84b41f47fa9d4d8cb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 12:57:34 +0400 Subject: [PATCH 1255/1472] Do not show unlisted buffers Closes #972 --- powerline/listers/vim.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index e0b3a92a..e59e623b 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -8,7 +8,7 @@ except ImportError: vim = {} # NOQA from powerline.theme import requires_segment_info -from powerline.bindings.vim import (current_tabpage, list_tabpages) +from powerline.bindings.vim import (current_tabpage, list_tabpages, vim_getbufoption) def tabpage_updated_segment_info(segment_info, tabpage): @@ -57,7 +57,7 @@ def tablister(pl, segment_info, **kwargs): ] -def buffer_updated_segment_info(segment_info, buffer): +def buffer_updated_segment_info(segment_info, buffer, mode): segment_info = segment_info.copy() segment_info.update( window=None, @@ -65,20 +65,25 @@ def buffer_updated_segment_info(segment_info, buffer): window_id=None, buffer=buffer, bufnr=buffer.number, + mode=mode, ) return segment_info @requires_segment_info -def bufferlister(pl, segment_info, **kwargs): +def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): '''List all buffers in segment_info format Specifically generates a list of segment info dictionaries with ``buffer`` and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and - ``window_id`` keys unset. + ``window_id`` keys set to None. Sets segment ``mode`` to either ``buf`` (for current buffer) or ``nc`` (for all other buffers). + + :param bool show_unlisted: + True if unlisted buffers should be shown as well. Current buffer is + always shown. ''' cur_buffer = vim.current.buffer cur_bufnr = cur_buffer.number @@ -89,10 +94,21 @@ def bufferlister(pl, segment_info, **kwargs): return [ ( - buffer_updated_segment_info(segment_info, buffer), - add_multiplier(buffer, {'mode': ('tab' if buffer == cur_buffer else 'nc')}) + buf_segment_info, + add_multiplier(buf_segment_info['buffer'], {'mode': buf_segment_info['mode']}) + ) + for buf_segment_info in ( + buffer_updated_segment_info( + segment_info, + buffer, + ('buf' if buffer is cur_buffer else 'nc') + ) + for buffer in vim.buffers + ) if ( + buf_segment_info['buffer'] is cur_buffer + or show_unlisted + or int(vim_getbufoption(buf_segment_info, 'buflisted')) ) - for buffer in vim.buffers ] From ff2009d5e3473396fc798762df589f9db5bfe8dd Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:03:40 +0400 Subject: [PATCH 1256/1472] Update mode in segment_info in tabpage lister --- powerline/listers/vim.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index e59e623b..95378c38 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -11,7 +11,7 @@ from powerline.theme import requires_segment_info from powerline.bindings.vim import (current_tabpage, list_tabpages, vim_getbufoption) -def tabpage_updated_segment_info(segment_info, tabpage): +def tabpage_updated_segment_info(segment_info, tabpage, mode): segment_info = segment_info.copy() window = tabpage.window buffer = window.buffer @@ -23,6 +23,7 @@ def tabpage_updated_segment_info(segment_info, tabpage): window_id=int(window.vars.get('powerline_window_id', -1)), buffer=buffer, bufnr=buffer.number, + mode=mode, ) return segment_info @@ -49,10 +50,10 @@ def tablister(pl, segment_info, **kwargs): return dct return [ - ( - tabpage_updated_segment_info(segment_info, tabpage), - add_multiplier(tabpage, {'mode': ('tab' if tabpage == cur_tabpage else 'nc')}) - ) + (lambda mode: ( + tabpage_updated_segment_info(segment_info, tabpage, mode), + add_multiplier(tabpage, {'mode': mode}) + ))('tab' if tabpage == cur_tabpage else 'nc') for tabpage in list_tabpages() ] From 24938e0bb91f4eb636577dd921a9ea16f9b06940 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:06:19 +0400 Subject: [PATCH 1257/1472] Remove duplicate information from configuration/segments.rst --- docs/source/configuration/segments.rst | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/source/configuration/segments.rst b/docs/source/configuration/segments.rst index e6e48f36..ce32302a 100644 --- a/docs/source/configuration/segments.rst +++ b/docs/source/configuration/segments.rst @@ -15,13 +15,7 @@ Segments are regular Python functions, and they may accept arguments. All arguments should have a default value which will be used for themes that don't provide an ``args`` dict. -A segment function must return one of the following values: - -* ``None``, which will remove the segment from the prompt/statusline. -* A string, which will be the segment contents. -* A list of dicts consisting of a ``contents`` string, and - a ``highlight_group`` list. This is useful for providing a particular - highlighting group depending on the segment contents. +More information is available in :ref:`Writing segments ` section. Available segments ================== From 402cc9e9111c1368a273ea918f1d3bf53807b6a5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:21:46 +0400 Subject: [PATCH 1258/1472] Also show documentation for plugin-specific segments --- docs/source/configuration/segments/vim.rst | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/source/configuration/segments/vim.rst b/docs/source/configuration/segments/vim.rst index 5d6c31a9..ace646c4 100644 --- a/docs/source/configuration/segments/vim.rst +++ b/docs/source/configuration/segments/vim.rst @@ -4,3 +4,31 @@ Vim segments .. automodule:: powerline.segments.vim :members: + + +Plugin-specific segments +======================== + +Syntastic segments +------------------ + +.. automodule:: powerline.segments.vim.plugin.syntastic + :members: + +Ctrl-P segments +--------------- + +.. automodule:: powerline.segments.vim.plugin.ctrlp + :members: + +Tagbar segments +--------------- + +.. automodule:: powerline.segments.vim.plugin.tagbar + :members: + +NERDTree segments +----------------- + +.. automodule:: powerline.segments.vim.plugin.nerdtree + :members: From c3099ce48b25bd6496be1aaca9c482dee65b883e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:22:31 +0400 Subject: [PATCH 1259/1472] Add lister reference --- docs/source/configuration.rst | 1 + docs/source/configuration/listers.rst | 21 +++++++++++++++++++++ docs/source/develop/segments.rst | 2 ++ 3 files changed, 24 insertions(+) create mode 100644 docs/source/configuration/listers.rst diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 88663a6c..8ed3fe7d 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -133,4 +133,5 @@ References configuration/reference configuration/segments + configuration/listers configuration/local diff --git a/docs/source/configuration/listers.rst b/docs/source/configuration/listers.rst new file mode 100644 index 00000000..c0db03d0 --- /dev/null +++ b/docs/source/configuration/listers.rst @@ -0,0 +1,21 @@ +**************** +Lister reference +**************** + +Listers are special segment collections which allow to show some list of +segments for each entity in the list of entities (multiply their segments list +by a list of entities). E.g. ``powerline.listers.vim.tablister`` presented with +``powerline.segments.vim.tabnr`` and ``….file_name`` as segments will emit +segments with buffer names and tabpage numbers for each tabpage shown by vim. + +Listers appear in configuration as irregular segments having ``segment_list`` as +their type and ``segments`` key with a list of segments (a bit more details in +:ref:`Themes section of configuration reference `). + +Currently only Vim listers are available. + +Vim listers +----------- + +.. automodule:: powerline.listers.vim + :members: diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 592f3052..2c9aecc1 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -1,3 +1,5 @@ +.. _dev-segments: + **************** Writing segments **************** From a3324134d11bd3957406973c80635a5f60ee1b01 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:33:59 +0400 Subject: [PATCH 1260/1472] Use generators in place of list comprehension in listers --- powerline/listers/vim.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index 95378c38..aa7449bd 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -49,13 +49,13 @@ def tablister(pl, segment_info, **kwargs): dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr)) return dct - return [ - (lambda mode: ( + return ( + (lambda tabpage, mode: ( tabpage_updated_segment_info(segment_info, tabpage, mode), add_multiplier(tabpage, {'mode': mode}) - ))('tab' if tabpage == cur_tabpage else 'nc') + ))(tabpage, 'tab' if tabpage == cur_tabpage else 'nc') for tabpage in list_tabpages() - ] + ) def buffer_updated_segment_info(segment_info, buffer, mode): @@ -93,7 +93,7 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr)) return dct - return [ + return ( ( buf_segment_info, add_multiplier(buf_segment_info['buffer'], {'mode': buf_segment_info['mode']}) @@ -110,7 +110,7 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): or show_unlisted or int(vim_getbufoption(buf_segment_info, 'buflisted')) ) - ] + ) @requires_segment_info From d1782d4aafae5e90e8d4aee0383b677fb2d2541e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:43:22 +0400 Subject: [PATCH 1261/1472] Add documentation about creating listers --- docs/source/configuration/listers.rst | 2 ++ docs/source/develop.rst | 1 + docs/source/develop/listers.rst | 45 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 docs/source/develop/listers.rst diff --git a/docs/source/configuration/listers.rst b/docs/source/configuration/listers.rst index c0db03d0..107159a1 100644 --- a/docs/source/configuration/listers.rst +++ b/docs/source/configuration/listers.rst @@ -12,6 +12,8 @@ Listers appear in configuration as irregular segments having ``segment_list`` as their type and ``segments`` key with a list of segments (a bit more details in :ref:`Themes section of configuration reference `). +More information in :ref:`Writing listers ` section. + Currently only Vim listers are available. Vim listers diff --git a/docs/source/develop.rst b/docs/source/develop.rst index e7b29cc7..6ee44f27 100644 --- a/docs/source/develop.rst +++ b/docs/source/develop.rst @@ -7,5 +7,6 @@ Developer guide :glob: develop/segments + develop/listers develop/local-themes develop/extensions diff --git a/docs/source/develop/listers.rst b/docs/source/develop/listers.rst new file mode 100644 index 00000000..dc027511 --- /dev/null +++ b/docs/source/develop/listers.rst @@ -0,0 +1,45 @@ +.. _dev-listers: + +*************** +Writing listers +*************** + +Listers allow you to show some segments multiple times: once per each entity +(buffer, tabpage, etc) lister knows. They are functions which receive the +following arguments: + +``pl`` + A :py:class:`powerline.PowerlineLogger` class instance. It must be used for + logging. + +``segment_info`` + Base segment info dictionary. Lister function or class must have + ``powerline_requires_segment_info`` to receive this argument. + + .. warning:: + Listers are close to useless if they do not have access to this + argument. + + Refer to :ref:`segment_info detailed description ` for + further details. + +And also any other argument(s) specified by user in :ref:`args key +` (no additional arguments by default). + +Listers must return a sequence of pairs. First item in the pair must contain +a ``segment_info`` dictionary specific to one of the listed entities. + +Second item must contain another dictionary: it will be used to modify the +resulting segment. In addition to usual keys that describe segment the following +keys may be present (it is advised that *only* the following keys will be used): + +``mode`` + Segment-specific mode. Used to alter segment highlighting. + +``priority_multiplier`` + Value (usually a ``float``) used to multiply segment priority. It is useful + for finer-grained controlling which segments disappear first: e.g. when + listing tab pages make first disappear directory names of the tabpages which + are most far away from current tabpage, then (when all directory names + disappeared) buffer names. Check out existing listers implementation in + :file:`powerline/listers/vim.py`. From ac7a9596ab2e2a5669d4c6abc4bdd3d4e23f2f07 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:46:14 +0400 Subject: [PATCH 1262/1472] Remove nonlocal image URLs from overview --- docs/source/overview.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/overview.rst b/docs/source/overview.rst index ed2abefd..b599b349 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -48,20 +48,20 @@ Vim statusline **Mode-dependent highlighting** -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-normal.png +* .. image:: _static/img/pl-mode-normal.png :alt: Normal mode -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-insert.png +* .. image:: _static/img/pl-mode-insert.png :alt: Insert mode -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-visual.png +* .. image:: _static/img/pl-mode-visual.png :alt: Visual mode -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-replace.png +* .. image:: _static/img/pl-mode-replace.png :alt: Replace mode **Automatic truncation of segments in small windows** -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate1.png +* .. image:: _static/img/pl-truncate1.png :alt: Truncation illustration -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate2.png +* .. image:: _static/img/pl-truncate2.png :alt: Truncation illustration -* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate3.png +* .. image:: _static/img/pl-truncate3.png :alt: Truncation illustration From 096c4801b02b40217d7898a67105c3431c374bad Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:51:26 +0400 Subject: [PATCH 1263/1472] Use buf_nc and tab_nc modes in listers --- .../colorschemes/vim/default.json | 38 +++++++++++++++++++ .../colorschemes/vim/solarized.json | 20 ++++++++++ .../colorschemes/vim/solarizedlight.json | 20 ++++++++++ .../config_files/themes/vim/tabline.json | 2 +- powerline/lint/__init__.py | 2 +- powerline/listers/vim.py | 8 ++-- 6 files changed, 84 insertions(+), 6 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index b4f1b483..3f2afb31 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -51,6 +51,44 @@ "dark_green_gray": "gray5" } }, + "tab_nc": { + "colors": { + "brightyellow": "darkorange", + "brightestred": "darkred", + "gray0": "gray0", + "gray1": "gray0", + "gray2": "gray0", + "gray3": "gray1", + "gray4": "gray1", + "gray5": "gray1", + "gray6": "gray1", + "gray7": "gray4", + "gray8": "gray4", + "gray9": "gray4", + "gray10": "gray5", + "white": "gray6", + "dark_green_gray": "gray5" + } + }, + "buf_nc": { + "colors": { + "brightyellow": "darkorange", + "brightestred": "darkred", + "gray0": "gray0", + "gray1": "gray0", + "gray2": "gray0", + "gray3": "gray1", + "gray4": "gray1", + "gray5": "gray1", + "gray6": "gray1", + "gray7": "gray4", + "gray8": "gray4", + "gray9": "gray4", + "gray10": "gray5", + "white": "gray6", + "dark_green_gray": "gray5" + } + }, "i": { "colors": { "gray0": "darkestblue", diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index a3703d74..f7f393fe 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -43,6 +43,26 @@ "oldlace": "gray61" } }, + "tab_nc": { + "colors": { + "darkgreencopper": "royalblue5", + "lightskyblue4": "royalblue5", + "azure4": "darkgreencopper", + "gray61": "lightskyblue4", + "lightyellow": "azure4", + "oldlace": "gray61" + } + }, + "buf_nc": { + "colors": { + "darkgreencopper": "royalblue5", + "lightskyblue4": "royalblue5", + "azure4": "darkgreencopper", + "gray61": "lightskyblue4", + "lightyellow": "azure4", + "oldlace": "gray61" + } + }, "i": { "groups": { "background": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index cd86a7a8..cab94968 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -43,6 +43,26 @@ "gray13": "gray61" } }, + "tab_nc": { + "colors": { + "lightyellow": "darkgreencopper", + "azure4": "darkgreencopper", + "lightskyblue4": "lightyellow", + "gray61": "azure4", + "royalblue5": "lightskyblue4", + "gray13": "gray61" + } + }, + "buf_nc": { + "colors": { + "lightyellow": "darkgreencopper", + "azure4": "darkgreencopper", + "lightskyblue4": "lightyellow", + "gray61": "azure4", + "royalblue5": "lightskyblue4", + "gray13": "gray61" + } + }, "i": { "groups": { "background": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index d252b073..c51d66fa 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -11,7 +11,7 @@ "name": "tabnr", "after": " ", "draw_soft_divider": false, - "exclude_modes": ["tab", "buf"], + "exclude_modes": ["tab", "buf", "buf_nc"], "priority": 5 }, { diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 995bc1f6..d9eb1112 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -733,7 +733,7 @@ top_colorscheme_spec = (Spec( mode_translations_value_spec(), ).optional().context_message('Error while loading mode translations (key {key})').optional(), ).context_message('Error while loading top-level coloscheme')) -vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc'])).copy +vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc', 'tab_nc', 'buf_nc'])).copy vim_colorscheme_spec = (Spec( name=name_spec(), groups=groups_spec(), diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index aa7449bd..b8d38ecb 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -36,7 +36,7 @@ def tablister(pl, segment_info, **kwargs): ``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local ones and additional ``tabpage`` and ``tabnr`` keys. - Sets segment ``mode`` to either ``tab`` (for current tab page) or ``nc`` + Sets segment ``mode`` to either ``tab`` (for current tab page) or ``tab_nc`` (for all other tab pages). Works best with vim-7.4 or later: earlier versions miss tabpage object and @@ -53,7 +53,7 @@ def tablister(pl, segment_info, **kwargs): (lambda tabpage, mode: ( tabpage_updated_segment_info(segment_info, tabpage, mode), add_multiplier(tabpage, {'mode': mode}) - ))(tabpage, 'tab' if tabpage == cur_tabpage else 'nc') + ))(tabpage, 'tab' if tabpage == cur_tabpage else 'tab_nc') for tabpage in list_tabpages() ) @@ -79,7 +79,7 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and ``window_id`` keys set to None. - Sets segment ``mode`` to either ``buf`` (for current buffer) or ``nc`` + Sets segment ``mode`` to either ``buf`` (for current buffer) or ``buf_nc`` (for all other buffers). :param bool show_unlisted: @@ -102,7 +102,7 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): buffer_updated_segment_info( segment_info, buffer, - ('buf' if buffer is cur_buffer else 'nc') + ('buf' if buffer is cur_buffer else 'buf_nc') ) for buffer in vim.buffers ) if ( From 407a8bca7e094efdea245a832f8e0b090723df09 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 13:59:00 +0400 Subject: [PATCH 1264/1472] Fix incorrect description of draw_inner_divider --- docs/source/develop/segments.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 2c9aecc1..d1214bed 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -61,8 +61,8 @@ Detailed description of used dictionary keys: .. _dev-segments-draw_inner_divider: ``draw_inner_divider`` - Determines whether *any* divider between segments returned by function - should be drawn. Defaults to ``False``. + Determines whether soft divider between segments returned by function should + be drawn. Defaults to ``False``. .. _dev-segments-highlight_group: From 6106e914e3fbc753dffcab95f26f21979ab49b38 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 14:00:08 +0400 Subject: [PATCH 1265/1472] Remove duplicate documentation of draw_inner_divider --- docs/source/configuration/reference.rst | 2 ++ docs/source/develop/segments.rst | 14 ++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 9d166292..12966b45 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -448,6 +448,8 @@ ascii Theme without any unicode characters at all background colors, soft ones are used between segments with same background. Both options default to ``True``. + .. _config-themes-seg-draw_inner_divider: + ``draw_inner_divider`` Determines whether inner soft dividers are to be drawn for function segments. Only applicable for functions returning multiple segments. diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index d1214bed..63ea35db 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -53,16 +53,10 @@ Detailed description of used dictionary keys: Text displayed by segment. Should be a ``unicode`` (Python2) or ``str`` (Python3) instance. -``draw_hard_divider``, ``draw_soft_divider`` - Determines whether given divider should be drawn. Both have the same meaning - as :ref:`the similar keys in configuration - `. - -.. _dev-segments-draw_inner_divider: - -``draw_inner_divider`` - Determines whether soft divider between segments returned by function should - be drawn. Defaults to ``False``. +``draw_hard_divider``, ``draw_soft_divider``, ``draw_inner_divider`` + Determines whether given divider should be drawn. All have the same meaning + as :ref:`the similar keys in configuration ` + (:ref:`draw_inner_divider `). .. _dev-segments-highlight_group: From a792fb0e1c215429ac0121de28382329d5634b31 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 14:25:12 +0400 Subject: [PATCH 1266/1472] Add support for `draw_inner_divider` in listers --- docs/source/develop/listers.rst | 6 ++++++ powerline/config_files/themes/vim/tabline.json | 2 -- powerline/segment.py | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/source/develop/listers.rst b/docs/source/develop/listers.rst index dc027511..8c7b439e 100644 --- a/docs/source/develop/listers.rst +++ b/docs/source/develop/listers.rst @@ -23,6 +23,12 @@ following arguments: Refer to :ref:`segment_info detailed description ` for further details. +``draw_inner_divider`` + If False (default) soft dividers between segments in the listed group will + not be drawn regardless of actual segment settings. If True they will be + drawn, again regardless of actual segment settings. Set it to ``None`` in + order to respect segment settings. + And also any other argument(s) specified by user in :ref:`args key ` (no additional arguments by default). diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index c51d66fa..d4a9e69b 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -10,13 +10,11 @@ { "name": "tabnr", "after": " ", - "draw_soft_divider": false, "exclude_modes": ["tab", "buf", "buf_nc"], "priority": 5 }, { "name": "file_directory", - "draw_soft_divider": false, "priority": 40 }, { diff --git a/powerline/segment.py b/powerline/segment.py index cde42398..a1958d0e 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -95,6 +95,8 @@ def get_attr_func(contents_func, key, args): def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subsegments, patcher_args): for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): + draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) + old_pslen = len(parsed_segments) for subsegment in subsegments: if subsegment_update: subsegment = subsegment.copy() @@ -102,6 +104,10 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subs if subsegment_update['priority_multiplier'] and subsegment['priority']: subsegment['priority'] *= subsegment_update['priority_multiplier'] process_segment(pl, side, subsegment_info, parsed_segments, subsegment) + new_pslen = len(parsed_segments) + if new_pslen > old_pslen + 1 and draw_inner_divider is not None: + for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): + parsed_segments[i]['draw_soft_divider'] = draw_inner_divider return None From 31731a93daf0c6419bbcd2064109e483f4a6ae01 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 14:25:43 +0400 Subject: [PATCH 1267/1472] Add bufnr and modified_indicator segments to tabline --- powerline/config_files/colorschemes/vim/__main__.json | 2 +- powerline/config_files/themes/vim/tabline.json | 11 +++++++++++ tests/test_tabline.vim | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index ea9ba29e..e7d995b8 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -15,7 +15,7 @@ "position": "information:additional", "single_tab": "line_current", "many_tabs": "line_current", - "bufnr": "information:unimportant", + "bufnr": "file_directory", "winnr": "information:unimportant", "tabnr": "file_directory" } diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index d4a9e69b..42aed081 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -13,6 +13,12 @@ "exclude_modes": ["tab", "buf", "buf_nc"], "priority": 5 }, + { + "name": "bufnr", + "after": " ", + "exclude_modes": ["tab", "buf", "tab_nc"], + "priority": 5 + }, { "name": "file_directory", "priority": 40 @@ -23,6 +29,11 @@ "display_no_file": true }, "priority": 10 + }, + { + "name": "modified_indicator", + "exclude_modes": ["tab", "tab_nc"], + "priority": 5 } ] }, diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 3819e74b..8e2cca40 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -26,7 +26,7 @@ catch cquit endtry -if result isnot# '%#Pl_240_5789784_235_2500134_NONE# ./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                               %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (2)', result], 'message.fail') cquit endif From 62e8e78866a5d941ce701a2186bcc251896ac6e6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 14:36:48 +0400 Subject: [PATCH 1268/1472] Fix exclude_/include_modes support with new segment['mode'] key --- powerline/renderer.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index ec84e198..204a328d 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -258,12 +258,14 @@ class Renderer(object): # Handle excluded/included segments for the current mode segments = [ self._get_highlighting(segment, segment['mode'] or mode) - for segment in segments - if ( - mode not in segment['exclude_modes'] + for segment, segment_mode in ( + (segment, segment['mode']) + for segment in segments + ) if ( + segment_mode not in segment['exclude_modes'] and ( not segment['include_modes'] - or mode in segment['include_modes'] + or segment_mode in segment['include_modes'] ) ) ] From 61074fcd6c5098eb05f0e677c76f908335841312 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 22:35:31 +0400 Subject: [PATCH 1269/1472] Refactor file_watcher.py into four files --- powerline/lib/config.py | 2 +- powerline/lib/path.py | 8 ++ powerline/lib/vcs/__init__.py | 2 +- powerline/lib/watcher/__init__.py | 49 ++++++++ .../{file_watcher.py => watcher/inotify.py} | 109 +----------------- powerline/lib/watcher/stat.py | 44 +++++++ powerline/segment.py | 2 +- tests/test_lib.py | 2 +- 8 files changed, 109 insertions(+), 109 deletions(-) create mode 100644 powerline/lib/path.py create mode 100644 powerline/lib/watcher/__init__.py rename powerline/lib/{file_watcher.py => watcher/inotify.py} (56%) create mode 100644 powerline/lib/watcher/stat.py diff --git a/powerline/lib/config.py b/powerline/lib/config.py index eaf4c502..522e5f45 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -1,7 +1,7 @@ # vim:fileencoding=utf-8:noet from powerline.lib.threaded import MultiRunnedThread -from powerline.lib.file_watcher import create_file_watcher +from powerline.lib.watcher import create_file_watcher from copy import deepcopy from threading import Event, Lock diff --git a/powerline/lib/path.py b/powerline/lib/path.py new file mode 100644 index 00000000..be6872e7 --- /dev/null +++ b/powerline/lib/path.py @@ -0,0 +1,8 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals, absolute_import + +import os + + +def realpath(path): + return os.path.abspath(os.path.realpath(path)) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 58a4883d..8876dd15 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -232,7 +232,7 @@ def guess(path, create_watcher): def get_fallback_create_watcher(): - from powerline.lib.file_watcher import create_file_watcher + from powerline.lib.watcher import create_file_watcher from powerline import get_fallback_logger from functools import partial return partial(create_file_watcher, get_fallback_logger(), 'auto') diff --git a/powerline/lib/watcher/__init__.py b/powerline/lib/watcher/__init__.py new file mode 100644 index 00000000..7e9e93a5 --- /dev/null +++ b/powerline/lib/watcher/__init__.py @@ -0,0 +1,49 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals, absolute_import + +import sys + +from powerline.lib.watcher.stat import StatWatch +from powerline.lib.watcher.inotify import INotifyWatch, INotifyError + + +def create_file_watcher(pl, watcher_type='auto', expire_time=10): + ''' + Create an object that can watch for changes to specified files + + Use ``.__call__()`` method of the returned object to start watching the file + or check whether file has changed since last call. + + Use ``.unwatch()`` method of the returned object to stop watching the file. + + Uses inotify if available, otherwise tracks mtimes. expire_time is the + number of minutes after the last query for a given path for the inotify + watch for that path to be automatically removed. This conserves kernel + resources. + + :param PowerlineLogger pl: + Logger. + :param str watcher_type: + One of ``inotify`` (linux only), ``stat``, ``auto``. Determines what + watcher will be used. ``auto`` will use ``inotify`` if available. + :param int expire_time: + Number of minutes since last ``.__call__()`` before inotify watcher will + stop watching given file. + ''' + if watcher_type == 'stat': + pl.debug('Using requested stat-based watcher', prefix='watcher') + return StatWatch() + if watcher_type == 'inotify': + # Explicitly selected inotify watcher: do not catch INotifyError then. + pl.debug('Using requested inotify watcher', prefix='watcher') + return INotifyWatch(expire_time=expire_time) + + if sys.platform.startswith('linux'): + try: + pl.debug('Trying to use inotify watcher', prefix='watcher') + return INotifyWatch(expire_time=expire_time) + except INotifyError: + pl.info('Failed to create inotify watcher', prefix='watcher') + + pl.debug('Using stat-based watcher') + return StatWatch() diff --git a/powerline/lib/file_watcher.py b/powerline/lib/watcher/inotify.py similarity index 56% rename from powerline/lib/file_watcher.py rename to powerline/lib/watcher/inotify.py index ffb337f2..0e2a3c4d 100644 --- a/powerline/lib/file_watcher.py +++ b/powerline/lib/watcher/inotify.py @@ -1,21 +1,14 @@ # vim:fileencoding=utf-8:noet from __future__ import unicode_literals, absolute_import -__copyright__ = '2013, Kovid Goyal ' -__docformat__ = 'restructuredtext en' - -import os -import sys import errno -from time import sleep +import os + from threading import RLock +from powerline.lib.inotify import INotify from powerline.lib.monotonic import monotonic -from powerline.lib.inotify import INotify, INotifyError - - -def realpath(path): - return os.path.abspath(os.path.realpath(path)) +from powerline.lib.path import realpath class INotifyWatch(INotify): @@ -142,97 +135,3 @@ class INotifyWatch(INotify): except OSError: pass super(INotifyWatch, self).close() - - -class StatWatch(object): - def __init__(self): - self.watches = {} - self.lock = RLock() - - def watch(self, path): - path = realpath(path) - with self.lock: - self.watches[path] = os.path.getmtime(path) - - def unwatch(self, path): - path = realpath(path) - with self.lock: - self.watches.pop(path, None) - - def is_watched(self, path): - with self.lock: - return realpath(path) in self.watches - - def __call__(self, path): - path = realpath(path) - with self.lock: - if path not in self.watches: - self.watches[path] = os.path.getmtime(path) - return True - mtime = os.path.getmtime(path) - if mtime != self.watches[path]: - self.watches[path] = mtime - return True - return False - - def close(self): - with self.lock: - self.watches.clear() - - -def create_file_watcher(pl, watcher_type='auto', expire_time=10): - ''' - Create an object that can watch for changes to specified files - - Use ``.__call__()`` method of the returned object to start watching the file - or check whether file has changed since last call. - - Use ``.unwatch()`` method of the returned object to stop watching the file. - - Uses inotify if available, otherwise tracks mtimes. expire_time is the - number of minutes after the last query for a given path for the inotify - watch for that path to be automatically removed. This conserves kernel - resources. - - :param PowerlineLogger pl: - Logger. - :param str watcher_type: - One of ``inotify`` (linux only), ``stat``, ``auto``. Determines what - watcher will be used. ``auto`` will use ``inotify`` if available. - :param int expire_time: - Number of minutes since last ``.__call__()`` before inotify watcher will - stop watching given file. - ''' - if watcher_type == 'stat': - pl.debug('Using requested stat-based watcher', prefix='watcher') - return StatWatch() - if watcher_type == 'inotify': - # Explicitly selected inotify watcher: do not catch INotifyError then. - pl.debug('Using requested inotify watcher', prefix='watcher') - return INotifyWatch(expire_time=expire_time) - - if sys.platform.startswith('linux'): - try: - pl.debug('Trying to use inotify watcher', prefix='watcher') - return INotifyWatch(expire_time=expire_time) - except INotifyError: - pl.info('Failed to create inotify watcher', prefix='watcher') - - pl.debug('Using stat-based watcher') - return StatWatch() - - -if __name__ == '__main__': - from powerline import get_fallback_logger - watcher = create_file_watcher(get_fallback_logger()) - print ('Using watcher: %s' % watcher.__class__.__name__) - print ('Watching %s, press Ctrl-C to quit' % sys.argv[-1]) - watcher.watch(sys.argv[-1]) - try: - while True: - if watcher(sys.argv[-1]): - print ('%s has changed' % sys.argv[-1]) - sleep(1) - except KeyboardInterrupt: - pass - watcher.close() diff --git a/powerline/lib/watcher/stat.py b/powerline/lib/watcher/stat.py new file mode 100644 index 00000000..2057564f --- /dev/null +++ b/powerline/lib/watcher/stat.py @@ -0,0 +1,44 @@ +# vim:fileencoding=utf-8:noet +from __future__ import unicode_literals, absolute_import + +import os + +from threading import RLock + +from powerline.lib.path import realpath + + +class StatWatch(object): + def __init__(self): + self.watches = {} + self.lock = RLock() + + def watch(self, path): + path = realpath(path) + with self.lock: + self.watches[path] = os.path.getmtime(path) + + def unwatch(self, path): + path = realpath(path) + with self.lock: + self.watches.pop(path, None) + + def is_watched(self, path): + with self.lock: + return realpath(path) in self.watches + + def __call__(self, path): + path = realpath(path) + with self.lock: + if path not in self.watches: + self.watches[path] = os.path.getmtime(path) + return True + mtime = os.path.getmtime(path) + if mtime != self.watches[path]: + self.watches[path] = mtime + return True + return False + + def close(self): + with self.lock: + self.watches.clear() diff --git a/powerline/segment.py b/powerline/segment.py index a1958d0e..144f4623 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -3,7 +3,7 @@ from __future__ import absolute_import, unicode_literals, division, print_functi import sys -from powerline.lib.file_watcher import create_file_watcher +from powerline.lib.watcher import create_file_watcher def list_segment_key_values(segment, theme_configs, key, module=None, default=None): diff --git a/tests/test_lib.py b/tests/test_lib.py index fa22da41..345b4e10 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -6,7 +6,7 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess, get_fallback_create_watcher from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic -from powerline.lib.file_watcher import create_file_watcher, INotifyError +from powerline.lib.watcher import create_file_watcher, INotifyError from powerline.lib.vcs.git import git_directory from powerline import get_fallback_logger import threading From 14608d1bf97a9005f875bf3990f19c4cd8d5278b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 22:37:05 +0400 Subject: [PATCH 1270/1472] Make naming consistent with tree_watcher --- powerline/lib/watcher/__init__.py | 12 ++++++------ powerline/lib/watcher/inotify.py | 6 +++--- powerline/lib/watcher/stat.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/powerline/lib/watcher/__init__.py b/powerline/lib/watcher/__init__.py index 7e9e93a5..816353b8 100644 --- a/powerline/lib/watcher/__init__.py +++ b/powerline/lib/watcher/__init__.py @@ -3,8 +3,8 @@ from __future__ import unicode_literals, absolute_import import sys -from powerline.lib.watcher.stat import StatWatch -from powerline.lib.watcher.inotify import INotifyWatch, INotifyError +from powerline.lib.watcher.stat import StatFileWatcher +from powerline.lib.watcher.inotify import INotifyFileWatcher, INotifyError def create_file_watcher(pl, watcher_type='auto', expire_time=10): @@ -32,18 +32,18 @@ def create_file_watcher(pl, watcher_type='auto', expire_time=10): ''' if watcher_type == 'stat': pl.debug('Using requested stat-based watcher', prefix='watcher') - return StatWatch() + return StatFileWatcher() if watcher_type == 'inotify': # Explicitly selected inotify watcher: do not catch INotifyError then. pl.debug('Using requested inotify watcher', prefix='watcher') - return INotifyWatch(expire_time=expire_time) + return INotifyFileWatcher(expire_time=expire_time) if sys.platform.startswith('linux'): try: pl.debug('Trying to use inotify watcher', prefix='watcher') - return INotifyWatch(expire_time=expire_time) + return INotifyFileWatcher(expire_time=expire_time) except INotifyError: pl.info('Failed to create inotify watcher', prefix='watcher') pl.debug('Using stat-based watcher') - return StatWatch() + return StatFileWatcher() diff --git a/powerline/lib/watcher/inotify.py b/powerline/lib/watcher/inotify.py index 0e2a3c4d..dbbdbcc3 100644 --- a/powerline/lib/watcher/inotify.py +++ b/powerline/lib/watcher/inotify.py @@ -11,9 +11,9 @@ from powerline.lib.monotonic import monotonic from powerline.lib.path import realpath -class INotifyWatch(INotify): +class INotifyFileWatcher(INotify): def __init__(self, expire_time=10): - super(INotifyWatch, self).__init__() + super(INotifyFileWatcher, self).__init__() self.watches = {} self.modified = {} self.last_query = {} @@ -134,4 +134,4 @@ class INotifyWatch(INotify): self.unwatch(path) except OSError: pass - super(INotifyWatch, self).close() + super(INotifyFileWatcher, self).close() diff --git a/powerline/lib/watcher/stat.py b/powerline/lib/watcher/stat.py index 2057564f..ff2bf154 100644 --- a/powerline/lib/watcher/stat.py +++ b/powerline/lib/watcher/stat.py @@ -8,7 +8,7 @@ from threading import RLock from powerline.lib.path import realpath -class StatWatch(object): +class StatFileWatcher(object): def __init__(self): self.watches = {} self.lock = RLock() From cb41ce40d2a6b48f0376f861edfd86424aec7c18 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 22:38:36 +0400 Subject: [PATCH 1271/1472] Remove function that is not used anywhere --- powerline/lib/tree_watcher.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py index 6a439dd7..cc4cc547 100644 --- a/powerline/lib/tree_watcher.py +++ b/powerline/lib/tree_watcher.py @@ -172,10 +172,6 @@ class TreeWatcher(object): self.watches[path] = w return w - def is_actually_watched(self, path): - w = self.watches.get(path, None) - return not getattr(w, 'is_dummy', True) - def expire_old_queries(self): pop = [] now = monotonic() From 2faa2a254fa65b35db573e6f3220b91cb87fd0ac Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jun 2014 22:59:46 +0400 Subject: [PATCH 1272/1472] Move tree_watcher to powerline/lib/watcher and split it --- powerline/lib/tree_watcher.py | 217 ------------------------------ powerline/lib/vcs/__init__.py | 20 +-- powerline/lib/watcher/__init__.py | 19 ++- powerline/lib/watcher/inotify.py | 126 +++++++++++++++++ powerline/lib/watcher/tree.py | 83 ++++++++++++ tests/test_lib.py | 7 +- 6 files changed, 241 insertions(+), 231 deletions(-) delete mode 100644 powerline/lib/tree_watcher.py create mode 100644 powerline/lib/watcher/tree.py diff --git a/powerline/lib/tree_watcher.py b/powerline/lib/tree_watcher.py deleted file mode 100644 index cc4cc547..00000000 --- a/powerline/lib/tree_watcher.py +++ /dev/null @@ -1,217 +0,0 @@ -# vim:fileencoding=utf-8:noet -from __future__ import (unicode_literals, absolute_import, print_function) - -__copyright__ = '2013, Kovid Goyal ' -__docformat__ = 'restructuredtext en' - -import sys -import os -import errno -from time import sleep -from powerline.lib.monotonic import monotonic - -from powerline.lib.inotify import INotify, INotifyError - - -class NoSuchDir(ValueError): - pass - - -class BaseDirChanged(ValueError): - pass - - -class DirTooLarge(ValueError): - def __init__(self, bdir): - ValueError.__init__(self, 'The directory {0} is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'.format(bdir)) - - -def realpath(path): - return os.path.abspath(os.path.realpath(path)) - - -class INotifyTreeWatcher(INotify): - is_dummy = False - - def __init__(self, basedir, ignore_event=None): - super(INotifyTreeWatcher, self).__init__() - self.basedir = realpath(basedir) - self.watch_tree() - self.modified = True - self.ignore_event = (lambda path, name: False) if ignore_event is None else ignore_event - - def watch_tree(self): - self.watched_dirs = {} - self.watched_rmap = {} - try: - self.add_watches(self.basedir) - except OSError as e: - if e.errno == errno.ENOSPC: - raise DirTooLarge(self.basedir) - - def add_watches(self, base, top_level=True): - ''' Add watches for this directory and all its descendant directories, - recursively. ''' - base = realpath(base) - # There may exist a link which leads to an endless - # add_watches loop or to maximum recursion depth exceeded - if not top_level and base in self.watched_dirs: - return - try: - is_dir = self.add_watch(base) - except OSError as e: - if e.errno == errno.ENOENT: - # The entry could have been deleted between listdir() and - # add_watch(). - if top_level: - raise NoSuchDir('The dir {0} does not exist'.format(base)) - return - if e.errno == errno.EACCES: - # We silently ignore entries for which we dont have permission, - # unless they are the top level dir - if top_level: - raise NoSuchDir('You do not have permission to monitor {0}'.format(base)) - return - raise - else: - if is_dir: - try: - files = os.listdir(base) - except OSError as e: - if e.errno in (errno.ENOTDIR, errno.ENOENT): - # The dir was deleted/replaced between the add_watch() - # and listdir() - if top_level: - raise NoSuchDir('The dir {0} does not exist'.format(base)) - return - raise - for x in files: - self.add_watches(os.path.join(base, x), top_level=False) - elif top_level: - # The top level dir is a file, not good. - raise NoSuchDir('The dir {0} does not exist'.format(base)) - - def add_watch(self, path): - import ctypes - bpath = path if isinstance(path, bytes) else path.encode(self.fenc) - wd = self._add_watch( - self._inotify_fd, ctypes.c_char_p(bpath), - # Ignore symlinks and watch only directories - self.DONT_FOLLOW | self.ONLYDIR | - - self.MODIFY | self.CREATE | self.DELETE | - self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | - self.ATTRIB | self.DELETE_SELF - ) - if wd == -1: - eno = ctypes.get_errno() - if eno == errno.ENOTDIR: - return False - raise OSError(eno, 'Failed to add watch for: {0}: {1}'.format(path, self.os.strerror(eno))) - self.watched_dirs[path] = wd - self.watched_rmap[wd] = path - return True - - def process_event(self, wd, mask, cookie, name): - if wd == -1 and (mask & self.Q_OVERFLOW): - # We missed some INOTIFY events, so we dont - # know the state of any tracked dirs. - self.watch_tree() - self.modified = True - return - path = self.watched_rmap.get(wd, None) - if path is not None: - if not self.ignore_event(path, name): - self.modified = True - if mask & self.CREATE: - # A new sub-directory might have been created, monitor it. - try: - self.add_watch(os.path.join(path, name)) - except OSError as e: - if e.errno == errno.ENOENT: - # Deleted before add_watch() - pass - elif e.errno == errno.ENOSPC: - raise DirTooLarge(self.basedir) - else: - raise - if (mask & self.DELETE_SELF or mask & self.MOVE_SELF) and path == self.basedir: - raise BaseDirChanged('The directory %s was moved/deleted' % path) - - def __call__(self): - self.read() - ret = self.modified - self.modified = False - return ret - - -class DummyTreeWatcher(object): - is_dummy = True - - def __init__(self, basedir): - self.basedir = realpath(basedir) - - def __call__(self): - return False - - -class TreeWatcher(object): - def __init__(self, expire_time=10): - self.watches = {} - self.last_query_times = {} - self.expire_time = expire_time * 60 - - def watch(self, path, logger=None, ignore_event=None): - path = realpath(path) - try: - w = INotifyTreeWatcher(path, ignore_event=ignore_event) - except (INotifyError, DirTooLarge) as e: - if logger is not None and not isinstance(e, INotifyError): - logger.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) - w = DummyTreeWatcher(path) - self.watches[path] = w - return w - - def expire_old_queries(self): - pop = [] - now = monotonic() - for path, lt in self.last_query_times.items(): - if now - lt > self.expire_time: - pop.append(path) - for path in pop: - del self.last_query_times[path] - - def __call__(self, path, logger=None, ignore_event=None): - path = realpath(path) - self.expire_old_queries() - self.last_query_times[path] = monotonic() - w = self.watches.get(path, None) - if w is None: - try: - self.watch(path, logger=logger, ignore_event=ignore_event) - except NoSuchDir: - pass - return True - try: - return w() - except BaseDirChanged: - self.watches.pop(path, None) - return True - except DirTooLarge as e: - if logger is not None: - logger.warn(str(e)) - self.watches[path] = DummyTreeWatcher(path) - return False - - -if __name__ == '__main__': - w = INotifyTreeWatcher(sys.argv[-1]) - w() - print ('Monitoring', sys.argv[-1], 'press Ctrl-C to stop') - try: - while True: - if w(): - print (sys.argv[-1], 'changed') - sleep(1) - except KeyboardInterrupt: - raise SystemExit(0) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 8876dd15..ea11b345 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -6,6 +6,8 @@ import errno from threading import Lock from collections import defaultdict +from powerline.lib.watcher import create_tree_watcher + def generate_directories(path): if os.path.isdir(path): @@ -178,9 +180,9 @@ def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_f class TreeStatusCache(dict): - def __init__(self): - from powerline.lib.tree_watcher import TreeWatcher - self.tw = TreeWatcher() + def __init__(self, pl): + self.tw = create_tree_watcher(pl) + self.pl = pl def cache_and_get(self, key, status): ans = self.get(key, self) @@ -188,24 +190,24 @@ class TreeStatusCache(dict): ans = self[key] = status() return ans - def __call__(self, repo, logger): + def __call__(self, repo): key = repo.directory try: - if self.tw(key, logger=logger, ignore_event=getattr(repo, 'ignore_event', None)): + if self.tw(key, ignore_event=getattr(repo, 'ignore_event', None)): self.pop(key, None) except OSError as e: - logger.warn('Failed to check %s for changes, with error: %s' % key, e) + self.pl.warn('Failed to check %s for changes, with error: %s' % key, e) return self.cache_and_get(key, repo.status) _tree_status_cache = None -def tree_status(repo, logger): +def tree_status(repo, pl): global _tree_status_cache if _tree_status_cache is None: - _tree_status_cache = TreeStatusCache() - return _tree_status_cache(repo, logger) + _tree_status_cache = TreeStatusCache(pl) + return _tree_status_cache(repo) vcs_props = ( diff --git a/powerline/lib/watcher/__init__.py b/powerline/lib/watcher/__init__.py index 816353b8..a36df182 100644 --- a/powerline/lib/watcher/__init__.py +++ b/powerline/lib/watcher/__init__.py @@ -4,7 +4,9 @@ from __future__ import unicode_literals, absolute_import import sys from powerline.lib.watcher.stat import StatFileWatcher -from powerline.lib.watcher.inotify import INotifyFileWatcher, INotifyError +from powerline.lib.watcher.inotify import INotifyFileWatcher +from powerline.lib.watcher.tree import TreeWatcher +from powerline.lib.inotify import INotifyError def create_file_watcher(pl, watcher_type='auto', expire_time=10): @@ -47,3 +49,18 @@ def create_file_watcher(pl, watcher_type='auto', expire_time=10): pl.debug('Using stat-based watcher') return StatFileWatcher() + + +def create_tree_watcher(pl, watcher_type='auto', expire_time=10): + '''Create an object that can watch for changes in specified directories + + :param PowerlineLogger pl: + Logger. + :param str watcher_type: + Watcher type. Currently the only supported types are ``inotify`` (linux + only), ``dummy`` and ``auto``. + :param int expire_time: + Number of minutes since last ``.__call__()`` before inotify watcher will + stop watching given file. + ''' + return TreeWatcher(pl, watcher_type, expire_time) diff --git a/powerline/lib/watcher/inotify.py b/powerline/lib/watcher/inotify.py index dbbdbcc3..4c7724f9 100644 --- a/powerline/lib/watcher/inotify.py +++ b/powerline/lib/watcher/inotify.py @@ -135,3 +135,129 @@ class INotifyFileWatcher(INotify): except OSError: pass super(INotifyFileWatcher, self).close() + + +class NoSuchDir(ValueError): + pass + + +class BaseDirChanged(ValueError): + pass + + +class DirTooLarge(ValueError): + def __init__(self, bdir): + ValueError.__init__(self, 'The directory {0} is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'.format(bdir)) + + +class INotifyTreeWatcher(INotify): + is_dummy = False + + def __init__(self, basedir, ignore_event=None): + super(INotifyTreeWatcher, self).__init__() + self.basedir = realpath(basedir) + self.watch_tree() + self.modified = True + self.ignore_event = (lambda path, name: False) if ignore_event is None else ignore_event + + def watch_tree(self): + self.watched_dirs = {} + self.watched_rmap = {} + try: + self.add_watches(self.basedir) + except OSError as e: + if e.errno == errno.ENOSPC: + raise DirTooLarge(self.basedir) + + def add_watches(self, base, top_level=True): + ''' Add watches for this directory and all its descendant directories, + recursively. ''' + base = realpath(base) + # There may exist a link which leads to an endless + # add_watches loop or to maximum recursion depth exceeded + if not top_level and base in self.watched_dirs: + return + try: + is_dir = self.add_watch(base) + except OSError as e: + if e.errno == errno.ENOENT: + # The entry could have been deleted between listdir() and + # add_watch(). + if top_level: + raise NoSuchDir('The dir {0} does not exist'.format(base)) + return + if e.errno == errno.EACCES: + # We silently ignore entries for which we dont have permission, + # unless they are the top level dir + if top_level: + raise NoSuchDir('You do not have permission to monitor {0}'.format(base)) + return + raise + else: + if is_dir: + try: + files = os.listdir(base) + except OSError as e: + if e.errno in (errno.ENOTDIR, errno.ENOENT): + # The dir was deleted/replaced between the add_watch() + # and listdir() + if top_level: + raise NoSuchDir('The dir {0} does not exist'.format(base)) + return + raise + for x in files: + self.add_watches(os.path.join(base, x), top_level=False) + elif top_level: + # The top level dir is a file, not good. + raise NoSuchDir('The dir {0} does not exist'.format(base)) + + def add_watch(self, path): + import ctypes + bpath = path if isinstance(path, bytes) else path.encode(self.fenc) + wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), + # Ignore symlinks and watch only directories + self.DONT_FOLLOW | self.ONLYDIR | + + self.MODIFY | self.CREATE | self.DELETE | + self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | + self.ATTRIB | self.DELETE_SELF) + if wd == -1: + eno = ctypes.get_errno() + if eno == errno.ENOTDIR: + return False + raise OSError(eno, 'Failed to add watch for: {0}: {1}'.format(path, self.os.strerror(eno))) + self.watched_dirs[path] = wd + self.watched_rmap[wd] = path + return True + + def process_event(self, wd, mask, cookie, name): + if wd == -1 and (mask & self.Q_OVERFLOW): + # We missed some INOTIFY events, so we dont + # know the state of any tracked dirs. + self.watch_tree() + self.modified = True + return + path = self.watched_rmap.get(wd, None) + if path is not None: + if not self.ignore_event(path, name): + self.modified = True + if mask & self.CREATE: + # A new sub-directory might have been created, monitor it. + try: + self.add_watch(os.path.join(path, name)) + except OSError as e: + if e.errno == errno.ENOENT: + # Deleted before add_watch() + pass + elif e.errno == errno.ENOSPC: + raise DirTooLarge(self.basedir) + else: + raise + if (mask & self.DELETE_SELF or mask & self.MOVE_SELF) and path == self.basedir: + raise BaseDirChanged('The directory %s was moved/deleted' % path) + + def __call__(self): + self.read() + ret = self.modified + self.modified = False + return ret diff --git a/powerline/lib/watcher/tree.py b/powerline/lib/watcher/tree.py new file mode 100644 index 00000000..127261f7 --- /dev/null +++ b/powerline/lib/watcher/tree.py @@ -0,0 +1,83 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, absolute_import, print_function) + +import sys + +from powerline.lib.monotonic import monotonic +from powerline.lib.inotify import INotifyError +from powerline.lib.path import realpath +from powerline.lib.watcher.inotify import INotifyTreeWatcher, DirTooLarge, NoSuchDir, BaseDirChanged + + +class DummyTreeWatcher(object): + is_dummy = True + + def __init__(self, basedir): + self.basedir = realpath(basedir) + + def __call__(self): + return False + + +class TreeWatcher(object): + def __init__(self, pl, watcher_type, expire_time): + self.watches = {} + self.last_query_times = {} + self.expire_time = expire_time * 60 + self.pl = pl + self.watcher_type = watcher_type + + def get_watcher(self, path, ignore_event): + if self.watcher_type == 'inotify': + return INotifyTreeWatcher(path, ignore_event=ignore_event) + if self.watcher_type == 'dummy': + return DummyTreeWatcher(path) + # FIXME + if self.watcher_type == 'stat': + return DummyTreeWatcher(path) + if self.watcher_type == 'auto': + if sys.platform.startswith('linux'): + try: + return INotifyTreeWatcher(path, ignore_event=ignore_event) + except (INotifyError, DirTooLarge) as e: + if not isinstance(e, INotifyError): + self.pl.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) + return DummyTreeWatcher(path) + else: + raise ValueError('Unknown watcher type: {0}'.format(self.watcher_type)) + + def watch(self, path, ignore_event=None): + path = realpath(path) + w = self.get_watcher(path, ignore_event) + self.watches[path] = w + return w + + def expire_old_queries(self): + pop = [] + now = monotonic() + for path, lt in self.last_query_times.items(): + if now - lt > self.expire_time: + pop.append(path) + for path in pop: + del self.last_query_times[path] + + def __call__(self, path, ignore_event=None): + path = realpath(path) + self.expire_old_queries() + self.last_query_times[path] = monotonic() + w = self.watches.get(path, None) + if w is None: + try: + self.watch(path, ignore_event=ignore_event) + except NoSuchDir: + pass + return True + try: + return w() + except BaseDirChanged: + self.watches.pop(path, None) + return True + except DirTooLarge as e: + self.pl.warn(str(e)) + self.watches[path] = DummyTreeWatcher(path) + return False diff --git a/tests/test_lib.py b/tests/test_lib.py index 345b4e10..ab6cd072 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -6,7 +6,7 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess, get_fallback_create_watcher from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic -from powerline.lib.watcher import create_file_watcher, INotifyError +from powerline.lib.watcher import create_file_watcher, create_tree_watcher, INotifyError from powerline.lib.vcs.git import git_directory from powerline import get_fallback_logger import threading @@ -14,6 +14,7 @@ import os import sys import re import platform +import shutil from time import sleep from subprocess import call, PIPE from functools import partial @@ -433,13 +434,11 @@ class TestFilesystemWatchers(TestCase): self.do_test_for_change(w, f2) def test_tree_watcher(self): - from powerline.lib.tree_watcher import TreeWatcher - tw = TreeWatcher() + tw = create_tree_watcher(get_fallback_logger()) subdir = os.path.join(INOTIFY_DIR, 'subdir') os.mkdir(subdir) if tw.watch(INOTIFY_DIR).is_dummy: raise SkipTest('No tree watcher available') - import shutil self.assertTrue(tw(INOTIFY_DIR)) self.assertFalse(tw(INOTIFY_DIR)) changed = partial(self.do_test_for_change, tw, INOTIFY_DIR) From cb99c06027bcc23b712a7d6180ba0bc3f31a73ea Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jun 2014 01:00:44 +0400 Subject: [PATCH 1273/1472] Move watcher tests into a separate file --- tests/test_lib.py | 97 +------------------------------ tests/test_watcher.py | 130 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 95 deletions(-) create mode 100644 tests/test_watcher.py diff --git a/tests/test_lib.py b/tests/test_lib.py index ab6cd072..07e356a0 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -6,20 +6,16 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess, get_fallback_create_watcher from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic -from powerline.lib.watcher import create_file_watcher, create_tree_watcher, INotifyError from powerline.lib.vcs.git import git_directory -from powerline import get_fallback_logger import threading import os import sys import re import platform -import shutil from time import sleep from subprocess import call, PIPE -from functools import partial -from tests import TestCase, SkipTest from tests.lib import Pl +from tests import TestCase def thread_number(): @@ -379,93 +375,6 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') -class TestFilesystemWatchers(TestCase): - def do_test_for_change(self, watcher, path): - st = monotonic() - while monotonic() - st < 1: - if watcher(path): - return - sleep(0.1) - self.fail('The change to {0} was not detected'.format(path)) - - def test_file_watcher(self): - try: - w = create_file_watcher(pl=get_fallback_logger(), watcher_type='inotify') - except INotifyError: - raise SkipTest('This test is not suitable for a stat based file watcher') - f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) - with open(f1, 'wb'): - with open(f2, 'wb'): - with open(f3, 'wb'): - pass - ne = os.path.join(INOTIFY_DIR, 'notexists') - self.assertRaises(OSError, w, ne) - self.assertTrue(w(f1)) - self.assertTrue(w(f2)) - os.utime(f1, None), os.utime(f2, None) - self.do_test_for_change(w, f1) - self.do_test_for_change(w, f2) - # Repeat once - os.utime(f1, None), os.utime(f2, None) - self.do_test_for_change(w, f1) - self.do_test_for_change(w, f2) - # Check that no false changes are reported - self.assertFalse(w(f1), 'Spurious change detected') - self.assertFalse(w(f2), 'Spurious change detected') - # Check that open the file with 'w' triggers a change - with open(f1, 'wb'): - with open(f2, 'wb'): - pass - self.do_test_for_change(w, f1) - self.do_test_for_change(w, f2) - # Check that writing to a file with 'a' triggers a change - with open(f1, 'ab') as f: - f.write(b'1') - self.do_test_for_change(w, f1) - # Check that deleting a file registers as a change - os.unlink(f1) - self.do_test_for_change(w, f1) - # Test that changing the inode of a file does not cause it to stop - # being watched - os.rename(f3, f2) - self.do_test_for_change(w, f2) - self.assertFalse(w(f2), 'Spurious change detected') - os.utime(f2, None) - self.do_test_for_change(w, f2) - - def test_tree_watcher(self): - tw = create_tree_watcher(get_fallback_logger()) - subdir = os.path.join(INOTIFY_DIR, 'subdir') - os.mkdir(subdir) - if tw.watch(INOTIFY_DIR).is_dummy: - raise SkipTest('No tree watcher available') - self.assertTrue(tw(INOTIFY_DIR)) - self.assertFalse(tw(INOTIFY_DIR)) - changed = partial(self.do_test_for_change, tw, INOTIFY_DIR) - open(os.path.join(INOTIFY_DIR, 'tree1'), 'w').close() - changed() - open(os.path.join(subdir, 'tree1'), 'w').close() - changed() - os.unlink(os.path.join(subdir, 'tree1')) - changed() - os.rmdir(subdir) - changed() - os.mkdir(subdir) - changed() - os.rename(subdir, subdir + '1') - changed() - shutil.rmtree(subdir + '1') - changed() - os.mkdir(subdir) - f = os.path.join(subdir, 'f') - open(f, 'w').close() - changed() - with open(f, 'a') as s: - s.write(' ') - changed() - os.rename(f, f + '1') - changed() - use_mercurial = use_bzr = (sys.version_info < (3, 0) and platform.python_implementation() == 'CPython') @@ -635,7 +544,6 @@ old_cwd = None GIT_REPO = 'git_repo' + os.environ.get('PYTHON', '') HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') -INOTIFY_DIR = 'inotify' + os.environ.get('PYTHON', '') def setUpModule(): @@ -660,13 +568,12 @@ def setUpModule(): call(['bzr', 'config', 'email=Foo '], cwd=BZR_REPO) call(['bzr', 'config', 'nickname=test_powerline'], cwd=BZR_REPO) call(['bzr', 'config', 'create_signatures=0'], cwd=BZR_REPO) - os.mkdir(INOTIFY_DIR) def tearDownModule(): global old_cwd global old_HGRCPATH - for repo_dir in [INOTIFY_DIR, GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): + for repo_dir in [GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): for root, dirs, files in list(os.walk(repo_dir, topdown=False)): for file in files: os.remove(os.path.join(root, file)) diff --git a/tests/test_watcher.py b/tests/test_watcher.py new file mode 100644 index 00000000..356790b6 --- /dev/null +++ b/tests/test_watcher.py @@ -0,0 +1,130 @@ +# vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals, print_function, division + +from powerline.lib.watcher import create_file_watcher, create_tree_watcher, INotifyError +from powerline import get_fallback_logger +from powerline.lib.monotonic import monotonic + +import shutil +from time import sleep +from functools import partial +import os + +from tests import TestCase, SkipTest + + +INOTIFY_DIR = 'inotify' + os.environ.get('PYTHON', '') + + +class TestFilesystemWatchers(TestCase): + def do_test_for_change(self, watcher, path): + st = monotonic() + while monotonic() - st < 1: + if watcher(path): + return + sleep(0.1) + self.fail('The change to {0} was not detected'.format(path)) + + def test_file_watcher(self): + try: + w = create_file_watcher(pl=get_fallback_logger(), watcher_type='inotify') + except INotifyError: + raise SkipTest('This test is not suitable for a stat based file watcher') + f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) + with open(f1, 'wb'): + with open(f2, 'wb'): + with open(f3, 'wb'): + pass + ne = os.path.join(INOTIFY_DIR, 'notexists') + self.assertRaises(OSError, w, ne) + self.assertTrue(w(f1)) + self.assertTrue(w(f2)) + os.utime(f1, None), os.utime(f2, None) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Repeat once + os.utime(f1, None), os.utime(f2, None) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Check that no false changes are reported + self.assertFalse(w(f1), 'Spurious change detected') + self.assertFalse(w(f2), 'Spurious change detected') + # Check that open the file with 'w' triggers a change + with open(f1, 'wb'): + with open(f2, 'wb'): + pass + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Check that writing to a file with 'a' triggers a change + with open(f1, 'ab') as f: + f.write(b'1') + self.do_test_for_change(w, f1) + # Check that deleting a file registers as a change + os.unlink(f1) + self.do_test_for_change(w, f1) + # Test that changing the inode of a file does not cause it to stop + # being watched + os.rename(f3, f2) + self.do_test_for_change(w, f2) + self.assertFalse(w(f2), 'Spurious change detected') + os.utime(f2, None) + self.do_test_for_change(w, f2) + + def test_tree_watcher(self): + tw = create_tree_watcher(get_fallback_logger()) + subdir = os.path.join(INOTIFY_DIR, 'subdir') + os.mkdir(subdir) + if tw.watch(INOTIFY_DIR).is_dummy: + raise SkipTest('No tree watcher available') + self.assertTrue(tw(INOTIFY_DIR)) + self.assertFalse(tw(INOTIFY_DIR)) + changed = partial(self.do_test_for_change, tw, INOTIFY_DIR) + open(os.path.join(INOTIFY_DIR, 'tree1'), 'w').close() + changed() + open(os.path.join(subdir, 'tree1'), 'w').close() + changed() + os.unlink(os.path.join(subdir, 'tree1')) + changed() + os.rmdir(subdir) + changed() + os.mkdir(subdir) + changed() + os.rename(subdir, subdir + '1') + changed() + shutil.rmtree(subdir + '1') + changed() + os.mkdir(subdir) + f = os.path.join(subdir, 'f') + open(f, 'w').close() + changed() + with open(f, 'a') as s: + s.write(' ') + changed() + os.rename(f, f + '1') + changed() + + +old_cwd = None + + +def setUpModule(): + global old_cwd + old_cwd = os.getcwd() + os.chdir(os.path.dirname(__file__)) + os.mkdir(INOTIFY_DIR) + + +def tearDownModule(): + for d in [INOTIFY_DIR]: + for root, dirs, files in list(os.walk(d, topdown=False)): + for file in files: + os.remove(os.path.join(root, file)) + for dir in dirs: + os.rmdir(os.path.join(root, dir)) + os.rmdir(d) + os.chdir(old_cwd) + + +if __name__ == '__main__': + from tests import main + main() From 39251ce1cb728c7499b7ef27b69649c064c85053 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 15:00:49 +0400 Subject: [PATCH 1274/1472] Remove in-method imports from inotify --- powerline/lib/inotify.py | 10 ++++------ powerline/lib/watcher/inotify.py | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index 48fe6ae7..ec40bcd7 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -7,6 +7,10 @@ __docformat__ = 'restructuredtext en' import sys import os import errno +import ctypes +import struct + +from ctypes.util import find_library class INotifyError(Exception): @@ -28,10 +32,8 @@ def load_inotify(): raise INotifyError('INotify not available on windows') if sys.platform == 'darwin': raise INotifyError('INotify not available on OS X') - import ctypes if not hasattr(ctypes, 'c_ssize_t'): raise INotifyError('You need python >= 2.7 to use inotify') - from ctypes.util import find_library name = find_library('c') if not name: raise INotifyError('Cannot find C library') @@ -107,8 +109,6 @@ class INotify(object): NONBLOCK = 0x800 def __init__(self, cloexec=True, nonblock=True): - import ctypes - import struct self._init1, self._add_watch, self._rm_watch, self._read = load_inotify() flags = 0 if cloexec: @@ -130,7 +130,6 @@ class INotify(object): self.os = os def handle_error(self): - import ctypes eno = ctypes.get_errno() extra = '' if eno == errno.ENOSPC: @@ -155,7 +154,6 @@ class INotify(object): del self._inotify_fd def read(self, get_name=True): - import ctypes buf = [] while True: num = self._read(self._inotify_fd, self._buf, len(self._buf)) diff --git a/powerline/lib/watcher/inotify.py b/powerline/lib/watcher/inotify.py index 4c7724f9..48c730c0 100644 --- a/powerline/lib/watcher/inotify.py +++ b/powerline/lib/watcher/inotify.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals, absolute_import import errno import os +import ctypes from threading import RLock @@ -80,7 +81,6 @@ class INotifyFileWatcher(INotify): def watch(self, path): ''' Register a watch for the file/directory named path. Raises an OSError if path does not exist. ''' - import ctypes path = realpath(path) with self.lock: if path not in self.watches: @@ -212,7 +212,6 @@ class INotifyTreeWatcher(INotify): raise NoSuchDir('The dir {0} does not exist'.format(base)) def add_watch(self, path): - import ctypes bpath = path if isinstance(path, bytes) else path.encode(self.fenc) wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), # Ignore symlinks and watch only directories From 446eb42ea8c8457d192df20c83754a2b95ebf333 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 8 Apr 2013 23:46:29 +0400 Subject: [PATCH 1275/1472] Replace various __import__ calls with one Powerline.get_module_attr Also makes some more errors non-fatal: - Failure to import renderer class in case there is an existing renderer object - Failure to import segment function - Failure to import matcher function One of the purposes: create a function that is able to collect all imported modules to reload them (really purge out from `sys.modules` and let the python do its job when reimporting powerline and recreating Powerline and other objects). --- powerline/__init__.py | 42 +++++++++++++++++++++++++++++++++++++----- powerline/matcher.py | 19 ------------------- powerline/segment.py | 15 ++++++--------- powerline/theme.py | 3 ++- powerline/vim.py | 27 +++++++++++++++++++-------- 5 files changed, 64 insertions(+), 42 deletions(-) delete mode 100644 powerline/matcher.py diff --git a/powerline/__init__.py b/powerline/__init__.py index f2a7a166..3e192cbb 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -407,6 +407,9 @@ class Powerline(object): 'common_config': self.common_config, 'run_once': self.run_once, 'shutdown_event': self.shutdown_event, + # Note: creates implicit reference to self meaning + # reference cycle. + 'get_module_attr': self.get_module_attr, }, ) @@ -468,11 +471,12 @@ class Powerline(object): self.renderer_options['theme_config'] = self.load_theme_config(self.ext_config.get('theme', 'default')) if create_renderer: - try: - Renderer = __import__(self.renderer_module, fromlist=['renderer']).renderer - except Exception as e: - self.exception('Failed to import renderer module: {0}', str(e)) - sys.exit(1) + Renderer = self.get_module_attr(self.renderer_module, 'renderer') + if not Renderer: + if hasattr(self, 'renderer'): + return + else: + raise ImportError('Failed to obtain renderer') # Renderer updates configuration file via segments’ .startup thus it # should be locked to prevent state when configuration was updated, @@ -652,6 +656,34 @@ class Powerline(object): with self.cr_kwargs_lock: self.cr_kwargs.clear() + def get_module_attr(self, module, attr, prefix='powerline'): + '''Import module and get its attribute. + + Replaces ``from {module} import {attr}``. + + :param str module: + Module name, will be passed as first argument to ``__import__``. + :param str attr: + Module attribute, will be passed to ``__import__`` as the only value + in ``fromlist`` tuple. + + :return: + Attribute value or ``None``. Note: there is no way to distinguish + between successfull import of attribute equal to ``None`` and + unsuccessfull import. + ''' + oldpath = sys.path + sys.path = self.import_paths + sys.path + module = str(module) + attr = str(attr) + try: + return getattr(__import__(module, fromlist=(attr,)), attr) + except Exception as e: + self.pl.exception('Failed to import attr {0} from module {1}: {2}', attr, module, str(e), prefix=prefix) + return None + finally: + sys.path = oldpath + def render(self, *args, **kwargs): '''Update/create renderer if needed and pass all arguments further to ``self.renderer.render()``. diff --git a/powerline/matcher.py b/powerline/matcher.py deleted file mode 100644 index 37049e2f..00000000 --- a/powerline/matcher.py +++ /dev/null @@ -1,19 +0,0 @@ -# vim:fileencoding=utf-8:noet - -from __future__ import absolute_import -import sys - - -def gen_matcher_getter(ext, import_paths): - def get(match_name): - match_module, separator, match_function = match_name.rpartition('.') - if not separator: - match_module = 'powerline.matchers.{0}'.format(ext) - match_function = match_name - oldpath = sys.path - sys.path = import_paths + sys.path - try: - return getattr(__import__(str(match_module), fromlist=[str(match_function)]), match_function) - finally: - sys.path = oldpath - return get diff --git a/powerline/segment.py b/powerline/segment.py index 144f4623..b2a7086c 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -55,14 +55,11 @@ def get_segment_key(merge, *args, **kwargs): def get_function(data, segment): - oldpath = sys.path - sys.path = data['path'] + sys.path segment_module = str(segment.get('module', data['default_module'])) - name = str(segment['name']) - try: - return None, getattr(__import__(segment_module, fromlist=[name]), name), segment_module - finally: - sys.path = oldpath + function = data['get_module_attr'](segment_module, segment['name'], prefix='segment_generator') + if not function: + raise ImportError('Failed to obtain segment function') + return None, function, segment_module def get_string(data, segment): @@ -162,10 +159,10 @@ def process_segment(pl, side, segment_info, parsed_segments, segment): parsed_segments.append(segment) -def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=None): +def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr): data = { 'default_module': default_module or 'powerline.segments.' + ext, - 'path': common_config['paths'], + 'get_module_attr': get_module_attr, } def get_key(merge, segment, module, key, default=None): diff --git a/powerline/theme.py b/powerline/theme.py index 378f8495..d2617dc5 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -29,6 +29,7 @@ class Theme(object): theme_config, common_config, pl, + get_module_attr, main_theme_config=None, run_once=False, shutdown_event=None): @@ -53,7 +54,7 @@ class Theme(object): theme_configs = [theme_config] if main_theme_config: theme_configs.append(main_theme_config) - get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module')) + get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module'), get_module_attr) for segdict in itertools.chain((theme_config['segments'],), theme_config['segments'].get('above', ())): self.segments.append(new_empty_segment_line()) diff --git a/powerline/vim.py b/powerline/vim.py index 2676ac29..a09e24cc 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -6,7 +6,6 @@ import sys from powerline.bindings.vim import vim_get_func, vim_getvar from powerline import Powerline from powerline.lib import mergedicts -from powerline.matcher import gen_matcher_getter import vim from itertools import count @@ -80,18 +79,30 @@ class VimPowerline(Powerline): ) def get_local_themes(self, local_themes): - self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) - if not local_themes: return {} return dict(( - ( - (None if key == '__tabline__' else self.get_matcher(key)), - {'config': self.load_theme_config(val)} + (matcher, {'config': self.load_theme_config(val)}) + for matcher, key, val in ( + ( + (None if k == '__tabline__' else self.get_matcher(k)), + k, + v + ) + for k, v in local_themes.items() + ) if ( + matcher or + key == '__tabline__' ) - for key, val in local_themes.items()) - ) + )) + + def get_matcher(self, match_name): + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.matchers.{0}'.format(self.ext) + match_function = match_name + return self.get_module_attr(match_module, match_function, prefix='matcher_generator') def get_config_paths(self): try: From bbe3210bb6c1d7fbd5962e880301eb175631f9f4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 9 Apr 2013 00:00:28 +0400 Subject: [PATCH 1276/1472] Remove reference cycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switches from powerline ↔ get_module_attr (as a bound method it contains reference to │ ↑ self) └────> theme to ┌────> pl, import_paths │ ↑ powerline → get_module_attr │ ↑ └────> theme This is a separate commit because I am not sure whether it makes sense to bother with this cycle. --- powerline/__init__.py | 64 +++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 3e192cbb..1b4c4cfd 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -270,6 +270,38 @@ else: raise exception +def gen_module_attr_getter(pl, import_paths): + def get_module_attr(module, attr, prefix='powerline'): + '''Import module and get its attribute. + + Replaces ``from {module} import {attr}``. + + :param str module: + Module name, will be passed as first argument to ``__import__``. + :param str attr: + Module attribute, will be passed to ``__import__`` as the only value + in ``fromlist`` tuple. + + :return: + Attribute value or ``None``. Note: there is no way to distinguish + between successfull import of attribute equal to ``None`` and + unsuccessfull import. + ''' + oldpath = sys.path + sys.path = import_paths + sys.path + module = str(module) + attr = str(attr) + try: + return getattr(__import__(module, fromlist=(attr,)), attr) + except Exception as e: + pl.exception('Failed to import attr {0} from module {1}: {2}', attr, module, str(e), prefix=prefix) + return None + finally: + sys.path = oldpath + + return get_module_attr + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -396,6 +428,8 @@ class Powerline(object): if not self.run_once: self.config_loader.set_watcher(self.common_config['watcher']) + self.get_module_attr = gen_module_attr_getter(self.pl, self.import_paths) + self.renderer_options.update( pl=self.pl, term_truecolor=self.common_config['term_truecolor'], @@ -407,8 +441,6 @@ class Powerline(object): 'common_config': self.common_config, 'run_once': self.run_once, 'shutdown_event': self.shutdown_event, - # Note: creates implicit reference to self meaning - # reference cycle. 'get_module_attr': self.get_module_attr, }, ) @@ -656,34 +688,6 @@ class Powerline(object): with self.cr_kwargs_lock: self.cr_kwargs.clear() - def get_module_attr(self, module, attr, prefix='powerline'): - '''Import module and get its attribute. - - Replaces ``from {module} import {attr}``. - - :param str module: - Module name, will be passed as first argument to ``__import__``. - :param str attr: - Module attribute, will be passed to ``__import__`` as the only value - in ``fromlist`` tuple. - - :return: - Attribute value or ``None``. Note: there is no way to distinguish - between successfull import of attribute equal to ``None`` and - unsuccessfull import. - ''' - oldpath = sys.path - sys.path = self.import_paths + sys.path - module = str(module) - attr = str(attr) - try: - return getattr(__import__(module, fromlist=(attr,)), attr) - except Exception as e: - self.pl.exception('Failed to import attr {0} from module {1}: {2}', attr, module, str(e), prefix=prefix) - return None - finally: - sys.path = oldpath - def render(self, *args, **kwargs): '''Update/create renderer if needed and pass all arguments further to ``self.renderer.render()``. From 85189e8b36b3cda9e7f2e2d8fc7ccb484ec5a3bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 9 Apr 2013 00:19:06 +0400 Subject: [PATCH 1277/1472] Add set_event argument to .shutdown() method --- powerline/__init__.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 1b4c4cfd..670ea198 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -726,15 +726,24 @@ class Powerline(object): pass yield FailedUnicode(safe_unicode(e)) - def shutdown(self): - '''Shut down all background threads. Must be run only prior to exiting - current application. + def shutdown(self, set_event=True): + '''Shut down all background threads. + + :param bool set_event: + Set ``shutdown_event`` and call ``renderer.shutdown`` which should + shut down all threads. Set it to False unless you are exiting an + application. + + If set to False this does nothing more then resolving reference + cycle ``powerline → config_loader → bound methods → powerline`` by + unsubscribing from config_loader events. ''' - self.shutdown_event.set() - try: - self.renderer.shutdown() - except AttributeError: - pass + if set_event: + self.shutdown_event.set() + try: + self.renderer.shutdown() + except AttributeError: + pass functions = tuple(self.cr_callbacks.values()) self.config_loader.unregister_functions(set(functions)) self.config_loader.unregister_missing(set(((self.find_config_files, function) for function in functions))) From 109525716119529b3c9eb110d5f053506d60b066 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 20:04:22 +0400 Subject: [PATCH 1278/1472] Pop path from self.loaded on exception --- powerline/lib/config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 522e5f45..d56b3c68 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -200,6 +200,10 @@ class ConfigLoader(MultiRunnedThread): except KeyError: pass self.exception('Error while loading {0}: {1}', path, str(e)) + try: + self.loaded.pop(path) + except KeyError: + pass def run(self): while self.interval is not None and not self.shutdown_event.is_set(): From 3c1ec1959f56b55b337704b3c4ac6a7ec7c31b86 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 15 Apr 2013 08:30:21 +0400 Subject: [PATCH 1279/1472] Record imported modules --- powerline/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 670ea198..67c3a0d9 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -270,7 +270,7 @@ else: raise exception -def gen_module_attr_getter(pl, import_paths): +def gen_module_attr_getter(pl, import_paths, imported_modules): def get_module_attr(module, attr, prefix='powerline'): '''Import module and get its attribute. @@ -292,6 +292,7 @@ def gen_module_attr_getter(pl, import_paths): module = str(module) attr = str(attr) try: + imported_modules.add(module) return getattr(__import__(module, fromlist=(attr,)), attr) except Exception as e: pl.exception('Failed to import attr {0} from module {1}: {2}', attr, module, str(e), prefix=prefix) @@ -381,6 +382,7 @@ class Powerline(object): self.prev_common_config = None self.prev_ext_config = None self.pl = None + self.imported_modules = set() def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): '''(Re)create renderer object. Can be used after Powerline object was @@ -428,7 +430,7 @@ class Powerline(object): if not self.run_once: self.config_loader.set_watcher(self.common_config['watcher']) - self.get_module_attr = gen_module_attr_getter(self.pl, self.import_paths) + self.get_module_attr = gen_module_attr_getter(self.pl, self.import_paths, self.imported_modules) self.renderer_options.update( pl=self.pl, From ca13bc53e444d0dd2e88ca050417d42932f54976 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 15 Apr 2013 08:31:02 +0400 Subject: [PATCH 1280/1472] Add setup function --- powerline/__init__.py | 8 ++ powerline/bindings/ipython/post_0_11.py | 4 +- powerline/bindings/ipython/pre_0_11.py | 4 +- powerline/bindings/vim/plugin/powerline.vim | 6 +- powerline/bindings/zsh/__init__.py | 1 + powerline/ipython.py | 4 + powerline/shell.py | 4 + powerline/vim.py | 105 +++++++++++--------- 8 files changed, 80 insertions(+), 56 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 67c3a0d9..a6958ba9 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -382,6 +382,7 @@ class Powerline(object): self.prev_common_config = None self.prev_ext_config = None self.pl = None + self.setup_args = None self.imported_modules = set() def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): @@ -728,6 +729,13 @@ class Powerline(object): pass yield FailedUnicode(safe_unicode(e)) + def setup(self, *args, **kwargs): + '''Setup the environment to use powerline. + + To be overridden by subclasses, this one only saves args and kwargs. + ''' + self.setup_args = (args, kwargs) + def shutdown(self, set_event=True): '''Shut down all background threads. diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index ec3a4769..12aa8ef0 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -16,8 +16,8 @@ class IpythonInfo(object): class PowerlinePromptManager(PromptManager): def __init__(self, prompt_powerline, non_prompt_powerline, shell): - self.prompt_powerline = prompt_powerline - self.non_prompt_powerline = non_prompt_powerline + prompt_powerline.setup('prompt_powerline', self) + non_prompt_powerline.setup('non_prompt_powerline', self) self.powerline_segment_info = IpythonInfo(shell) self.shell = shell diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index ed6ad75e..68fb34f1 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -19,8 +19,8 @@ class IpythonInfo(object): class PowerlinePrompt(BasePrompt): def __init__(self, powerline, other_powerline, powerline_last_in, old_prompt): - self.powerline = powerline - self.other_powerline = other_powerline + powerline.setup('powerline', self) + other_powerline.setup('other_powerline', self) self.powerline_last_in = powerline_last_in self.powerline_segment_info = IpythonInfo(old_prompt.cache) self.cache = old_prompt.cache diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index cdf92726..e0367e8f 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -49,7 +49,7 @@ if !s:has_python endif unlet s:has_python -let s:import_cmd = 'from powerline.vim import setup as powerline_setup' +let s:import_cmd = 'from powerline.vim import VimPowerline' try let s:pystr = "try:\n" let s:pystr .= " ".s:import_cmd."\n" @@ -120,8 +120,8 @@ endtry let s:can_replace_pyeval = !exists('g:powerline_pyeval') execute s:pycmd 'import vim' -execute s:pycmd 'powerline_setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"), can_replace_pyeval=int(vim.eval("s:can_replace_pyeval")))' -execute s:pycmd 'del powerline_setup' +execute s:pycmd 'VimPowerline().setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"), can_replace_pyeval=int(vim.eval("s:can_replace_pyeval")))' +execute s:pycmd 'del VimPowerline' unlet s:can_replace_pyeval unlet s:pycmd diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index b6ccde1a..1f6ae40b 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -104,6 +104,7 @@ class Prompt(object): def __init__(self, powerline, side, theme, savedpsvar=None, savedps=None, above=False): self.powerline = powerline + powerline.setup(self) self.side = side self.above = above self.savedpsvar = savedpsvar diff --git a/powerline/ipython.py b/powerline/ipython.py index bc244d6d..29c165b1 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -55,3 +55,7 @@ class IpythonPowerline(Powerline): if name in self.theme_overrides: mergedicts(r, self.theme_overrides[name]) return r + + def setup(self, attr, obj): + setattr(obj, attr, self) + super(IpythonPowerline, self).setup(attr, obj) diff --git a/powerline/shell.py b/powerline/shell.py index 2d4152d2..b2fea9e7 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -43,6 +43,10 @@ class ShellPowerline(Powerline): for key, val in local_themes.items() )) + def setup(self, obj): + obj.powerline = self + super(ShellPowerline, self).setup(obj) + def get_argparser(parser=None, *args, **kwargs): if not parser: diff --git a/powerline/vim.py b/powerline/vim.py index a09e24cc..3a0f7c8b 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -110,6 +110,59 @@ class VimPowerline(Powerline): except KeyError: return super(VimPowerline, self).get_config_paths() + def setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True): + super(VimPowerline, self).setup() + import __main__ + if not pyeval: + pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' + can_replace_pyeval = True + if not pycmd: + pycmd = get_default_pycmd() + + set_pycmd(pycmd) + + # pyeval() and vim.bindeval were both introduced in one patch + if not hasattr(vim, 'bindeval') and can_replace_pyeval: + vim.command((''' + function! PowerlinePyeval(e) + {pycmd} powerline.do_pyeval() + endfunction + ''').format(pycmd=pycmd)) + pyeval = 'PowerlinePyeval' + + self.pyeval = pyeval + self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' + + self.update_renderer() + __main__.powerline = self + + if ( + bool(int(vim.eval("has('gui_running') && argc() == 0"))) + and not vim.current.buffer.name + and len(vim.windows) == 1 + ): + # Hack to show startup screen. Problems in GUI: + # - Defining local value of &statusline option while computing global + # value purges startup screen. + # - Defining highlight group while computing statusline purges startup + # screen. + # This hack removes the “while computing statusline” part: both things + # are defined, but they are defined right now. + # + # The above condition disables this hack if no GUI is running, Vim did + # not open any files and there is only one window. Without GUI + # everything works, in other cases startup screen is not shown. + self.new_window() + + # Cannot have this in one line due to weird newline handling (in :execute + # context newline is considered part of the command in just the same cases + # when bar is considered part of the command (unless defining function + # inside :execute)). vim.command is :execute equivalent regarding this case. + vim.command('augroup Powerline') + vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd)) + vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) + vim.command('augroup END') + @staticmethod def get_segment_info(): return {} @@ -211,52 +264,6 @@ def get_default_pycmd(): return 'python' if sys.version_info < (3,) else 'python3' -def setup(pyeval=None, pycmd=None, can_replace_pyeval=True): - import __main__ - if not pyeval: - pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' - can_replace_pyeval = True - if not pycmd: - pycmd = get_default_pycmd() - - set_pycmd(pycmd) - - # pyeval() and vim.bindeval were both introduced in one patch - if not hasattr(vim, 'bindeval') and can_replace_pyeval: - vim.command((''' - function! PowerlinePyeval(e) - {pycmd} powerline.do_pyeval() - endfunction - ''').format(pycmd=pycmd)) - pyeval = 'PowerlinePyeval' - - powerline = VimPowerline(pyeval) - powerline.update_renderer() - __main__.powerline = powerline - - if ( - bool(int(vim.eval("has('gui_running') && argc() == 0"))) - and not vim.current.buffer.name - and len(vim.windows) == 1 - ): - # Hack to show startup screen. Problems in GUI: - # - Defining local value of &statusline option while computing global - # value purges startup screen. - # - Defining highlight group while computing statusline purges startup - # screen. - # This hack removes the “while computing statusline” part: both things - # are defined, but they are defined right now. - # - # The above condition disables this hack if no GUI is running, Vim did - # not open any files and there is only one window. Without GUI - # everything works, in other cases startup screen is not shown. - powerline.new_window() - - # Cannot have this in one line due to weird newline handling (in :execute - # context newline is considered part of the command in just the same cases - # when bar is considered part of the command (unless defining function - # inside :execute)). vim.command is :execute equivalent regarding this case. - vim.command('augroup Powerline') - vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd)) - vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) - vim.command('augroup END') +def setup(*args, **kwargs): + powerline = VimPowerline() + return powerline.setup(*args, **kwargs) From 94354475b5d259d012e4d8eab8c270938dbcadf6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 15 Apr 2013 23:49:04 +0400 Subject: [PATCH 1281/1472] Add support for full powerline reloading at runtime Is not guaranteed to work in all cases. --- powerline/__init__.py | 58 +++++++++++++++++++++---- powerline/bindings/ipython/post_0_11.py | 4 +- powerline/bindings/ipython/pre_0_11.py | 4 +- powerline/ipython.py | 4 +- powerline/shell.py | 4 +- powerline/vim.py | 4 +- 6 files changed, 59 insertions(+), 19 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index a6958ba9..716c8abf 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -336,14 +336,22 @@ class Powerline(object): Instance of the class that manages (re)loading of the configuration. ''' - def __init__(self, - ext, - renderer_module=None, - run_once=False, - logger=None, - use_daemon_threads=True, - shutdown_event=None, - config_loader=None): + def __init__(self, *args, **kwargs): + self.init_args = (args, kwargs) + self.init(*args, **kwargs) + + def init(self, + ext, + renderer_module=None, + run_once=False, + logger=None, + use_daemon_threads=True, + shutdown_event=None, + config_loader=None): + '''Do actual initialization. + + __init__ function only stores the arguments. + ''' self.ext = ext self.run_once = run_once self.logger = logger @@ -732,10 +740,42 @@ class Powerline(object): def setup(self, *args, **kwargs): '''Setup the environment to use powerline. - To be overridden by subclasses, this one only saves args and kwargs. + To be overridden by subclasses, this one only saves args and kwargs and + unsets shutdown_event. ''' + self.shutdown_event.clear() self.setup_args = (args, kwargs) + def reload(self): + '''Reload powerline after update. + + Should handle most (but not all) powerline updates. + + Purges out all powerline modules and modules imported by powerline for + segment and matcher functions. Requires defining ``setup`` function that + updates reference to main powerline object. + + .. warning:: + Not guaranteed to work properly, use it at your own risk. It + may break your python code. + ''' + from imp import reload + modules = self.imported_modules | set((module for module in sys.modules if module.startswith('powerline'))) + modules_holder = [] + for module in modules: + try: + # Needs to hold module to prevent garbage collecting until they + # are all reloaded. + modules_holder.append(sys.modules.pop(module)) + except KeyError: + pass + PowerlineClass = getattr(__import__(self.__module__, fromlist=(self.__class__.__name__,)), self.__class__.__name__) + self.shutdown(set_event=True) + init_args, init_kwargs = self.init_args + powerline = PowerlineClass(*init_args, **init_kwargs) + setup_args, setup_kwargs = self.setup_args + powerline.setup(*setup_args, **setup_kwargs) + def shutdown(self, set_event=True): '''Shut down all background threads. diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 12aa8ef0..e30d8797 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -43,12 +43,12 @@ class PowerlinePromptManager(PromptManager): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, ip, is_prompt, old_widths): + def init(self, ip, is_prompt, old_widths): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) self.paths = config.get('paths') - super(ConfigurableIpythonPowerline, self).__init__(is_prompt, old_widths) + super(ConfigurableIpythonPowerline, self).init(is_prompt, old_widths) old_prompt_manager = None diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 68fb34f1..e740c8f3 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -86,11 +86,11 @@ class PowerlinePrompt2(PowerlinePromptOut): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): + def init(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides self.paths = paths - super(ConfigurableIpythonPowerline, self).__init__(is_prompt, old_widths) + super(ConfigurableIpythonPowerline, self).init(is_prompt, old_widths) def setup(**kwargs): diff --git a/powerline/ipython.py b/powerline/ipython.py index 29c165b1..7d675058 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -23,8 +23,8 @@ class RewriteResult(object): class IpythonPowerline(Powerline): - def __init__(self, is_prompt, old_widths): - super(IpythonPowerline, self).__init__( + def init(self, is_prompt, old_widths): + super(IpythonPowerline, self).init( 'ipython', renderer_module=('.prompt' if is_prompt else None), use_daemon_threads=True diff --git a/powerline/shell.py b/powerline/shell.py index b2fea9e7..cbd55fae 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -14,10 +14,10 @@ def mergeargs(argvalue): class ShellPowerline(Powerline): - def __init__(self, args, **kwargs): + def init(self, args, **kwargs): self.args = args self.theme_option = args.theme_option - super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, **kwargs) + super(ShellPowerline, self).init(args.ext[0], args.renderer_module, **kwargs) def load_main_config(self): r = super(ShellPowerline, self).load_main_config() diff --git a/powerline/vim.py b/powerline/vim.py index 3a0f7c8b..3594a6c0 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -23,8 +23,8 @@ def _override_from(config, override_varname): class VimPowerline(Powerline): - def __init__(self, pyeval='PowerlinePyeval', **kwargs): - super(VimPowerline, self).__init__('vim', **kwargs) + def init(self, pyeval='PowerlinePyeval', **kwargs): + super(VimPowerline, self).init('vim', **kwargs) self.last_window_id = 1 self.pyeval = pyeval self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' From fe7aad7695bd091bba60f66e5feeda6229f3431c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 18:30:25 +0400 Subject: [PATCH 1282/1472] Update documentation for `Powerline.init` --- powerline/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 716c8abf..dd3d37c3 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -350,7 +350,11 @@ class Powerline(object): config_loader=None): '''Do actual initialization. - __init__ function only stores the arguments. + __init__ function only stores the arguments and runs this function. This + function exists for powerline to be able to reload itself: it is easier + to make ``__init__`` store arguments and call overriddable ``init`` than + tell developers that each time they override Powerline.__init__ in + subclasses they must store actual arguments. ''' self.ext = ext self.run_once = run_once From f88d6cde0f94cde4391db3038a494d69bc31db92 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 19:56:47 +0400 Subject: [PATCH 1283/1472] Fix typo in unicode_terminus theme: make it use one space --- powerline/config_files/themes/unicode_terminus.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json index 79c21dae..aa9c679d 100644 --- a/powerline/config_files/themes/unicode_terminus.json +++ b/powerline/config_files/themes/unicode_terminus.json @@ -56,7 +56,7 @@ "before": "MAIL " }, "powerline.segments.common.virtualenv": { - "before": "(e) " + "before": "(e) " }, "powerline.segments.common.hostname": { "before": "⌂ " From ff78eaa35c3c92e06b77409d7f878eeff40595d2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 18 Aug 2014 22:45:32 +0400 Subject: [PATCH 1284/1472] Do not override Powerline.setup(), use .do_setup() for this Same reason as for `.__init__()` which was replaced by `.init()` --- powerline/__init__.py | 14 ++++++++++++-- powerline/ipython.py | 3 +-- powerline/shell.py | 3 +-- powerline/vim.py | 3 +-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index dd3d37c3..1f57940e 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -744,11 +744,21 @@ class Powerline(object): def setup(self, *args, **kwargs): '''Setup the environment to use powerline. - To be overridden by subclasses, this one only saves args and kwargs and - unsets shutdown_event. + Must not be overridden by subclasses. This one only saves setup + arguments for :py:meth:`reload` method and calls :py:meth:`do_setup`. ''' self.shutdown_event.clear() self.setup_args = (args, kwargs) + self.do_setup(*args, **kwargs) + + @staticmethod + def do_setup(): + '''Function that does initialization + + Should be overridden by subclasses. May accept any number of regular or + keyword arguments. + ''' + pass def reload(self): '''Reload powerline after update. diff --git a/powerline/ipython.py b/powerline/ipython.py index 7d675058..5626713e 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -56,6 +56,5 @@ class IpythonPowerline(Powerline): mergedicts(r, self.theme_overrides[name]) return r - def setup(self, attr, obj): + def do_setup(self, attr, obj): setattr(obj, attr, self) - super(IpythonPowerline, self).setup(attr, obj) diff --git a/powerline/shell.py b/powerline/shell.py index cbd55fae..5920f897 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -43,9 +43,8 @@ class ShellPowerline(Powerline): for key, val in local_themes.items() )) - def setup(self, obj): + def do_setup(self, obj): obj.powerline = self - super(ShellPowerline, self).setup(obj) def get_argparser(parser=None, *args, **kwargs): diff --git a/powerline/vim.py b/powerline/vim.py index 3594a6c0..23a59900 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -110,8 +110,7 @@ class VimPowerline(Powerline): except KeyError: return super(VimPowerline, self).get_config_paths() - def setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True): - super(VimPowerline, self).setup() + def do_setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True): import __main__ if not pyeval: pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' From 015a2160ce728ffb4ed25cfa950a9477b9008f8d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 19:05:14 +0400 Subject: [PATCH 1285/1472] Import sys in reload() function It may be None sometimes --- powerline/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/__init__.py b/powerline/__init__.py index 1f57940e..c19b0424 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -774,6 +774,7 @@ class Powerline(object): may break your python code. ''' from imp import reload + import sys modules = self.imported_modules | set((module for module in sys.modules if module.startswith('powerline'))) modules_holder = [] for module in modules: From 23a3dc8f3cfcd6aa0b7489a361b65191496f9100 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 19:05:49 +0400 Subject: [PATCH 1286/1472] Implement powerline reloading in libzpython bindings --- powerline/bindings/zsh/__init__.py | 49 ++++++++++++++++++---------- powerline/bindings/zsh/powerline.zsh | 2 +- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 1f6ae40b..ad3fa90b 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,18 +1,22 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import, unicode_literals, division, print_function -import zsh import atexit + +import zsh + +from weakref import WeakValueDictionary, ref + from powerline.shell import ShellPowerline from powerline.lib import parsedotval from powerline.lib.unicode import unicode -used_powerlines = [] +used_powerlines = WeakValueDictionary() def shutdown(): - for powerline in used_powerlines: + for powerline in tuple(used_powerlines.values()): powerline.shutdown() @@ -94,17 +98,28 @@ environ = Environment() class ZshPowerline(ShellPowerline): + def init(self, **kwargs): + super(ZshPowerline, self).init(Args(), **kwargs) + def precmd(self): self.args.last_pipe_status = zsh.pipestatus() self.args.last_exit_code = zsh.last_exit_code() + def do_setup(self, zsh_globals): + set_prompt(self, 'PS1', 'left', None, above=True) + set_prompt(self, 'RPS1', 'right', None) + set_prompt(self, 'PS2', 'left', 'continuation') + set_prompt(self, 'RPS2', 'right', 'continuation') + set_prompt(self, 'PS3', 'left', 'select') + used_powerlines[id(self)] = self + zsh_globals['_powerline'] = self + class Prompt(object): - __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme', 'above') + __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme', 'above', '__weakref__') def __init__(self, powerline, side, theme, savedpsvar=None, savedps=None, above=False): self.powerline = powerline - powerline.setup(self) self.side = side self.above = above self.savedpsvar = savedpsvar @@ -143,9 +158,7 @@ class Prompt(object): def __del__(self): if self.savedps: zsh.setvalue(self.savedpsvar, self.savedps) - used_powerlines.remove(self.powerline) - if self.powerline not in used_powerlines: - self.powerline.shutdown() + self.powerline.shutdown() def set_prompt(powerline, psvar, side, theme, above=False): @@ -155,18 +168,18 @@ def set_prompt(powerline, psvar, side, theme, above=False): savedps = None zpyvar = 'ZPYTHON_POWERLINE_' + psvar prompt = Prompt(powerline, side, theme, psvar, savedps, above) + zsh.eval('unset ' + zpyvar) zsh.set_special_string(zpyvar, prompt) zsh.setvalue(psvar, '${' + zpyvar + '}') + return ref(prompt) -def setup(): - powerline = ZshPowerline(Args()) - used_powerlines.append(powerline) - used_powerlines.append(powerline) - set_prompt(powerline, 'PS1', 'left', None, above=True) - set_prompt(powerline, 'RPS1', 'right', None) - set_prompt(powerline, 'PS2', 'left', 'continuation') - set_prompt(powerline, 'RPS2', 'right', 'continuation') - set_prompt(powerline, 'PS3', 'left', 'select') +def reload(): + for powerline in tuple(used_powerlines.values()): + powerline.reload() + + +def setup(zsh_globals): + powerline = ZshPowerline() + powerline.setup(zsh_globals) atexit.register(shutdown) - return powerline diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 8a7078f1..113a306d 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -122,7 +122,7 @@ _powerline_setup_prompt() { if test -z "${POWERLINE_NO_ZSH_ZPYTHON}" && { zmodload libzpython || zmodload zsh/zpython } &>/dev/null ; then precmd_functions+=( _powerline_update_counter ) zpython 'from powerline.bindings.zsh import setup as _powerline_setup' - zpython '_powerline = _powerline_setup()' + zpython '_powerline_setup(globals())' zpython 'del _powerline_setup' else if test -z "${POWERLINE_COMMAND}" ; then From 50160e98897fb7e75d5f774b06fc7ddfda442938 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 21:57:23 +0400 Subject: [PATCH 1287/1472] Add powerline-reload function for libzpython bindings --- powerline/bindings/zsh/powerline.zsh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 113a306d..b75f138d 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -124,6 +124,11 @@ _powerline_setup_prompt() { zpython 'from powerline.bindings.zsh import setup as _powerline_setup' zpython '_powerline_setup(globals())' zpython 'del _powerline_setup' + powerline-reload() { + zpython 'from powerline.bindings.zsh import reload as _powerline_reload' + zpython '_powerline_reload()' + zpython 'del _powerline_reload' + } else if test -z "${POWERLINE_COMMAND}" ; then POWERLINE_COMMAND=( "$($POWERLINE_CONFIG shell command)" ) From b93f8d9b7bfbdd4247ae0a490363c8ce80073902 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 19:44:07 +0400 Subject: [PATCH 1288/1472] Rename Ipython to IPython (two capital letters in place of one) --- powerline/bindings/ipython/post_0_11.py | 14 +++++++------- powerline/bindings/ipython/pre_0_11.py | 14 +++++++------- powerline/ipython.py | 12 ++++++------ powerline/renderers/ipython/__init__.py | 6 +++--- powerline/renderers/ipython/prompt.py | 6 +++--- tests/test_provided_config_files.py | 4 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index e30d8797..f119ad63 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,11 +1,11 @@ # vim:fileencoding=utf-8:noet -from powerline.ipython import IpythonPowerline, RewriteResult +from powerline.ipython import IPythonPowerline, RewriteResult from IPython.core.prompts import PromptManager from IPython.core.hooks import TryNext -class IpythonInfo(object): +class IPythonInfo(object): def __init__(self, shell): self._shell = shell @@ -18,7 +18,7 @@ class PowerlinePromptManager(PromptManager): def __init__(self, prompt_powerline, non_prompt_powerline, shell): prompt_powerline.setup('prompt_powerline', self) non_prompt_powerline.setup('non_prompt_powerline', self) - self.powerline_segment_info = IpythonInfo(shell) + self.powerline_segment_info = IPythonInfo(shell) self.shell = shell def render(self, name, color=True, *args, **kwargs): @@ -42,13 +42,13 @@ class PowerlinePromptManager(PromptManager): return ret -class ConfigurableIpythonPowerline(IpythonPowerline): +class ConfigurableIPythonPowerline(IPythonPowerline): def init(self, ip, is_prompt, old_widths): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) self.paths = config.get('paths') - super(ConfigurableIpythonPowerline, self).init(is_prompt, old_widths) + super(ConfigurableIPythonPowerline, self).init(is_prompt, old_widths) old_prompt_manager = None @@ -59,8 +59,8 @@ def load_ipython_extension(ip): old_prompt_manager = ip.prompt_manager old_widths = {} - prompt_powerline = ConfigurableIpythonPowerline(ip, True, old_widths) - non_prompt_powerline = ConfigurableIpythonPowerline(ip, False, old_widths) + prompt_powerline = ConfigurableIPythonPowerline(ip, True, old_widths) + non_prompt_powerline = ConfigurableIPythonPowerline(ip, False, old_widths) ip.prompt_manager = PowerlinePromptManager( prompt_powerline=prompt_powerline, diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index e740c8f3..4f6f40bc 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from powerline.ipython import IpythonPowerline, RewriteResult +from powerline.ipython import IPythonPowerline, RewriteResult from powerline.lib.unicode import string from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython @@ -8,7 +8,7 @@ from IPython.ipapi import TryNext import re -class IpythonInfo(object): +class IPythonInfo(object): def __init__(self, cache): self._cache = cache @@ -22,7 +22,7 @@ class PowerlinePrompt(BasePrompt): powerline.setup('powerline', self) other_powerline.setup('other_powerline', self) self.powerline_last_in = powerline_last_in - self.powerline_segment_info = IpythonInfo(old_prompt.cache) + self.powerline_segment_info = IPythonInfo(old_prompt.cache) self.cache = old_prompt.cache if hasattr(old_prompt, 'sep'): self.sep = old_prompt.sep @@ -85,20 +85,20 @@ class PowerlinePrompt2(PowerlinePromptOut): powerline_prompt_type = 'in2' -class ConfigurableIpythonPowerline(IpythonPowerline): +class ConfigurableIPythonPowerline(IPythonPowerline): def init(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides self.paths = paths - super(ConfigurableIpythonPowerline, self).init(is_prompt, old_widths) + super(ConfigurableIPythonPowerline, self).init(is_prompt, old_widths) def setup(**kwargs): ip = get_ipython() old_widths = {} - prompt_powerline = ConfigurableIpythonPowerline(True, old_widths, **kwargs) - non_prompt_powerline = ConfigurableIpythonPowerline(False, old_widths, **kwargs) + prompt_powerline = ConfigurableIPythonPowerline(True, old_widths, **kwargs) + non_prompt_powerline = ConfigurableIPythonPowerline(False, old_widths, **kwargs) def late_startup_hook(): last_in = {'nrspaces': 0} diff --git a/powerline/ipython.py b/powerline/ipython.py index 5626713e..8ad7c604 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -22,9 +22,9 @@ class RewriteResult(object): return RewriteResult(self.prompt + s) -class IpythonPowerline(Powerline): +class IPythonPowerline(Powerline): def init(self, is_prompt, old_widths): - super(IpythonPowerline, self).init( + super(IPythonPowerline, self).init( 'ipython', renderer_module=('.prompt' if is_prompt else None), use_daemon_threads=True @@ -32,26 +32,26 @@ class IpythonPowerline(Powerline): self.old_widths = old_widths def create_renderer(self, *args, **kwargs): - super(IpythonPowerline, self).create_renderer(*args, **kwargs) + super(IPythonPowerline, self).create_renderer(*args, **kwargs) self.renderer.old_widths = self.old_widths def get_config_paths(self): if self.paths: return self.paths else: - return super(IpythonPowerline, self).get_config_paths() + return super(IPythonPowerline, self).get_config_paths() def get_local_themes(self, local_themes): return dict(((type, {'config': self.load_theme_config(name)}) for type, name in local_themes.items())) def load_main_config(self): - r = super(IpythonPowerline, self).load_main_config() + r = super(IPythonPowerline, self).load_main_config() if self.config_overrides: mergedicts(r, self.config_overrides) return r def load_theme_config(self, name): - r = super(IpythonPowerline, self).load_theme_config(name) + r = super(IPythonPowerline, self).load_theme_config(name) if name in self.theme_overrides: mergedicts(r, self.theme_overrides[name]) return r diff --git a/powerline/renderers/ipython/__init__.py b/powerline/renderers/ipython/__init__.py index 7d7e4c55..5361f8b8 100644 --- a/powerline/renderers/ipython/__init__.py +++ b/powerline/renderers/ipython/__init__.py @@ -4,7 +4,7 @@ from powerline.renderers.shell import ShellRenderer from powerline.theme import Theme -class IpythonRenderer(ShellRenderer): +class IPythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' def get_segment_info(self, segment_info, mode): r = self.segment_info.copy() @@ -33,8 +33,8 @@ class IpythonRenderer(ShellRenderer): match['theme'].shutdown() def render(self, *args, **kwargs): - # XXX super(ShellRenderer), *not* super(IpythonRenderer) + # XXX super(ShellRenderer), *not* super(IPythonRenderer) return super(ShellRenderer, self).render(*args, **kwargs) -renderer = IpythonRenderer +renderer = IPythonRenderer diff --git a/powerline/renderers/ipython/prompt.py b/powerline/renderers/ipython/prompt.py index a1103ec8..8516c889 100644 --- a/powerline/renderers/ipython/prompt.py +++ b/powerline/renderers/ipython/prompt.py @@ -1,12 +1,12 @@ # vim:fileencoding=utf-8:noet -from powerline.renderers.ipython import IpythonRenderer +from powerline.renderers.ipython import IPythonRenderer -class IpythonPromptRenderer(IpythonRenderer): +class IPythonPromptRenderer(IPythonRenderer): '''Powerline ipython prompt renderer''' escape_hl_start = '\x01' escape_hl_end = '\x02' -renderer = IpythonPromptRenderer +renderer = IPythonPromptRenderer diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index da160ab1..646b762a 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -109,9 +109,9 @@ class TestConfig(TestCase): powerline.render(segment_info={'args': args}) def test_ipython(self): - from powerline.ipython import IpythonPowerline + from powerline.ipython import IPythonPowerline - class IpyPowerline(IpythonPowerline): + class IpyPowerline(IPythonPowerline): paths = None config_overrides = None theme_overrides = {} From 06175dcd747d036bdb5188865672b6100e1c7bb5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 19:54:54 +0400 Subject: [PATCH 1289/1472] Refactor IPython bindings - Replaced two powerline objects with one powerline object utilizing RendererProxy proxy class that holds two Renderer instances. - Made .setup() functions do something more meaningful. --- powerline/bindings/ipython/post_0_11.py | 31 +++++++--------- powerline/bindings/ipython/pre_0_11.py | 49 ++++++++++++++++--------- powerline/ipython.py | 14 +++---- powerline/renderers/ipython/__init__.py | 36 +++++++++++++++++- powerline/renderers/ipython/prompt.py | 12 ------ powerline/renderers/shell/__init__.py | 6 +-- tests/test_provided_config_files.py | 4 +- 7 files changed, 90 insertions(+), 62 deletions(-) delete mode 100644 powerline/renderers/ipython/prompt.py diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index f119ad63..7148f641 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,4 +1,7 @@ # vim:fileencoding=utf-8:noet + +from weakref import ref + from powerline.ipython import IPythonPowerline, RewriteResult from IPython.core.prompts import PromptManager @@ -15,18 +18,13 @@ class IPythonInfo(object): class PowerlinePromptManager(PromptManager): - def __init__(self, prompt_powerline, non_prompt_powerline, shell): - prompt_powerline.setup('prompt_powerline', self) - non_prompt_powerline.setup('non_prompt_powerline', self) + def __init__(self, powerline, shell): self.powerline_segment_info = IPythonInfo(shell) self.shell = shell def render(self, name, color=True, *args, **kwargs): - if name == 'out' or name == 'rewrite': - powerline = self.non_prompt_powerline - else: - powerline = self.prompt_powerline - res = powerline.render( + res = self.powerline.render( + is_prompt=name.startswith('in'), side='left', output_width=True, output_raw=not color, @@ -43,12 +41,12 @@ class PowerlinePromptManager(PromptManager): class ConfigurableIPythonPowerline(IPythonPowerline): - def init(self, ip, is_prompt, old_widths): + def init(self, ip): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) self.paths = config.get('paths') - super(ConfigurableIPythonPowerline, self).init(is_prompt, old_widths) + super(ConfigurableIPythonPowerline, self).init() old_prompt_manager = None @@ -58,19 +56,16 @@ def load_ipython_extension(ip): global old_prompt_manager old_prompt_manager = ip.prompt_manager - old_widths = {} - prompt_powerline = ConfigurableIPythonPowerline(ip, True, old_widths) - non_prompt_powerline = ConfigurableIPythonPowerline(ip, False, old_widths) + powerline = ConfigurableIPythonPowerline(ip) ip.prompt_manager = PowerlinePromptManager( - prompt_powerline=prompt_powerline, - non_prompt_powerline=non_prompt_powerline, - shell=ip.prompt_manager.shell + powerline=powerline, + shell=ip.prompt_manager.shell, ) + powerline.setup(ref(ip.prompt_manager)) def shutdown_hook(): - prompt_powerline.shutdown() - non_prompt_powerline.shutdown() + powerline.shutdown() raise TryNext() ip.hooks.shutdown_hook.add(shutdown_hook) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index 4f6f40bc..da2c315d 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,12 +1,16 @@ # vim:fileencoding=utf-8:noet + +import re + +from weakref import ref + from powerline.ipython import IPythonPowerline, RewriteResult from powerline.lib.unicode import string + from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython from IPython.ipapi import TryNext -import re - class IPythonInfo(object): def __init__(self, cache): @@ -18,9 +22,7 @@ class IPythonInfo(object): class PowerlinePrompt(BasePrompt): - def __init__(self, powerline, other_powerline, powerline_last_in, old_prompt): - powerline.setup('powerline', self) - other_powerline.setup('other_powerline', self) + def __init__(self, powerline, powerline_last_in, old_prompt): self.powerline_last_in = powerline_last_in self.powerline_segment_info = IPythonInfo(old_prompt.cache) self.cache = old_prompt.cache @@ -35,6 +37,7 @@ class PowerlinePrompt(BasePrompt): def set_p_str(self): self.p_str, self.p_str_nocolor, self.powerline_prompt_width = ( self.powerline.render( + is_prompt=self.powerline_is_prompt, side='left', output_raw=True, output_width=True, @@ -50,6 +53,7 @@ class PowerlinePrompt(BasePrompt): class PowerlinePrompt1(PowerlinePrompt): powerline_prompt_type = 'in' + powerline_is_prompt = True rspace = re.compile(r'(\s*)$') def __str__(self): @@ -64,7 +68,8 @@ class PowerlinePrompt1(PowerlinePrompt): self.powerline_last_in['nrspaces'] = self.nrspaces def auto_rewrite(self): - return RewriteResult(self.other_powerline.render( + return RewriteResult(self.powerline.render( + is_prompt=False, side='left', matcher_info='rewrite', segment_info=self.powerline_segment_info) + (' ' * self.nrspaces) @@ -73,6 +78,7 @@ class PowerlinePrompt1(PowerlinePrompt): class PowerlinePromptOut(PowerlinePrompt): powerline_prompt_type = 'out' + powerline_is_prompt = False def set_p_str(self): super(PowerlinePromptOut, self).set_p_str() @@ -83,37 +89,46 @@ class PowerlinePromptOut(PowerlinePrompt): class PowerlinePrompt2(PowerlinePromptOut): powerline_prompt_type = 'in2' + powerline_is_prompt = True class ConfigurableIPythonPowerline(IPythonPowerline): - def init(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): + def init(self, config_overrides=None, theme_overrides={}, paths=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides self.paths = paths - super(ConfigurableIPythonPowerline, self).init(is_prompt, old_widths) + super(ConfigurableIPythonPowerline, self).init() + + def do_setup(self, wrefs): + for wref in wrefs: + obj = wref() + if obj is not None: + setattr(obj, 'powerline', self) def setup(**kwargs): ip = get_ipython() old_widths = {} - prompt_powerline = ConfigurableIPythonPowerline(True, old_widths, **kwargs) - non_prompt_powerline = ConfigurableIPythonPowerline(False, old_widths, **kwargs) + powerline = ConfigurableIPythonPowerline(**kwargs) def late_startup_hook(): last_in = {'nrspaces': 0} - for attr, prompt_class, powerline, other_powerline in ( - ('prompt1', PowerlinePrompt1, prompt_powerline, non_prompt_powerline), - ('prompt2', PowerlinePrompt2, prompt_powerline, None), - ('prompt_out', PowerlinePromptOut, non_prompt_powerline, None) + prompts = [] + for attr, prompt_class in ( + ('prompt1', PowerlinePrompt1), + ('prompt2', PowerlinePrompt2), + ('prompt_out', PowerlinePromptOut) ): old_prompt = getattr(ip.IP.outputcache, attr) - setattr(ip.IP.outputcache, attr, prompt_class(powerline, other_powerline, last_in, old_prompt)) + prompt = prompt_class(powerline, last_in, old_prompt) + setattr(ip.IP.outputcache, attr, prompt) + prompts.append(ref(prompt)) + powerline.setup(prompts) raise TryNext() def shutdown_hook(): - prompt_powerline.shutdown() - non_prompt_powerline.shutdown() + powerline.shutdown() raise TryNext() ip.IP.hooks.late_startup_hook.add(late_startup_hook) diff --git a/powerline/ipython.py b/powerline/ipython.py index 8ad7c604..7bb9d1e5 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -23,17 +23,11 @@ class RewriteResult(object): class IPythonPowerline(Powerline): - def init(self, is_prompt, old_widths): + def init(self): super(IPythonPowerline, self).init( 'ipython', - renderer_module=('.prompt' if is_prompt else None), use_daemon_threads=True ) - self.old_widths = old_widths - - def create_renderer(self, *args, **kwargs): - super(IPythonPowerline, self).create_renderer(*args, **kwargs) - self.renderer.old_widths = self.old_widths def get_config_paths(self): if self.paths: @@ -56,5 +50,7 @@ class IPythonPowerline(Powerline): mergedicts(r, self.theme_overrides[name]) return r - def do_setup(self, attr, obj): - setattr(obj, attr, self) + def do_setup(self, wref): + obj = wref() + if obj: + setattr(obj, 'powerline', self) diff --git a/powerline/renderers/ipython/__init__.py b/powerline/renderers/ipython/__init__.py index 5361f8b8..1fce961e 100644 --- a/powerline/renderers/ipython/__init__.py +++ b/powerline/renderers/ipython/__init__.py @@ -37,4 +37,38 @@ class IPythonRenderer(ShellRenderer): return super(ShellRenderer, self).render(*args, **kwargs) -renderer = IPythonRenderer +class IPythonPromptRenderer(IPythonRenderer): + '''Powerline ipython prompt (in and in2) renderer''' + escape_hl_start = '\x01' + escape_hl_end = '\x02' + + +class IPythonNonPromptRenderer(IPythonRenderer): + '''Powerline ipython non-prompt (out and rewrite) renderer''' + pass + + +class RendererProxy(object): + '''Powerline IPython renderer proxy which chooses appropriate renderer + + Instantiates two renderer objects: one will be used for prompts and the + other for non-prompts. + ''' + def __init__(self, **kwargs): + old_widths = {} + self.non_prompt_renderer = IPythonNonPromptRenderer(old_widths=old_widths, **kwargs) + self.prompt_renderer = IPythonPromptRenderer(old_widths=old_widths, **kwargs) + + def render_above_lines(self, *args, **kwargs): + return self.non_prompt_renderer.render_above_lines(*args, **kwargs) + + def render(self, is_prompt, *args, **kwargs): + return (self.prompt_renderer if is_prompt else self.non_prompt_renderer).render( + *args, **kwargs) + + def shutdown(self, *args, **kwargs): + self.prompt_renderer.shutdown(*args, **kwargs) + self.non_prompt_renderer.shutdown(*args, **kwargs) + + +renderer = RendererProxy diff --git a/powerline/renderers/ipython/prompt.py b/powerline/renderers/ipython/prompt.py deleted file mode 100644 index 8516c889..00000000 --- a/powerline/renderers/ipython/prompt.py +++ /dev/null @@ -1,12 +0,0 @@ -# vim:fileencoding=utf-8:noet - -from powerline.renderers.ipython import IPythonRenderer - - -class IPythonPromptRenderer(IPythonRenderer): - '''Powerline ipython prompt renderer''' - escape_hl_start = '\x01' - escape_hl_end = '\x02' - - -renderer = IPythonPromptRenderer diff --git a/powerline/renderers/shell/__init__.py b/powerline/renderers/shell/__init__.py index fdffaa25..70d9ac6a 100644 --- a/powerline/renderers/shell/__init__.py +++ b/powerline/renderers/shell/__init__.py @@ -24,9 +24,9 @@ class ShellRenderer(Renderer): character_translations = Renderer.character_translations.copy() - def __init__(self, *args, **kwargs): - super(ShellRenderer, self).__init__(*args, **kwargs) - self.old_widths = {} + def __init__(self, old_widths=None, **kwargs): + super(ShellRenderer, self).__init__(**kwargs) + self.old_widths = old_widths if old_widths is not None else {} def render(self, segment_info, **kwargs): local_theme = segment_info.get('local_theme') diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 646b762a..aeba0617 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -118,11 +118,11 @@ class TestConfig(TestCase): segment_info = Args(prompt_count=1) - with IpyPowerline(True, {}) as powerline: + with IpyPowerline() as powerline: for prompt_type in ['in', 'in2']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) - with IpyPowerline(False, {}) as powerline: + with IpyPowerline() as powerline: for prompt_type in ['out', 'rewrite']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) From 137fffc9f78827ef60d58cad0da190a8429dc017 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 21:29:32 +0400 Subject: [PATCH 1290/1472] Add `%powerline reload` IPython magic function --- powerline/bindings/ipython/post_0_11.py | 52 +++++++++++++++++++------ powerline/bindings/ipython/pre_0_11.py | 48 ++++++++++++++--------- powerline/ipython.py | 9 +++-- 3 files changed, 75 insertions(+), 34 deletions(-) diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 7148f641..5586fee4 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -5,7 +5,21 @@ from weakref import ref from powerline.ipython import IPythonPowerline, RewriteResult from IPython.core.prompts import PromptManager -from IPython.core.hooks import TryNext +from IPython.core.magic import Magics, magics_class, line_magic + + +@magics_class +class PowerlineMagics(Magics): + def __init__(self, ip, powerline): + super(PowerlineMagics, self).__init__(ip) + self._powerline = powerline + + @line_magic + def powerline(self, line): + if line == 'reload': + self._powerline.reload() + else: + raise ValueError('Expected `reload`, but got {0}'.format(line)) class IPythonInfo(object): @@ -19,6 +33,7 @@ class IPythonInfo(object): class PowerlinePromptManager(PromptManager): def __init__(self, powerline, shell): + self.powerline = powerline self.powerline_segment_info = IPythonInfo(shell) self.shell = shell @@ -40,6 +55,17 @@ class PowerlinePromptManager(PromptManager): return ret +class ShutdownHook(object): + powerline = lambda: None + + def __call__(self): + from IPython.core.hooks import TryNext + powerline = self.powerline() + if powerline is not None: + powerline.shutdown() + raise TryNext() + + class ConfigurableIPythonPowerline(IPythonPowerline): def init(self, ip): config = ip.config.Powerline @@ -48,25 +74,29 @@ class ConfigurableIPythonPowerline(IPythonPowerline): self.paths = config.get('paths') super(ConfigurableIPythonPowerline, self).init() + def do_setup(self, ip, shutdown_hook): + prompt_manager = PowerlinePromptManager( + powerline=self, + shell=ip.prompt_manager.shell, + ) + magics = PowerlineMagics(ip, self) + shutdown_hook.powerline = ref(self) + + ip.prompt_manager = prompt_manager + ip.register_magics(magics) + old_prompt_manager = None def load_ipython_extension(ip): global old_prompt_manager - old_prompt_manager = ip.prompt_manager + powerline = ConfigurableIPythonPowerline(ip) + shutdown_hook = ShutdownHook() - ip.prompt_manager = PowerlinePromptManager( - powerline=powerline, - shell=ip.prompt_manager.shell, - ) - powerline.setup(ref(ip.prompt_manager)) - - def shutdown_hook(): - powerline.shutdown() - raise TryNext() + powerline.setup(ip, shutdown_hook) ip.hooks.shutdown_hook.add(shutdown_hook) diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index da2c315d..d5389efe 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -23,6 +23,7 @@ class IPythonInfo(object): class PowerlinePrompt(BasePrompt): def __init__(self, powerline, powerline_last_in, old_prompt): + self.powerline = powerline self.powerline_last_in = powerline_last_in self.powerline_segment_info = IPythonInfo(old_prompt.cache) self.cache = old_prompt.cache @@ -99,36 +100,45 @@ class ConfigurableIPythonPowerline(IPythonPowerline): self.paths = paths super(ConfigurableIPythonPowerline, self).init() - def do_setup(self, wrefs): - for wref in wrefs: - obj = wref() - if obj is not None: - setattr(obj, 'powerline', self) + def ipython_magic(self, ip, parameter_s=''): + if parameter_s == 'reload': + self.reload() + else: + raise ValueError('Expected `reload`, but got {0}'.format(parameter_s)) - -def setup(**kwargs): - ip = get_ipython() - - old_widths = {} - powerline = ConfigurableIPythonPowerline(**kwargs) - - def late_startup_hook(): + def do_setup(self, ip, shutdown_hook): last_in = {'nrspaces': 0} - prompts = [] for attr, prompt_class in ( ('prompt1', PowerlinePrompt1), ('prompt2', PowerlinePrompt2), ('prompt_out', PowerlinePromptOut) ): old_prompt = getattr(ip.IP.outputcache, attr) - prompt = prompt_class(powerline, last_in, old_prompt) + prompt = prompt_class(self, last_in, old_prompt) setattr(ip.IP.outputcache, attr, prompt) - prompts.append(ref(prompt)) - powerline.setup(prompts) + ip.expose_magic('powerline', self.ipython_magic) + shutdown_hook.powerline = ref(self) + + +class ShutdownHook(object): + powerline = lambda: None + + def __call__(self): + from IPython.ipapi import TryNext + powerline = self.powerline() + if powerline is not None: + powerline.shutdown() raise TryNext() - def shutdown_hook(): - powerline.shutdown() + +def setup(**kwargs): + ip = get_ipython() + + powerline = ConfigurableIPythonPowerline(**kwargs) + shutdown_hook = ShutdownHook() + + def late_startup_hook(): + powerline.setup(ip, shutdown_hook) raise TryNext() ip.IP.hooks.late_startup_hook.add(late_startup_hook) diff --git a/powerline/ipython.py b/powerline/ipython.py index 7bb9d1e5..f658f111 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -50,7 +50,8 @@ class IPythonPowerline(Powerline): mergedicts(r, self.theme_overrides[name]) return r - def do_setup(self, wref): - obj = wref() - if obj: - setattr(obj, 'powerline', self) + def do_setup(self, wrefs): + for wref in wrefs: + obj = wref() + if obj is not None: + setattr(obj, 'powerline', self) From 28bcb0134516f6557e52ba26bee59342ed516641 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 19 Aug 2014 22:12:33 +0400 Subject: [PATCH 1291/1472] Update documentation --- docs/source/develop/segments.rst | 2 ++ docs/source/tips-and-tricks.rst | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 63ea35db..1715d3a7 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -53,6 +53,8 @@ Detailed description of used dictionary keys: Text displayed by segment. Should be a ``unicode`` (Python2) or ``str`` (Python3) instance. +.. _dev-segments-draw_inner_divider: + ``draw_hard_divider``, ``draw_soft_divider``, ``draw_inner_divider`` Determines whether given divider should be drawn. All have the same meaning as :ref:`the similar keys in configuration ` diff --git a/docs/source/tips-and-tricks.rst b/docs/source/tips-and-tricks.rst index b2216b12..4be6c764 100644 --- a/docs/source/tips-and-tricks.rst +++ b/docs/source/tips-and-tricks.rst @@ -49,3 +49,46 @@ of the fonts play a role in whether or not the > or the < separators showing up or not. Using font size 12, glyphs on the right hand side of the powerline are present, but the ones on the left don't. Pixel size 14, brings the reverse problem. Font size 13 seems to work just fine. + +Reloading powerline after update +================================ + +Once you have updated powerline you generally have the following options: + +#. Restart the application you are using it in. This is the safest one. Will not + work if the application uses ``powerline-daemon``. +#. For shell and tmux bindings (except for zsh with libzpython): do not do + anything if you do not use ``powerline-daemon``, run ``powerline-daemon + --replace`` if you do. +#. Use powerline reloading feature. + + .. warning:: + This feature is an unsafe one. It is not guaranteed to work always, it may + render your Python constantly error out in place of displaying powerline + and sometimes may render your application useless, forcing you to + restart. + + *Do not report any bugs occurred when using this feature unless you know + both what caused it and how this can be fixed.* + + * When using zsh with libzpython use + + .. code-block:: bash + + powerline-reload + + .. note:: This shell function is only defined when using libzpython. + + * When using IPython use + + :: + + %powerline reload + + * When using Vim use + + .. code-block:: Vim + + py powerline.reload() + " or (depending on Python version you are using) + py3 powerline.reload() From 48aac90681bbdda7562c9a977c392c79667e074e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Aug 2014 21:12:10 +0400 Subject: [PATCH 1292/1472] Fix ipython tests --- tests/test_provided_config_files.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index aeba0617..3bd41d94 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -120,12 +120,12 @@ class TestConfig(TestCase): with IpyPowerline() as powerline: for prompt_type in ['in', 'in2']: - powerline.render(matcher_info=prompt_type, segment_info=segment_info) - powerline.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.render(is_prompt=True, matcher_info=prompt_type, segment_info=segment_info) + powerline.render(is_prompt=True, matcher_info=prompt_type, segment_info=segment_info) with IpyPowerline() as powerline: for prompt_type in ['out', 'rewrite']: - powerline.render(matcher_info=prompt_type, segment_info=segment_info) - powerline.render(matcher_info=prompt_type, segment_info=segment_info) + powerline.render(is_prompt=False, matcher_info=prompt_type, segment_info=segment_info) + powerline.render(is_prompt=False, matcher_info=prompt_type, segment_info=segment_info) def test_wm(self): from powerline.segments import common From 8262a0ec4417cd47d8cc5619ff1abbe614668b28 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 20 Aug 2014 20:34:23 +0400 Subject: [PATCH 1293/1472] Do not use some features in old Vims as they cause a crash Fixes #997 Closes #999 --- powerline/bindings/vim/__init__.py | 50 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index f3f9783a..e6edb92a 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -41,7 +41,12 @@ else: vim_get_func = VimFunc -if hasattr(vim, 'bindeval'): +_getbufvar = vim_get_func('getbufvar') + + +# It may crash on some old vim versions and I do not remember in which patch +# I fixed this crash. +if hasattr(vim, 'vvars') and vim.vvars['version'] > 703: _vim_to_python_types = { getattr(vim, 'Dictionary', None) or type(vim.bindeval('{}')): lambda value: dict(((key, _vim_to_python(value[key])) for key in value.keys())), @@ -51,18 +56,6 @@ if hasattr(vim, 'bindeval'): lambda _: None, } - if sys.version_info >= (3,): - _vim_to_python_types[bytes] = lambda value: value.decode('utf-8') - - _id = lambda value: value - - def _vim_to_python(value): - return _vim_to_python_types.get(type(value), _id)(value) - - -# It may crash on some old vim versions and I do not remember in which patch -# I fixed this crash. -if hasattr(vim, 'vvars') and vim.vvars['version'] > 703: def vim_getvar(varname): return _vim_to_python(vim.vars[str(varname)]) @@ -73,6 +66,11 @@ if hasattr(vim, 'vvars') and vim.vvars['version'] > 703: def vim_getwinvar(segment_info, varname): return _vim_to_python(segment_info['window'].vars[str(varname)]) else: + _vim_to_python_types = { + dict: (lambda value: dict(((k, _vim_to_python(v)) for k, v in value.items()))), + list: (lambda value: [_vim_to_python(i) for i in value]), + } + _vim_exists = vim_get_func('exists', rettype=int) def vim_getvar(varname): # NOQA @@ -97,6 +95,23 @@ else: raise KeyError(varname) return result + +if sys.version_info < (3,): + getbufvar = _getbufvar +else: + _vim_to_python_types[bytes] = lambda value: value.decode('utf-8') + + def getbufvar(*args): + return _vim_to_python(_getbufvar(*args)) + + +_id = lambda value: value + + +def _vim_to_python(value): + return _vim_to_python_types.get(type(value), _id)(value) + + if hasattr(vim, 'options'): def vim_getbufoption(info, option): return info['buffer'].options[str(option)] @@ -191,15 +206,6 @@ else: return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)] -if sys.version_info < (3,) or not hasattr(vim, 'bindeval'): - getbufvar = vim_get_func('getbufvar') -else: - _getbufvar = vim_get_func('getbufvar') - - def getbufvar(*args): - return _vim_to_python(_getbufvar(*args)) - - class VimEnviron(object): @staticmethod def __getitem__(key): From 6b51d122e2bc103d5e1f0a51471b7c768ceff260 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Aug 2014 23:10:54 +0400 Subject: [PATCH 1294/1472] Fix typo in renderer implementation Fixes #1003 --- powerline/renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 204a328d..a2a9d32d 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -257,9 +257,9 @@ class Renderer(object): segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode segments = [ - self._get_highlighting(segment, segment['mode'] or mode) + self._get_highlighting(segment, segment_mode) for segment, segment_mode in ( - (segment, segment['mode']) + (segment, segment['mode'] or mode) for segment in segments ) if ( segment_mode not in segment['exclude_modes'] From 88a0b9075c938782c2edff54455fdfcccf7c87e5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Aug 2014 23:17:15 +0400 Subject: [PATCH 1295/1472] Remove unused vim import --- tests/test_configuration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index ec05dab4..765ad01c 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -406,7 +406,6 @@ class TestVim(TestCase): # Regression test: VimPowerline.add_local_theme did not work properly. from powerline.vim import VimPowerline import powerline as powerline_module - import vim with swap_attributes(config, powerline_module): with get_powerline_raw(config, VimPowerline) as powerline: powerline.add_local_theme('tests.matchers.always_true', { From 95bdca87ec6dfab5070a68909e31b0a1b33ebfbf Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Aug 2014 23:24:30 +0400 Subject: [PATCH 1296/1472] Fix syntastic warnings in powerline/__init__ --- powerline/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index c19b0424..0b99d85c 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -349,7 +349,7 @@ class Powerline(object): shutdown_event=None, config_loader=None): '''Do actual initialization. - + __init__ function only stores the arguments and runs this function. This function exists for powerline to be able to reload itself: it is easier to make ``__init__`` store arguments and call overriddable ``init`` than @@ -773,7 +773,6 @@ class Powerline(object): Not guaranteed to work properly, use it at your own risk. It may break your python code. ''' - from imp import reload import sys modules = self.imported_modules | set((module for module in sys.modules if module.startswith('powerline'))) modules_holder = [] From 075eb8420e1a35f6ecd0b48a08288c61e68e4a4c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 21 Aug 2014 23:31:48 +0400 Subject: [PATCH 1297/1472] Add tests --- tests/test_configuration.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 765ad01c..39bb9d20 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -384,6 +384,50 @@ class TestThemeHierarchy(TestRender): ]) +class TestModes(TestRender): + @add_args + def test_include_modes(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s1', 'g1', include_modes=['m1']), + highlighted_string('s2', 'g1', include_modes=['m1', 'm2']), + highlighted_string('s3', 'g1', include_modes=['m3']), + ] + } + self.assertRenderEqual(p, '{--}') + self.assertRenderEqual(p, '{56} s1{56}>{56}s2{6-}>>{--}', mode='m1') + self.assertRenderEqual(p, '{56} s2{6-}>>{--}', mode='m2') + self.assertRenderEqual(p, '{56} s3{6-}>>{--}', mode='m3') + + @add_args + def test_exclude_modes(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s1', 'g1', exclude_modes=['m1']), + highlighted_string('s2', 'g1', exclude_modes=['m1', 'm2']), + highlighted_string('s3', 'g1', exclude_modes=['m3']), + ] + } + self.assertRenderEqual(p, '{56} s1{56}>{56}s2{56}>{56}s3{6-}>>{--}') + self.assertRenderEqual(p, '{56} s3{6-}>>{--}', mode='m1') + self.assertRenderEqual(p, '{56} s1{56}>{56}s3{6-}>>{--}', mode='m2') + self.assertRenderEqual(p, '{56} s1{56}>{56}s2{6-}>>{--}', mode='m3') + + @add_args + def test_exinclude_modes(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s1', 'g1', exclude_modes=['m1'], include_modes=['m2']), + highlighted_string('s2', 'g1', exclude_modes=['m1', 'm2'], include_modes=['m3']), + highlighted_string('s3', 'g1', exclude_modes=['m3'], include_modes=['m3']), + ] + } + self.assertRenderEqual(p, '{--}') + self.assertRenderEqual(p, '{--}', mode='m1') + self.assertRenderEqual(p, '{56} s1{6-}>>{--}', mode='m2') + self.assertRenderEqual(p, '{56} s2{6-}>>{--}', mode='m3') + + class TestVim(TestCase): def test_environ_update(self): # Regression test: test that segment obtains environment from vim, not From a50b48804271d2ed23821f7f0441d80f04eb9f70 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 00:30:29 +0400 Subject: [PATCH 1298/1472] Fix incorrect use of partial Fixes #1002 --- powerline/segments/common.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index da07c7a0..ecfd4e04 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -9,7 +9,6 @@ import socket from datetime import datetime from multiprocessing import cpu_count as _cpu_count -from functools import partial from powerline.lib import add_divider_highlight_group from powerline.lib.shell import asrun, run_cmd @@ -1142,11 +1141,12 @@ def _get_battery(pl): pl.debug('Not using DBUS+UPower with {0}: not a power supply', devpath) continue pl.debug('Using DBUS+UPower with {0}', devpath) - return lambda pl: float(partial( - dbus.Interface(dev, dbus_interface=devinterface).Get, - devtype_name, - 'Percentage' - )) + return lambda pl: float( + dbus.Interface(dev, dbus_interface=devinterface).Get( + devtype_name, + 'Percentage' + ) + ) pl.debug('Not using DBUS+UPower as no batteries were found') if os.path.isdir('/sys/class/power_supply'): From abaac3f0a0bb8a0c6ae325aba3b7f71539286927 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 00:32:17 +0400 Subject: [PATCH 1299/1472] Fix indentation error reported by syntastic --- powerline/segments/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ecfd4e04..6d86818c 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -703,7 +703,7 @@ def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_form hours_format.format(hours=hours) if hours and hours_format else None, minutes_format.format(minutes=minutes) if minutes and minutes_format else None, seconds_format.format(seconds=seconds) if seconds and seconds_format else None, - ]))[0:shorten_len] + ]))[0:shorten_len] return ''.join(time_formatted).strip() From 849a8664784e24071b7ccf4047313d46f7e2d11f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 00:46:12 +0400 Subject: [PATCH 1300/1472] Fix Vim extension reloading when using .add_local_theme() --- powerline/__init__.py | 9 +++++---- powerline/vim.py | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 0b99d85c..36210c12 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -394,7 +394,8 @@ class Powerline(object): self.prev_common_config = None self.prev_ext_config = None self.pl = None - self.setup_args = None + self.setup_args = () + self.setup_kwargs = {} self.imported_modules = set() def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): @@ -748,7 +749,8 @@ class Powerline(object): arguments for :py:meth:`reload` method and calls :py:meth:`do_setup`. ''' self.shutdown_event.clear() - self.setup_args = (args, kwargs) + self.setup_args = args + self.setup_kwargs.update(kwargs) self.do_setup(*args, **kwargs) @staticmethod @@ -787,8 +789,7 @@ class Powerline(object): self.shutdown(set_event=True) init_args, init_kwargs = self.init_args powerline = PowerlineClass(*init_args, **init_kwargs) - setup_args, setup_kwargs = self.setup_args - powerline.setup(*setup_args, **setup_kwargs) + powerline.setup(*self.setup_args, **self.setup_kwargs) def shutdown(self, set_event=True): '''Shut down all background threads. diff --git a/powerline/vim.py b/powerline/vim.py index 23a59900..0a5fc9f9 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -49,7 +49,7 @@ class VimPowerline(Powerline): the same matcher already exists. ''' self.update_renderer() - key = self.get_matcher(key) + matcher = self.get_matcher(key) theme_config = {} for cfg_path in self.theme_levels: try: @@ -60,10 +60,15 @@ class VimPowerline(Powerline): mergedicts(theme_config, lvl_config) mergedicts(theme_config, config) try: - self.renderer.add_local_theme(key, {'config': theme_config}) + self.renderer.add_local_theme(matcher, {'config': theme_config}) except KeyError: return False else: + # Hack for local themes support: when reloading modules it is not + # guaranteed that .add_local_theme will be called once again, so + # this function arguments will be saved here for calling from + # .do_setup(). + self.setup_kwargs.setdefault('_local_themes', []).append((key, config)) return True def load_main_config(self): @@ -110,7 +115,7 @@ class VimPowerline(Powerline): except KeyError: return super(VimPowerline, self).get_config_paths() - def do_setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True): + def do_setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True, _local_themes=()): import __main__ if not pyeval: pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' @@ -162,6 +167,10 @@ class VimPowerline(Powerline): vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) vim.command('augroup END') + # Hack for local themes support after reloading. + for args in _local_themes: + self.add_local_theme(*args) + @staticmethod def get_segment_info(): return {} From 96e297cc6efd65f9b7a02d2b3b27d73ef9f44ce5 Mon Sep 17 00:00:00 2001 From: Jan Losinski Date: Mon, 18 Aug 2014 17:33:16 +0200 Subject: [PATCH 1301/1472] Add tab_modified_indicator to the vim segments This indicates in the tabline that any of the open buffers in a tab page has unsaved modifications (like the default tabline do). Signed-off-by: Jan Losinski --- .../config_files/themes/vim/tabline.json | 5 +++++ powerline/segments/vim/__init__.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 42aed081..79b145fa 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -30,6 +30,11 @@ }, "priority": 10 }, + { + "name": "tab_modified_indicator", + "exclude_modes": ["buf", "buf_nc"], + "priority": 5 + }, { "name": "modified_indicator", "exclude_modes": ["tab", "tab_nc"], diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 3e991e4f..529a4328 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -167,6 +167,25 @@ def modified_indicator(pl, segment_info, text='+'): return text if int(vim_getbufoption(segment_info, 'modified')) else None +@requires_segment_info +def tab_modified_indicator(pl, segment_info, text='+'): + '''Return a file modified indicator for tabpages. + + :param string text: + text to display if any buffer in the current tab is modified + ''' + if 'tabpage' in segment_info: + buffers = [dict(buffer=w.buffer) for w in segment_info['tabpage'].windows] + modified = [int(vim_getbufoption(buf, 'modified')) != 0 for buf in buffers] + ret = text if reduce(lambda x, y: x or y, modified) else None + if ret: + return [{ + 'contents': ret, + 'highlight_group': ['modified_indicator'], + }] + return None + + @requires_segment_info def paste_indicator(pl, segment_info, text='PASTE'): '''Return a paste mode indicator. From 80c4856bcbb02a1827f7f3ce31241c14c4a8a9ba Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 08:38:38 +0400 Subject: [PATCH 1302/1472] Remove useless TODO --- powerline/segments/vim/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 529a4328..fb1c1ed5 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -446,7 +446,6 @@ def col_current(pl, segment_info): return str(segment_info['window'].cursor[1] + 1) -# TODO Add &textwidth-based gradient @window_cached def virtcol_current(pl, gradient=True): '''Return current visual column with concealed characters ingored From 58b7e6c800457b59abc34313c9b37998ceff1f67 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 08:38:56 +0400 Subject: [PATCH 1303/1472] Use spaces for alignment --- powerline/segments/vim/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index fb1c1ed5..c21a3168 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -13,9 +13,9 @@ except ImportError: from collections import defaultdict from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, - buffer_name, vim_getwinvar, - register_buffer_cache, current_tabpage, - list_tabpages) + buffer_name, vim_getwinvar, + register_buffer_cache, current_tabpage, + list_tabpages) from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status From 9cab9d6fff1dfd824411f1902dc594fc238e7055 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 08:40:11 +0400 Subject: [PATCH 1304/1472] Use tab_modified_indicator highlight group --- powerline/segments/vim/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index c21a3168..bfb7b475 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -173,6 +173,8 @@ def tab_modified_indicator(pl, segment_info, text='+'): :param string text: text to display if any buffer in the current tab is modified + + Highlight groups used: ``tab_modified_indicator`` or ``modified_indicator``. ''' if 'tabpage' in segment_info: buffers = [dict(buffer=w.buffer) for w in segment_info['tabpage'].windows] @@ -181,7 +183,7 @@ def tab_modified_indicator(pl, segment_info, text='+'): if ret: return [{ 'contents': ret, - 'highlight_group': ['modified_indicator'], + 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], }] return None From 5c4d75f9526424d68a107d76ad350c4bb95193bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 08:59:25 +0400 Subject: [PATCH 1305/1472] Make tab_modified_indicator work in old Vims Closes #996 --- powerline/bindings/vim/__init__.py | 24 ++++++++++++++++++++++++ powerline/segments/vim/__init__.py | 12 +++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index e6edb92a..eec4da3c 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -136,6 +136,12 @@ else: if hasattr(vim, 'tabpages'): current_tabpage = lambda: vim.current.tabpage list_tabpages = lambda: vim.tabpages + + def list_tabpage_buffers_segment_info(segment_info): + return ( + {'buffer': window.buffer, 'bufnr': window.buffer.number} + for window in segment_info['tabpage'].windows + ) else: class FalseObject(object): @staticmethod @@ -205,6 +211,24 @@ else: def list_tabpages(): # NOQA return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)] + class TabBufSegmentInfo(dict): + def __getitem__(self, key): + try: + return super(TabBufSegmentInfo, self).__getitem__(key) + except KeyError: + if key != 'buffer': + raise + else: + buffer = get_buffer(super(TabBufSegmentInfo, self).__getitem__('bufnr')) + self['buffer'] = buffer + return buffer + + def list_tabpage_buffers_segment_info(segment_info): + return ( + TabBufSegmentInfo(bufnr=int(bufnrstr)) + for bufnrstr in vim.eval('tabpagebuflist({0})'.format(segment_info['tabnr'])) + ) + class VimEnviron(object): @staticmethod diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index bfb7b475..65118b2f 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -15,7 +15,8 @@ from collections import defaultdict from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar, register_buffer_cache, current_tabpage, - list_tabpages) + list_tabpages, + list_tabpage_buffers_segment_info) from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -176,13 +177,10 @@ def tab_modified_indicator(pl, segment_info, text='+'): Highlight groups used: ``tab_modified_indicator`` or ``modified_indicator``. ''' - if 'tabpage' in segment_info: - buffers = [dict(buffer=w.buffer) for w in segment_info['tabpage'].windows] - modified = [int(vim_getbufoption(buf, 'modified')) != 0 for buf in buffers] - ret = text if reduce(lambda x, y: x or y, modified) else None - if ret: + for buf_segment_info in list_tabpage_buffers_segment_info(segment_info): + if int(vim_getbufoption(buf_segment_info, 'modified')): return [{ - 'contents': ret, + 'contents': text, 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], }] return None From da48e9d84e97de35cbc13de6582ba8fdef14582b Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 09:08:58 +0400 Subject: [PATCH 1306/1472] Add tests --- tests/test_segments.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_segments.py b/tests/test_segments.py index 60d05574..2acccb46 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -903,6 +903,29 @@ class TestVim(TestCase): self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}]) self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['single_tab'], 'contents': 's'}]) + def test_segment_info(self): + pl = Pl() + with vim_module._with('tabpage'): + with vim_module._with('buffer', '1') as segment_info: + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + vim_module.current.buffer[0] = ' ' + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ + 'contents': '+', + 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], + }]) + vim_module._undo() + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + old_buffer = vim_module.current.buffer + vim_module._new('2') + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + old_buffer[0] = ' ' + self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ + 'contents': '+', + 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], + }]) + old_cwd = None From 6481f1b65e270a3d5be8e4d6e37805eeacddf76f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 20:39:31 +0400 Subject: [PATCH 1307/1472] Always specify all keys in segment_info --- powerline/renderers/vim.py | 43 ++++++++++++++--------------- powerline/vim.py | 9 +++--- tests/test_provided_config_files.py | 2 +- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 50586194..28cf0ee4 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -84,36 +84,33 @@ class VimRenderer(Renderer): def get_segment_info(self, segment_info, mode): return segment_info or self.segment_info - def render(self, window=None, window_id=None, winnr=None): + def render(self, window=None, window_id=None, winnr=None, is_tabline=False): '''Render all segments.''' segment_info = self.segment_info.copy() - if window is not None: - if window is vim.current.window: - mode = vim_mode(1) - mode = mode_translations.get(mode, mode) - else: - mode = 'nc' - segment_info.update( - window=window, - mode=mode, - window_id=window_id, - winnr=winnr, - buffer=window.buffer, - tabpage=current_tabpage(), - ) - segment_info['tabnr'] = segment_info['tabpage'].number - segment_info['bufnr'] = segment_info['buffer'].number - winwidth = segment_info['window'].width - matcher_info = segment_info + + if window is vim.current.window: + mode = vim_mode(1) + mode = mode_translations.get(mode, mode) else: - mode = None - winwidth = int(vim.eval('&columns')) - matcher_info = None + mode = 'nc' + + segment_info.update( + window=window, + mode=mode, + window_id=window_id, + winnr=winnr, + buffer=window.buffer, + tabpage=current_tabpage(), + ) + segment_info['tabnr'] = segment_info['tabpage'].number + segment_info['bufnr'] = segment_info['buffer'].number + winwidth = segment_info['window'].width + statusline = super(VimRenderer, self).render( mode=mode, width=winwidth, segment_info=segment_info, - matcher_info=matcher_info, + matcher_info=(None if is_tabline else segment_info), ) return statusline diff --git a/powerline/vim.py b/powerline/vim.py index 0a5fc9f9..ecea537b 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import sys from powerline.bindings.vim import vim_get_func, vim_getvar -from powerline import Powerline +from powerline import Powerline, FailedUnicode from powerline.lib import mergedicts import vim from itertools import count @@ -228,15 +228,14 @@ class VimPowerline(Powerline): def statusline(self, window_id): window, window_id, winnr = self.win_idx(window_id) or (None, None, None) if not window: - return 'No window {0}'.format(window_id) + return FailedUnicode('No window {0}'.format(window_id)) return self.render(window, window_id, winnr) def tabline(self): - return self.render() + return self.render(*self.win_idx(None), is_tabline=True) def new_window(self): - window, window_id, winnr = self.win_idx(None) - return self.render(window, window_id, winnr) + return self.render(*self.win_idx(None)) if not hasattr(vim, 'bindeval'): # Method for PowerlinePyeval function. Is here to reduce the number of diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 3bd41d94..27698cde 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -53,7 +53,7 @@ class TestConfig(TestCase): outputs[out] = (i, (args, kwargs), mode) with vim_module._with('bufname', '/tmp/foo.txt'): - out = powerline.render() + out = powerline.render(vim_module.current.window, 1, vim_module.current.window.number, is_tabline=True) outputs[out] = (-1, (None, None), 'tab') with vim_module._with('globals', powerline_config_path=cfg_path): exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) From 974ec17bae8d977565ec783883919c5d53e8c890 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 21:03:27 +0400 Subject: [PATCH 1308/1472] Filter segments using mode in Theme.get_segments --- powerline/renderer.py | 28 +++++++++++++--------------- powerline/theme.py | 21 ++++++++++++++++++--- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index a2a9d32d..4ac0a9db 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -147,10 +147,17 @@ class Renderer(object): ''' self.theme.shutdown() - def _get_highlighting(self, segment, mode): - segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) + def _set_highlighting(self, segment): + segment['highlight'] = self.colorscheme.get_highlighting( + segment['highlight_group'], + segment['mode'], + segment.get('gradient_level') + ) if segment['divider_highlight_group']: - segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode) + segment['divider_highlight'] = self.colorscheme.get_highlighting( + segment['divider_highlight_group'], + segment['mode'] + ) else: segment['divider_highlight'] = None return segment @@ -254,19 +261,10 @@ class Renderer(object): def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme): '''Like Renderer.render(), but accept theme in place of matcher_info ''' - segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) - # Handle excluded/included segments for the current mode segments = [ - self._get_highlighting(segment, segment_mode) - for segment, segment_mode in ( - (segment, segment['mode'] or mode) - for segment in segments - ) if ( - segment_mode not in segment['exclude_modes'] - and ( - not segment['include_modes'] - or segment_mode in segment['include_modes'] - ) + self._set_highlighting(segment) + for segment in ( + theme.get_segments(side, line, self.get_segment_info(segment_info, mode), mode) ) ] diff --git a/powerline/theme.py b/powerline/theme.py index d2617dc5..efe5e5a4 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -90,7 +90,7 @@ class Theme(object): def get_line_number(self): return len(self.segments) - def get_segments(self, side=None, line=0, segment_info=None): + def get_segments(self, side=None, line=0, segment_info=None, mode=None): '''Return all segments. Function segments are called, and all segments get their before/after @@ -103,8 +103,21 @@ class Theme(object): for side in [side] if side else ['left', 'right']: parsed_segments = [] for segment in self.segments[line][side]: - process_segment(self.pl, side, segment_info, parsed_segments, segment) + # No segment-local modes at this point + if mode not in segment['exclude_modes'] and ( + not segment['include_modes'] or mode in segment['include_modes'] + ): + process_segment(self.pl, side, segment_info, parsed_segments, segment) for segment in parsed_segments: + segment_mode = segment['mode'] + if segment_mode and ( + segment_mode in segment['exclude_modes'] + or ( + segment['include_modes'] + and segment_mode not in segment['include_modes'] + ) + ): + continue segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents if segment['width'] and segment['width'] != 'auto': @@ -117,4 +130,6 @@ class Theme(object): # We need to yield a copy of the segment, or else mode-dependent # segment contents can't be cached correctly e.g. when caching # non-current window contents for vim statuslines - yield segment.copy() + segment = segment.copy() + segment['mode'] = segment_mode or mode + yield segment From 5a45b7d291a12569572770b3e7a1f29636563f06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 21:34:37 +0400 Subject: [PATCH 1309/1472] Move processing segment['mode'] to segment.py --- powerline/segment.py | 32 +++++++++++++++++++++++--------- powerline/theme.py | 15 ++------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index b2a7086c..d75be664 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,8 +1,6 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import, unicode_literals, division, print_function -import sys - from powerline.lib.watcher import create_file_watcher @@ -90,7 +88,7 @@ def get_attr_func(contents_func, key, args): return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) -def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subsegments, patcher_args): +def process_segment_lister(pl, segment_info, parsed_segments, side, mode, lister, subsegments, patcher_args): for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) old_pslen = len(parsed_segments) @@ -100,7 +98,18 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subs subsegment.update(subsegment_update) if subsegment_update['priority_multiplier'] and subsegment['priority']: subsegment['priority'] *= subsegment_update['priority_multiplier'] - process_segment(pl, side, subsegment_info, parsed_segments, subsegment) + + subsegment_mode = subsegment_update.get('mode') + if subsegment_mode and ( + subsegment_mode in subsegment['exclude_modes'] + or ( + subsegment['include_modes'] + and subsegment_mode not in subsegment['include_modes'] + ) + ): + continue + + process_segment(pl, side, subsegment_info, parsed_segments, subsegment, subsegment_mode or mode) new_pslen = len(parsed_segments) if new_pslen > old_pslen + 1 and draw_inner_divider is not None: for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): @@ -108,22 +117,27 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subs return None -def process_segment(pl, side, segment_info, parsed_segments, segment): +def process_segment(pl, side, segment_info, parsed_segments, segment, mode): + segment = segment.copy() + segment['mode'] = mode if segment['type'] in ('function', 'segment_list'): pl.prefix = segment['name'] try: if segment['type'] == 'function': contents = segment['contents_func'](pl, segment_info) else: - contents = segment['contents_func'](pl, segment_info, parsed_segments, side) + contents = segment['contents_func'](pl, segment_info, parsed_segments, side, mode) except Exception as e: pl.exception('Exception while computing segment: {0}', str(e)) return if contents is None: return + if isinstance(contents, list): - segment_base = segment.copy() + # Needs copying here, but it was performed at the very start of the + # function + segment_base = segment if contents: draw_divider_position = -1 if side == 'left' else 0 for key, i, newval in ( @@ -206,8 +220,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'divider_highlight_group': None, 'before': None, 'after': None, - 'contents_func': lambda pl, segment_info, parsed_segments, side: process_segment_lister( - pl, segment_info, parsed_segments, side, + 'contents_func': lambda pl, segment_info, parsed_segments, side, mode: process_segment_lister( + pl, segment_info, parsed_segments, side, mode, patcher_args=args, subsegments=subsegments, lister=_contents_func, diff --git a/powerline/theme.py b/powerline/theme.py index efe5e5a4..fe494457 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -107,17 +107,8 @@ class Theme(object): if mode not in segment['exclude_modes'] and ( not segment['include_modes'] or mode in segment['include_modes'] ): - process_segment(self.pl, side, segment_info, parsed_segments, segment) + process_segment(self.pl, side, segment_info, parsed_segments, segment, mode) for segment in parsed_segments: - segment_mode = segment['mode'] - if segment_mode and ( - segment_mode in segment['exclude_modes'] - or ( - segment['include_modes'] - and segment_mode not in segment['include_modes'] - ) - ): - continue segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents if segment['width'] and segment['width'] != 'auto': @@ -130,6 +121,4 @@ class Theme(object): # We need to yield a copy of the segment, or else mode-dependent # segment contents can't be cached correctly e.g. when caching # non-current window contents for vim statuslines - segment = segment.copy() - segment['mode'] = segment_mode or mode - yield segment + yield segment.copy() From 74d147a0bef5fd3b90307269c295b54ebdcbd861 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 03:55:37 +0400 Subject: [PATCH 1310/1472] Use `open` to open file descriptors in place of `file` Reason: there is no `file()` in Python 3. Fix was originally presented by @kovidgoyal in [3deb69][1], but I cannot cherry-pick this commit, because its commit message is highly undescriptive. Fixes #1008 [1]: https://github.com/kovidgoyal/powerline-daemon/commit/3deb6988c8992d92ef43d9f03c7a725ac9836c20 --- scripts/powerline-daemon | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 20794575..4a8ffb9b 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -274,9 +274,9 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull): sys.exit(1) # Redirect standard file descriptors. - si = file(stdin, 'r') - so = file(stdout, 'a+') - se = file(stderr, 'a+', 0) + si = open(stdin, 'rb') + so = open(stdout, 'a+b') + se = open(stderr, 'a+b', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) From dc763969124bc6ef2d59cfef0537b7ed9e7f23b7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 04:10:47 +0400 Subject: [PATCH 1311/1472] Solve encoding issues in powerline python client --- client/powerline.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/client/powerline.py b/client/powerline.py index 5cec13e8..6b76e804 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -8,6 +8,7 @@ import socket import os import errno + if len(sys.argv) < 2: print('Must provide at least one argument.', file=sys.stderr) raise SystemExit(1) @@ -42,22 +43,23 @@ fenc = sys.getfilesystemencoding() or 'utf-8' if fenc == 'ascii': fenc = 'utf-8' -args = [bytes('%x' % (len(sys.argv) - 1))] -args.extend((x.encode(fenc) if isinstance(x, type('')) else x for x in sys.argv[1:])) +tobytes = lambda s: s if isinstance(s, bytes) else s.encode(fenc) + +args = [tobytes('%x' % (len(sys.argv) - 1))] +args.extend((tobytes(s) for s in sys.argv[1:])) + try: cwd = os.getcwd() except EnvironmentError: pass else: - if isinstance(cwd, type('')): + if not isinstance(cwd, bytes): cwd = cwd.encode(fenc) args.append(cwd) -env = (k + b'=' + v for k, v in os.environ.items()) -env = (x if isinstance(x, bytes) else x.encode(fenc, 'replace') for x in env) -args.extend(env) +args.extend((tobytes(k) + b'=' + tobytes(v) for k, v in os.environ.items())) EOF = b'\0\0' @@ -75,4 +77,7 @@ while True: sock.close() -sys.stdout.write(b''.join(received)) +if sys.version_info < (3,): + sys.stdout.write(b''.join(received)) +else: + sys.stdout.buffer.write(b''.join(received)) From 8e77262f2d1b5d75a1242e99261f77012be1bdae Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 04:19:54 +0400 Subject: [PATCH 1312/1472] Perform shell word splitting in zsh bindings This makes POWERLINE_COMMAND be consistent across various bindings. --- docs/source/configuration/local.rst | 8 +++----- powerline/bindings/zsh/powerline.zsh | 12 ++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index 13902514..316e83c6 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -122,11 +122,9 @@ powerline into different directory. .. note:: ``$POWERLINE_COMMAND`` appears in shell scripts without quotes thus you can - specify additional parameters in bash. In zsh you will have to make - ``$POWERLINE_COMMAND`` an array parameter to achieve the same result. In - tmux it is passed to ``eval`` and depends on the shell used. - POSIX-compatible shells, zsh, bash and fish will split this variable in this - case. + specify additional parameters in bash. In tmux it is passed to ``eval`` and + depends on the shell used. POSIX-compatible shells, zsh, bash and fish will + split this variable in this case. If you want to disable prompt in shell, but still have tmux support or if you want to disable tmux support you can use variables diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index b75f138d..42eafe45 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -131,7 +131,7 @@ _powerline_setup_prompt() { } else if test -z "${POWERLINE_COMMAND}" ; then - POWERLINE_COMMAND=( "$($POWERLINE_CONFIG shell command)" ) + POWERLINE_COMMAND="$($POWERLINE_CONFIG shell command)" fi local add_args='-r .zsh' @@ -145,11 +145,11 @@ _powerline_setup_prompt() { local add_args_2=$add_args$new_args_2 add_args+=' --width=$(( ${COLUMNS:-$(_powerline_columns_fallback)} - 1 ))' local add_args_r2=$add_args$new_args_2 - PS1='$($POWERLINE_COMMAND shell aboveleft '$add_args')' - RPS1='$($POWERLINE_COMMAND shell right '$add_args')' - PS2='$($POWERLINE_COMMAND shell left '$add_args_2')' - RPS2='$($POWERLINE_COMMAND shell right '$add_args_r2')' - PS3='$($POWERLINE_COMMAND shell left '$add_args_3')' + PS1='$($=POWERLINE_COMMAND shell aboveleft '$add_args')' + RPS1='$($=POWERLINE_COMMAND shell right '$add_args')' + PS2='$($=POWERLINE_COMMAND shell left '$add_args_2')' + RPS2='$($=POWERLINE_COMMAND shell right '$add_args_r2')' + PS3='$($=POWERLINE_COMMAND shell left '$add_args_3')' fi } From 03c22e94a7833c352be89c916fcacd6cb565e682 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 04:30:22 +0400 Subject: [PATCH 1313/1472] Make tcsh check whether some variables are defined or empty Makes it consistent with other shells --- docs/source/configuration/local.rst | 6 ------ powerline/bindings/tcsh/powerline.tcsh | 12 ++++++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index 316e83c6..c66b5c78 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -145,9 +145,3 @@ tcsh you should set ``$POWERLINE_NO_TCSH_ABOVE`` or If you do not want to see additional space which is added to the right prompt in fish in order to support multiline prompt you should set ``$POWERLINE_NO_FISH_ABOVE`` or ``$POWERLINE_NO_SHELL_ABOVE`` variables. - -.. note:: - - Most supported shells’ configuration scripts check for ``$POWERLINE_CONFIG`` - and ``$POWERLINE_COMMAND`` configuration variables being empty. But tcsh - configuration script checks for variables being *defined*, not empty. diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index ac93341b..fafdb39b 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -11,6 +11,14 @@ if ! $?POWERLINE_CONFIG then else set POWERLINE_CONFIG="$POWERLINE_SOURCED[2]:h:h:h:h/scripts/powerline-config" endif +else + if "$POWERLINE_CONFIG" == "" then + if ( { which powerline-config > /dev/null } ) then + set POWERLINE_CONFIG="powerline-config" + else + set POWERLINE_CONFIG="$POWERLINE_SOURCED[2]:h:h:h:h/scripts/powerline-config" + endif + endif endif if ( { $POWERLINE_CONFIG shell --shell=tcsh uses tmux } ) then alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_`tmux display -p "#D" | tr -d %` $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' @@ -19,6 +27,10 @@ endif if ( { $POWERLINE_CONFIG shell --shell=tcsh uses prompt } ) then if ! $?POWERLINE_COMMAND then set POWERLINE_COMMAND="`$POWERLINE_CONFIG:q shell command`" + else + if "$POWERLINE_COMMAND" == "" then + set POWERLINE_COMMAND="`$POWERLINE_CONFIG:q shell command`" + endif endif if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then From b9360a083aa3c0dcb40287985256648226df9dfa Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 03:28:51 +0400 Subject: [PATCH 1314/1472] Make tests test powerline daemon with valid python and all clients --- tests/install.sh | 2 +- tests/test_shells/postproc.py | 7 +- tests/test_shells/screenrc | 2 +- tests/test_shells/test.sh | 179 ++++++++++++++++++++++------------ 4 files changed, 123 insertions(+), 67 deletions(-) diff --git a/tests/install.sh b/tests/install.sh index fcfd0b73..e9e243b3 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -22,7 +22,7 @@ else pip install ipython fi fi -sudo apt-get install -qq screen zsh tcsh mksh busybox +sudo apt-get install -qq screen zsh tcsh mksh busybox socat # Travis has too outdated fish. It cannot be used for tests. # sudo apt-get install fish true diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index 6866dfc1..d86fe6fc 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -9,9 +9,10 @@ import codecs test_type = sys.argv[1] -shell = sys.argv[2] -fname = os.path.join('tests', 'shell', shell + '.' + test_type + '.full.log') -new_fname = os.path.join('tests', 'shell', shell + '.' + test_type + '.log') +test_client = sys.argv[2] +shell = sys.argv[3] +fname = os.path.join('tests', 'shell', '.'.join((shell, test_type, test_client, 'full.log'))) +new_fname = os.path.join('tests', 'shell', '.'.join((shell, test_type, test_client, 'log'))) pid_fname = os.path.join('tests', 'shell', '3rd', 'pid') diff --git a/tests/test_shells/screenrc b/tests/test_shells/screenrc index 7c9674e5..3ba7ba7f 100644 --- a/tests/test_shells/screenrc +++ b/tests/test_shells/screenrc @@ -1,3 +1,3 @@ width 1024 height 1 -logfile "tests/shell/${SH}.${TEST_TYPE}.full.log" +logfile "tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log" diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index b9b46529..40990e81 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -1,23 +1,46 @@ #!/bin/bash +: ${PYTHON:=python} FAILED=0 ONLY_SHELL="$1" +ONLY_TEST_TYPE="$2" +COMMAND_PATTERN="$3" + +export PYTHON + +if test "x$ONLY_SHELL" = "x--help" ; then +cat << EOF +Usage: + $0 [[[ONLY_SHELL | ""] (ONLY_TEST_TYPE | "")] (COMMAND_PATTERN | "")] + +ONLY_SHELL: execute only tests for given shell +ONLY_TEST_TYPE: execute only "daemon" or "nodaemon" tests +COMMAND_PATTERN: use only commands that match given pattern for testing +EOF +fi check_screen_log() { TEST_TYPE="$1" - SH="$2" + TEST_CLIENT="$2" + SH="$3" if test -e tests/test_shells/${SH}.${TEST_TYPE}.ok ; then - diff -a -u tests/test_shells/${SH}.${TEST_TYPE}.ok tests/shell/${SH}.${TEST_TYPE}.log + diff -a -u tests/test_shells/${SH}.${TEST_TYPE}.ok tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.log return $? elif test -e tests/test_shells/${SH}.ok ; then - diff -a -u tests/test_shells/${SH}.ok tests/shell/${SH}.${TEST_TYPE}.log + diff -a -u tests/test_shells/${SH}.ok tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.log return $? else - cat tests/shell/${SH}.${TEST_TYPE}.log + cat tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.log return 1 fi } run() { + TEST_TYPE="$1" + shift + TEST_CLIENT="$1" + shift + SH="$1" + shift local local_path="$PWD/tests/shell/path:$PWD/scripts" if test "x$SH" = "xfish" ; then local_path="${local_path}:/usr/bin:/bin" @@ -33,6 +56,7 @@ run() { COLUMNS="${COLUMNS}" \ LINES="${LINES}" \ TEST_TYPE="${TEST_TYPE}" \ + TEST_CLIENT="${TEST_CLIENT}" \ SH="${SH}" \ DIR1="${DIR1}" \ DIR2="${DIR2}" \ @@ -40,27 +64,22 @@ run() { IPYTHONDIR="$PWD/tests/shell/ipython_home" \ POWERLINE_SHELL_CONTINUATION=$additional_prompts \ POWERLINE_SHELL_SELECT=$additional_prompts \ + POWERLINE_COMMAND="${POWERLINE_COMMAND}" \ "$@" } run_test() { TEST_TYPE="$1" shift + TEST_CLIENT="$1" + shift SH="$1" SESNAME="powerline-shell-test-${SH}-$$" ARGS=( "$@" ) - test "x$ONLY_SHELL" = "x" || test "x$ONLY_SHELL" = "x$SH" || return 0 - - if ! which "${SH}" ; then - return 0 - fi - - export TEST_TYPE - export SH - - run screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ - env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" + run "${TEST_TYPE}" "${TEST_CLIENT}" "${SH}" \ + screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ + env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" while ! screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH ; do sleep 0.1s done @@ -88,23 +107,27 @@ run_test() { while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do sleep 0.1s done - ./tests/test_shells/postproc.py ${TEST_TYPE} ${SH} + ./tests/test_shells/postproc.py ${TEST_TYPE} ${TEST_CLIENT} ${SH} rm -f tests/shell/3rd/pid - if ! check_screen_log ${TEST_TYPE} ${SH} ; then - echo '____________________________________________________________' - # Repeat the diff to make it better viewable in travis output - echo "Diff (cat -v):" - echo '============================================================' - check_screen_log ${TEST_TYPE} ${SH} | cat -v + if ! check_screen_log ${TEST_TYPE} ${TEST_CLIENT} ${SH} ; then echo '____________________________________________________________' + if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then + # Repeat the diff to make it better viewable in travis output + echo "Diff (cat -v):" + echo '============================================================' + check_screen_log ${TEST_TYPE} ${TEST_CLIENT} ${SH} | cat -v + echo '____________________________________________________________' + fi echo "Failed ${SH}. Full output:" echo '============================================================' - cat tests/shell/${SH}.${TEST_TYPE}.full.log - echo '____________________________________________________________' - echo "Full output (cat -v):" - echo '============================================================' - cat -v tests/shell/${SH}.${TEST_TYPE}.full.log + cat tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log echo '____________________________________________________________' + if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then + echo "Full output (cat -v):" + echo '============================================================' + cat -v tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log + echo '____________________________________________________________' + fi case ${SH} in *ksh) ${SH} -c 'echo ${KSH_VERSION}' @@ -146,7 +169,7 @@ mkdir tests/shell/fish_home cp -r tests/test_shells/ipython_home tests/shell mkdir tests/shell/path -ln -s "$(which "${PYTHON:-python}")" tests/shell/path/python +ln -s "$(which "${PYTHON}")" tests/shell/path/python ln -s "$(which screen)" tests/shell/path ln -s "$(which env)" tests/shell/path ln -s "$(which sleep)" tests/shell/path @@ -168,6 +191,9 @@ ln -s "$(which sed)" tests/shell/path ln -s "$(which rm)" tests/shell/path ln -s ../../test_shells/bgscript.sh tests/shell/path ln -s ../../test_shells/waitpid.sh tests/shell/path +if which socat ; then + ln -s "$(which socat)" tests/shell/path +fi for pexe in powerline powerline-config ; do if test -e scripts/$pexe ; then ln -s "$PWD/scripts/$pexe" tests/shell/path @@ -194,40 +220,66 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te scripts/powerline-config shell command for TEST_TYPE in "daemon" "nodaemon" ; do - if test $TEST_TYPE == daemon ; then - sh -c 'echo $$ > tests/shell/daemon_pid; ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & + if test $TEST_TYPE = daemon ; then + sh -c 'echo $$ > tests/shell/daemon_pid; $PYTHON ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & fi - if ! run_test $TEST_TYPE bash --norc --noprofile -i ; then - FAILED=1 + if test "x$ONLY_TEST_TYPE" != "x" && test "x$ONLY_TEST_TYPE" != "x$TEST_TYPE" ; then + continue fi - - if ! run_test $TEST_TYPE zsh -f -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE fish -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE tcsh -f -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE busybox ash -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE mksh -i ; then - FAILED=1 - fi - - if ! run_test $TEST_TYPE dash -i ; then - # dash tests are not stable, see #931 - # FAILED=1 - true - fi - if test $TEST_TYPE == daemon ; then - ./scripts/powerline-daemon -k + echo "> Testing $TEST_TYPE" + for POWERLINE_COMMAND in "" \ + $PWD/scripts/powerline \ + $PWD/scripts/powerline-render \ + "$PYTHON $PWD/client/powerline.py" \ + $PWD/client/powerline.sh + do + case "$POWERLINE_COMMAND" in + *powerline) TEST_CLIENT=C ;; + *powerline-render) TEST_CLIENT=render ;; + *powerline.py) TEST_CLIENT=python ;; + *powerline.sh) TEST_CLIENT=shell ;; + "") TEST_CLIENT=auto ;; + esac + if test "$TEST_CLIENT" = "shell" && ! which socat >/dev/null ; then + continue + fi + if test "$TEST_CLIENT" = render && test "$TEST_TYPE" = daemon ; then + continue + fi + if test "x$COMMAND_PATTERN" != "x" && ! ( + echo "$POWERLINE_COMMAND" | grep -e"$COMMAND_PATTERN" &>/dev/null) + then + continue + fi + export POWERLINE_COMMAND + echo ">> powerline command is ${POWERLINE_COMMAND:-empty}" + for TEST_COMMAND in \ + "bash --norc --noprofile -i" \ + "zsh -f -i" \ + "fish -i" \ + "tcsh -f -i" \ + "busybox ash -i" \ + "mksh -i" \ + "dash -i" + do + SH="${TEST_COMMAND%% *}" + if test "x$ONLY_SHELL" != "x" && test "x$ONLY_SHELL" != "x$SH" ; then + continue + fi + if ! which $SH >/dev/null ; then + continue + fi + echo ">>> $(which $SH)" + if ! run_test $TEST_TYPE $TEST_CLIENT $TEST_COMMAND ; then + # dash tests are not stable, see #931 + if test "x$SH" != "xdash" ; then + FAILED=1 + fi + fi + done + done + if test $TEST_TYPE = daemon ; then + $PYTHON ./scripts/powerline-daemon -k wait $(cat tests/shell/daemon_pid) if ! test -z "$(cat tests/shell/daemon_log)" ; then echo '____________________________________________________________' @@ -240,8 +292,11 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te done fi -if ! run_test ipython ipython ; then - FAILED=1 +if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xipython" ; then + echo "> $(which ipython)" + if ! run_test ipython ipython ipython ; then + FAILED=1 + fi fi test $FAILED -eq 0 && rm -r tests/shell From 042d5422311287582b7639e0350bb7833ae73d93 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 03:52:09 +0400 Subject: [PATCH 1315/1472] Also test whether running daemon is successfull without arguments --- tests/test_shells/test.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 40990e81..903ac79c 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -292,6 +292,23 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te done fi +if ! $PYTHON scripts/powerline-daemon &> tests/shell/daemon_log_2 ; then + echo "Daemon exited with status $?" + FAILED=1 +else + sleep 1 + $PYTHON scripts/powerline-daemon -k +fi + +if ! test -z "$(cat tests/shell/daemon_log_2)" ; then + FAILED=1 + echo '____________________________________________________________' + echo "Daemon log (2nd):" + echo '============================================================' + cat tests/shell/daemon_log_2 + FAILED=1 +fi + if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xipython" ; then echo "> $(which ipython)" if ! run_test ipython ipython ipython ; then From 77a7a267826465ffeedb10e4b8f3e095f3fab01a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 04:43:17 +0400 Subject: [PATCH 1316/1472] Do not require scripts/powerline It may be immediately moved to bin/ sometimes or not compiled at all for some reason. --- tests/test_shells/test.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 903ac79c..9053c1e8 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -240,6 +240,13 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te *powerline.sh) TEST_CLIENT=shell ;; "") TEST_CLIENT=auto ;; esac + if test "$TEST_CLIENT" = "C" && ! test -x scripts/powerline ; then + if which powerline >/dev/null ; then + POWERLINE_COMMAND=powerline + else + continue + fi + fi if test "$TEST_CLIENT" = "shell" && ! which socat >/dev/null ; then continue fi From 0232d3215b207189a727fc66700a4a82d4c3902d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 14:55:59 +0400 Subject: [PATCH 1317/1472] Add --socket argument to all clients Makes it not needed to kill daemon for testing, but disables implicit `powerline-config shell command` testing. --- client/powerline.c | 12 +++++++++-- client/powerline.py | 9 ++++++-- client/powerline.sh | 8 +++++++- powerline/shell.py | 1 + scripts/powerline-daemon | 43 ++++++++++++++++++++++++++------------- tests/test_shells/test.sh | 23 +++++++++++++-------- 6 files changed, 68 insertions(+), 28 deletions(-) diff --git a/client/powerline.c b/client/powerline.c index 44819ffe..f53e3457 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -60,20 +60,28 @@ int main(int argc, char *argv[]) { int i; ptrdiff_t read_size; struct sockaddr_un server; - char address[ADDRESS_SIZE]; + char address_buf[ADDRESS_SIZE]; const char eof[2] = "\0\0"; char num_args[NUM_ARGS_SIZE]; char buf[BUF_SIZE]; char *newargv[NEW_ARGV_SIZE]; char *wd = NULL; char **envp; + const char *address; if (argc < 2) { printf("Must provide at least one argument.\n"); return EXIT_FAILURE; } - snprintf(address, ADDRESS_SIZE, ADDRESS_TEMPLATE, getuid()); + if (argc > 3 && strcmp(argv[1], "--socket") == 0) { + address = argv[2]; + argv += 2; + argc -= 2; + } else { + snprintf(address_buf, ADDRESS_SIZE, ADDRESS_TEMPLATE, getuid()); + address = &(address_buf[0]); + } sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) diff --git a/client/powerline.py b/client/powerline.py index 6b76e804..d874b86a 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -15,10 +15,15 @@ if len(sys.argv) < 2: platform = sys.platform.lower() use_filesystem = 'darwin' in platform -# use_filesystem = True del platform -address = ('/tmp/powerline-ipc-%d' if use_filesystem else '\0powerline-ipc-%d') % os.getuid() +if sys.argv[1] == '--socket': + address = sys.argv[2] + if not use_filesystem: + address = '\0' + address + del sys.argv[1:3] +else: + address = ('/tmp/powerline-ipc-%d' if use_filesystem else '\0powerline-ipc-%d') % os.getuid() sock = socket.socket(family=socket.AF_UNIX) diff --git a/client/powerline.sh b/client/powerline.sh index 5860b7f8..2c85fe77 100755 --- a/client/powerline.sh +++ b/client/powerline.sh @@ -1,6 +1,12 @@ #!/bin/sh -ADDRESS="powerline-ipc-${UID:-`id -u`}" +if test "$1" = "--socket" ; then + shift + ADDRESS="$1" + shift +else + ADDRESS="powerline-ipc-${UID:-`id -u`}" +fi # Warning: env -0 does not work in busybox. Consider switching to parsing # `set` output in this case diff --git a/powerline/shell.py b/powerline/shell.py index 5920f897..7f006e58 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -66,6 +66,7 @@ def get_argparser(parser=None, *args, **kwargs): p.add_argument('-t', '--theme_option', metavar='THEME.KEY.KEY=VALUE', action='append', help='Like above, but theme-specific. THEME should point to an existing and used theme to have any effect, but it is fine to use any theme here.') p.add_argument('-R', '--renderer_arg', metavar='KEY=VAL', action='append', help='Like above, but provides argument for renderer. Is supposed to be used only by shell bindings to provide various data like last_exit_code or last_pipe_status (they are not using --renderer_arg for historical resons: renderer_arg was added later).') p.add_argument('-p', '--config_path', action='append', metavar='PATH', help='Path to configuration directory. If it is present then configuration files will only be seeked in the provided path. May be provided multiple times to search in a list of directories.') + p.add_argument('--socket', metavar='ADDRESS', type=str, help='Socket address to use in daemon clients. Is always UNIX domain socket on linux and file socket on Mac OS X. Not used here, present only for compatibility with other powerline clients. This argument must always be the first one and be in a form `--socket ADDRESS\': no `=\' or short form allowed (in other powerline clients, not here).') return p diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 4a8ffb9b..593002df 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -17,20 +17,13 @@ from io import StringIO from powerline.shell import get_argparser, finish_args, ShellPowerline, write_output from powerline.lib.monotonic import monotonic + is_daemon = False platform = sys.platform.lower() use_filesystem = 'darwin' in platform -# use_filesystem = True -del platform -if use_filesystem: - address = '/tmp/powerline-ipc-%d' - pidfile = address + '.pid' -else: - # Use the abstract namespace for sockets rather than the filesystem - # (Available only in linux) - address = '\0powerline-ipc-%d' -address = address % os.getuid() +address = None +pidfile = None class NonInteractiveArgParser(ArgumentParser): @@ -334,8 +327,11 @@ def lockpidfile(): import fcntl import atexit import stat - fd = os.open(pidfile, os.O_WRONLY | os.O_CREAT, - stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + fd = os.open( + pidfile, + os.O_WRONLY | os.O_CREAT, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH + ) try: fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except EnvironmentError: @@ -352,15 +348,34 @@ def lockpidfile(): def main(): - p = ArgumentParser(description= - 'Daemon to improve the performance of powerline') + global address + global pidfile + p = ArgumentParser(description='Daemon to improve the performance of powerline') p.add_argument('--quiet', '-q', action='store_true', help='Without other options: do not complain about already running powerline-daemon instance. Will still exit with 1. With `--kill\' and `--replace\': do not show any messages. With `--foreground\': ignored. Does not silence exceptions in any case.') + p.add_argument('--socket', '-s', help='Specify socket which will be used for connecting to daemon.') a = p.add_mutually_exclusive_group().add_argument a('--kill', '-k', action='store_true', help='Kill an already running instance') a('--foreground', '-f', action='store_true', help='Run in the foreground (dont daemonize)') a('--replace', '-r', action='store_true', help='Replace an already running instance') args = p.parse_args() + if args.socket: + address = args.socket + if not use_filesystem: + address = '\0' + address + else: + if use_filesystem: + address = '/tmp/powerline-ipc-%d' + else: + # Use the abstract namespace for sockets rather than the filesystem + # (Available only in linux) + address = '\0powerline-ipc-%d' + + address = address % os.getuid() + + if use_filesystem: + pidfile = address + '.pid' + if args.kill: if kill_daemon(): if not args.quiet: diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 9053c1e8..9f65aff0 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -16,6 +16,7 @@ ONLY_SHELL: execute only tests for given shell ONLY_TEST_TYPE: execute only "daemon" or "nodaemon" tests COMMAND_PATTERN: use only commands that match given pattern for testing EOF +exit 0 fi check_screen_log() { @@ -213,21 +214,25 @@ done unset ENV -if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbusybox ; then - powerline-daemon -k || true - sleep 1s +export ADDRESS="powerline-ipc-test-$RANDOM" +export PYTHON +echo "Powerline address: $ADDRESS" +if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbusybox ; then scripts/powerline-config shell command for TEST_TYPE in "daemon" "nodaemon" ; do if test $TEST_TYPE = daemon ; then - sh -c 'echo $$ > tests/shell/daemon_pid; $PYTHON ./scripts/powerline-daemon -f &>tests/shell/daemon_log' & + sh -c ' + echo $$ > tests/shell/daemon_pid + $PYTHON ./scripts/powerline-daemon -s$ADDRESS -f &>tests/shell/daemon_log + ' & fi if test "x$ONLY_TEST_TYPE" != "x" && test "x$ONLY_TEST_TYPE" != "x$TEST_TYPE" ; then continue fi echo "> Testing $TEST_TYPE" - for POWERLINE_COMMAND in "" \ + for POWERLINE_COMMAND in \ $PWD/scripts/powerline \ $PWD/scripts/powerline-render \ "$PYTHON $PWD/client/powerline.py" \ @@ -238,7 +243,6 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te *powerline-render) TEST_CLIENT=render ;; *powerline.py) TEST_CLIENT=python ;; *powerline.sh) TEST_CLIENT=shell ;; - "") TEST_CLIENT=auto ;; esac if test "$TEST_CLIENT" = "C" && ! test -x scripts/powerline ; then if which powerline >/dev/null ; then @@ -258,6 +262,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te then continue fi + POWERLINE_COMMAND="$POWERLINE_COMMAND --socket $ADDRESS" export POWERLINE_COMMAND echo ">> powerline command is ${POWERLINE_COMMAND:-empty}" for TEST_COMMAND in \ @@ -286,7 +291,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te done done if test $TEST_TYPE = daemon ; then - $PYTHON ./scripts/powerline-daemon -k + $PYTHON ./scripts/powerline-daemon -s$ADDRESS -k wait $(cat tests/shell/daemon_pid) if ! test -z "$(cat tests/shell/daemon_log)" ; then echo '____________________________________________________________' @@ -299,12 +304,12 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te done fi -if ! $PYTHON scripts/powerline-daemon &> tests/shell/daemon_log_2 ; then +if ! $PYTHON scripts/powerline-daemon -s$ADDRESS &> tests/shell/daemon_log_2 ; then echo "Daemon exited with status $?" FAILED=1 else sleep 1 - $PYTHON scripts/powerline-daemon -k + $PYTHON scripts/powerline-daemon -s$ADDRESS -k fi if ! test -z "$(cat tests/shell/daemon_log_2)" ; then From 9b7052bf3ee23de19cd0ed0f57fb8417edf3f6e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 15:16:30 +0400 Subject: [PATCH 1318/1472] Do not execute all tests in travis --- tests/test.sh | 2 +- tests/test_shells/test.sh | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/tests/test.sh b/tests/test.sh index f25d63a6..129e0b5e 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -20,7 +20,7 @@ for script in tests/*.vim ; do FAILED=1 fi done -if ! bash tests/test_shells/test.sh ; then +if ! bash tests/test_shells/test.sh --fast ; then echo "Failed shells" if ${PYTHON} -c 'import platform, sys; sys.exit(1 * (platform.python_implementation() == "PyPy"))' ; then FAILED=1 diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 9f65aff0..8f3e0d0c 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -1,10 +1,18 @@ #!/bin/bash : ${PYTHON:=python} FAILED=0 +if test "x$1" = "x--fast" ; then + FAST=1 + shift +fi ONLY_SHELL="$1" ONLY_TEST_TYPE="$2" COMMAND_PATTERN="$3" +if ! test -z "$ONLY_SHELL$ONLY_TEST_TYPE$COMMAND_PATTERN" ; then + FAST= +fi + export PYTHON if test "x$ONLY_SHELL" = "x--help" ; then @@ -222,6 +230,16 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te scripts/powerline-config shell command for TEST_TYPE in "daemon" "nodaemon" ; do + if test x$FAST = x1 ; then + if test $TEST_TYPE = daemon ; then + VARIANTS=3 + else + VARIANTS=4 + fi + EXETEST="$(( ${RANDOM:-`date +%N | sed s/^0*//`} % $VARIANTS ))" + echo "Execute tests: $EXETEST" + fi + if test $TEST_TYPE = daemon ; then sh -c ' echo $$ > tests/shell/daemon_pid @@ -232,6 +250,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te continue fi echo "> Testing $TEST_TYPE" + I=-1 for POWERLINE_COMMAND in \ $PWD/scripts/powerline \ $PWD/scripts/powerline-render \ @@ -244,6 +263,10 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te *powerline.py) TEST_CLIENT=python ;; *powerline.sh) TEST_CLIENT=shell ;; esac + if test "$TEST_CLIENT" = render && test "$TEST_TYPE" = daemon ; then + continue + fi + I="$(( I + 1 ))" if test "$TEST_CLIENT" = "C" && ! test -x scripts/powerline ; then if which powerline >/dev/null ; then POWERLINE_COMMAND=powerline @@ -254,9 +277,6 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te if test "$TEST_CLIENT" = "shell" && ! which socat >/dev/null ; then continue fi - if test "$TEST_CLIENT" = render && test "$TEST_TYPE" = daemon ; then - continue - fi if test "x$COMMAND_PATTERN" != "x" && ! ( echo "$POWERLINE_COMMAND" | grep -e"$COMMAND_PATTERN" &>/dev/null) then @@ -265,6 +285,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te POWERLINE_COMMAND="$POWERLINE_COMMAND --socket $ADDRESS" export POWERLINE_COMMAND echo ">> powerline command is ${POWERLINE_COMMAND:-empty}" + J=-1 for TEST_COMMAND in \ "bash --norc --noprofile -i" \ "zsh -f -i" \ @@ -274,6 +295,12 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te "mksh -i" \ "dash -i" do + J="$(( J + 1 ))" + if test x$FAST = x1 ; then + if test $(( (I + J) % $VARIANTS )) -ne $EXETEST ; then + continue + fi + fi SH="${TEST_COMMAND%% *}" if test "x$ONLY_SHELL" != "x" && test "x$ONLY_SHELL" != "x$SH" ; then continue From ebc98977aef7552b546f4b98b5478818692ec28a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 15:18:00 +0400 Subject: [PATCH 1319/1472] Make test_shells/test.sh compatible with dash --- tests/test_shells/test.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 8f3e0d0c..f495708b 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh : ${PYTHON:=python} FAILED=0 if test "x$1" = "x--fast" ; then @@ -84,11 +84,10 @@ run_test() { shift SH="$1" SESNAME="powerline-shell-test-${SH}-$$" - ARGS=( "$@" ) run "${TEST_TYPE}" "${TEST_CLIENT}" "${SH}" \ screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ - env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" + env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" while ! screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH ; do sleep 0.1s done @@ -105,7 +104,7 @@ run_test() { # … # prompt1> prompt2> … while read -r line ; do - screen -S "$SESNAME" -p 0 -X stuff "$line"$'\n' + screen -S "$SESNAME" -p 0 -X stuff "$line"$(printf '\r') sleep 1 done < tests/test_shells/input.$SH else @@ -222,7 +221,7 @@ done unset ENV -export ADDRESS="powerline-ipc-test-$RANDOM" +export ADDRESS="powerline-ipc-test-$$" export PYTHON echo "Powerline address: $ADDRESS" From 1b56179676edb6f9d0064ea85aed323630cf3ba0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 16:02:04 +0400 Subject: [PATCH 1320/1472] Disable dash tests in travis --- tests/test_shells/test.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index f495708b..64f9146f 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -301,6 +301,10 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te fi fi SH="${TEST_COMMAND%% *}" + # dash tests are not stable, see #931 + if test x$FAST$SH = x1dash ; then + continue + fi if test "x$ONLY_SHELL" != "x" && test "x$ONLY_SHELL" != "x$SH" ; then continue fi @@ -309,10 +313,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te fi echo ">>> $(which $SH)" if ! run_test $TEST_TYPE $TEST_CLIENT $TEST_COMMAND ; then - # dash tests are not stable, see #931 - if test "x$SH" != "xdash" ; then - FAILED=1 - fi + FAILED=1 fi done done From 20e9575c176b4520d1cfd7e87a9a92386e09c190 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 16:24:16 +0400 Subject: [PATCH 1321/1472] Do not try to test ipython if it is not available --- tests/test_shells/test.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 64f9146f..9104fa99 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -349,9 +349,11 @@ if ! test -z "$(cat tests/shell/daemon_log_2)" ; then fi if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xipython" ; then - echo "> $(which ipython)" - if ! run_test ipython ipython ipython ; then - FAILED=1 + if which ipython >/dev/null ; then + echo "> $(which ipython)" + if ! run_test ipython ipython ipython ; then + FAILED=1 + fi fi fi From f6f8dd8af3844b66edb6afe2824844da8d08c856 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 19:07:57 +0400 Subject: [PATCH 1322/1472] Make condition function return path as expected by lib.config --- powerline/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 36210c12..1bd5a5d3 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -15,7 +15,9 @@ from threading import Lock, Event def _config_loader_condition(path): - return path and os.path.isfile(path) + if path and os.path.isfile(path): + return path + return None def _find_config_files(search_paths, config_file, config_loader=None, loader_callback=None): From 483b261019499f1f4292842c02dbd92dcd48ffd4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 23 Aug 2014 19:09:28 +0400 Subject: [PATCH 1323/1472] When processing exception from _load do not allow overwriting it If `self.loaded.pop` raises KeyError then this KeyError will be logged, not the initial exception from `self._load`. --- powerline/lib/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index d56b3c68..8742667b 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -195,11 +195,11 @@ class ConfigLoader(MultiRunnedThread): try: self.loaded[path] = deepcopy(self._load(path)) except Exception as e: + self.exception('Error while loading {0}: {1}', path, str(e)) try: self.loaded.pop(path) except KeyError: pass - self.exception('Error while loading {0}: {1}', path, str(e)) try: self.loaded.pop(path) except KeyError: From 173c4d6e76637c5ac1579d8d70ac60c3e50f79d8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 01:20:25 +0400 Subject: [PATCH 1324/1472] Add internal_ip segment Closes #857 --- .../config_files/colorschemes/default.json | 1 + powerline/segments/common.py | 68 +++++++++++++++++++ tests/install.sh | 2 +- tests/lib/__init__.py | 3 +- tests/test_segments.py | 48 ++++++++++++- 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json index a015fe6b..8aed60a3 100644 --- a/powerline/config_files/colorschemes/default.json +++ b/powerline/config_files/colorschemes/default.json @@ -16,6 +16,7 @@ "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0", "attr": [] }, "uptime": { "fg": "gray8", "bg": "gray0", "attr": [] }, "external_ip": { "fg": "gray8", "bg": "gray0", "attr": [] }, + "internal_ip": { "fg": "gray8", "bg": "gray0", "attr": [] }, "network_load": { "fg": "gray8", "bg": "gray0", "attr": [] }, "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attr": [] }, "system_load": { "fg": "gray8", "bg": "gray0", "attr": [] }, diff --git a/powerline/segments/common.py b/powerline/segments/common.py index da07c7a0..ecb77700 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -263,6 +263,74 @@ Divider highlight group used: ``background:divider``. ''') +try: + import netifaces +except ImportError: + def internal_ip(pl, interface='detect', ipv=4): + return None +else: + _interface_starts = { + 'eth': 10, # Regular ethernet adapters : eth1 + 'enp': 10, # Regular ethernet adapters, Gentoo : enp2s0 + 'ath': 9, # Atheros WiFi adapters : ath0 + 'wlan': 9, # Other WiFi adapters : wlan1 + 'wlp': 9, # Other WiFi adapters, Gentoo : wlp5s0 + 'teredo': 1, # miredo interface : teredo + 'lo': -10, # Loopback interface : lo + } + + _interface_start_re = re.compile(r'^([a-z]+?)(\d|$)') + + def _interface_key(interface): + match = _interface_start_re.match(interface) + if match: + try: + base = _interface_starts[match.group(1)] * 100 + except KeyError: + base = 500 + if match.group(2): + return base - int(match.group(2)) + else: + return base + else: + return 0 + + def internal_ip(pl, interface='detect', ipv=4): + if interface == 'detect': + try: + interface = next(iter(sorted(netifaces.interfaces(), key=_interface_key, reverse=True))) + except StopIteration: + pl.info('No network interfaces found') + return None + addrs = netifaces.ifaddresses(interface) + try: + return addrs[netifaces.AF_INET6 if ipv == 6 else netifaces.AF_INET][0]['addr'] + except (KeyError, IndexError): + return None + + +internal_ip = with_docstring(internal_ip, +'''Return internal IP address + +Requires ``netifaces`` package to work properly. + +:param str interface: + Interface on which IP will be checked. Use ``detect`` to automatically + detect interface. In this case interfaces with lower numbers will be + preferred over interfaces with similar names. Order of preference based on + names: + + #. ``eth`` and ``enp`` followed by number or the end of string. + #. ``ath``, ``wlan`` and ``wlp`` followed by number or the end of string. + #. ``teredo`` followed by number or the end of string. + #. Any other interface that is not ``lo*``. + #. ``lo`` followed by number or the end of string. +:param int ipv: + 4 or 6 for ipv4 and ipv6 respectively, depending on which IP address you + need exactly. +''') + + # Weather condition code descriptions available at # http://developer.yahoo.com/weather/#codes weather_conditions_codes = ( diff --git a/tests/install.sh b/tests/install.sh index e9e243b3..42ef7650 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -1,6 +1,6 @@ #!/bin/sh pip install . -pip install psutil +pip install psutil netifaces if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then # Python 2 if python -c 'import platform, sys; sys.exit(1 - (platform.python_implementation() == "CPython"))' ; then diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index ff340982..641642c2 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -9,10 +9,11 @@ class Pl(object): self.errors = [] self.warns = [] self.debugs = [] + self.infos = [] self.prefix = None self.use_daemon_threads = True - for meth in ('error', 'warn', 'debug', 'exception'): + for meth in ('error', 'warn', 'debug', 'exception', 'info'): exec (( 'def {0}(self, msg, *args, **kwargs):\n' ' self.{0}s.append((kwargs.get("prefix") or self.prefix, msg, args, kwargs))\n' diff --git a/tests/test_segments.py b/tests/test_segments.py index 2acccb46..9666ca0b 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -9,7 +9,7 @@ import sys import os from functools import partial from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl -from tests import TestCase +from tests import TestCase, SkipTest vim = None @@ -587,6 +587,52 @@ class TestCommon(TestCase): } ]) + def test_internal_ip(self): + try: + import netifaces + except ImportError: + raise SkipTest() + pl = Pl() + addr = { + 'enp2s0': { + netifaces.AF_INET: [{'addr': '192.168.100.200'}], + netifaces.AF_INET6: [{'addr': 'feff::5446:5eff:fe5a:7777%enp2s0'}] + }, + 'lo': { + netifaces.AF_INET: [{'addr': '127.0.0.1'}], + netifaces.AF_INET6: [{'addr': '::1'}] + }, + 'teredo': { + netifaces.AF_INET6: [{'addr': 'feff::5446:5eff:fe5a:7777'}] + }, + } + interfaces = ['lo', 'enp2s0', 'teredo'] + with replace_module_module( + common, 'netifaces', + interfaces=(lambda: interfaces), + ifaddresses=(lambda interface: addr[interface]), + AF_INET=netifaces.AF_INET, + AF_INET6=netifaces.AF_INET6, + ): + self.assertEqual(common.internal_ip(pl=pl), '192.168.100.200') + self.assertEqual(common.internal_ip(pl=pl, interface='detect'), '192.168.100.200') + self.assertEqual(common.internal_ip(pl=pl, interface='lo'), '127.0.0.1') + self.assertEqual(common.internal_ip(pl=pl, interface='teredo'), None) + self.assertEqual(common.internal_ip(pl=pl, ipv=4), '192.168.100.200') + self.assertEqual(common.internal_ip(pl=pl, interface='detect', ipv=4), '192.168.100.200') + self.assertEqual(common.internal_ip(pl=pl, interface='lo', ipv=4), '127.0.0.1') + self.assertEqual(common.internal_ip(pl=pl, interface='teredo', ipv=4), None) + self.assertEqual(common.internal_ip(pl=pl, ipv=6), 'feff::5446:5eff:fe5a:7777%enp2s0') + self.assertEqual(common.internal_ip(pl=pl, interface='detect', ipv=6), 'feff::5446:5eff:fe5a:7777%enp2s0') + self.assertEqual(common.internal_ip(pl=pl, interface='lo', ipv=6), '::1') + self.assertEqual(common.internal_ip(pl=pl, interface='teredo', ipv=6), 'feff::5446:5eff:fe5a:7777') + interfaces[1:2] = () + self.assertEqual(common.internal_ip(pl=pl, ipv=6), 'feff::5446:5eff:fe5a:7777') + interfaces[1:2] = () + self.assertEqual(common.internal_ip(pl=pl, ipv=6), '::1') + interfaces[:] = () + self.assertEqual(common.internal_ip(pl=pl, ipv=6), None) + class TestVim(TestCase): def test_mode(self): From 098dadadf542c3d551a1c2fe737d51510fc7f426 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:20:08 +0400 Subject: [PATCH 1325/1472] Add some unlets before calling :finish --- powerline/bindings/vim/plugin/powerline.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index e0367e8f..14f385dc 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -20,6 +20,7 @@ if exists('g:powerline_pycmd') echomsg 'specify g:powerline_pyeval explicitly or unset both and let powerline' echomsg 'figure them out.' echohl None + unlet s:pycmd finish endif let s:pyeval = g:powerline_pyeval @@ -45,6 +46,7 @@ if !s:has_python echomsg 'details.' echohl None endif + unlet s:has_python finish endif unlet s:has_python From c2609e31b821678fa61102c706f7c533ba9378eb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:21:09 +0400 Subject: [PATCH 1326/1472] =?UTF-8?q?Replace=20let=20s:pystr=E2=80=A6=20wi?= =?UTF-8?q?th=20call=20s:rcmd(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes lines slightly shorter --- powerline/bindings/vim/plugin/powerline.vim | 84 ++++++++++++--------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 14f385dc..357d1064 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -52,19 +52,27 @@ endif unlet s:has_python let s:import_cmd = 'from powerline.vim import VimPowerline' +function s:rcmd(s) + if !exists('s:pystr') + let s:pystr = a:s . "\n" + else + let s:pystr = s:pystr . a:s . "\n" + endif +endfunction try - let s:pystr = "try:\n" - let s:pystr .= " ".s:import_cmd."\n" - let s:pystr .= "except ImportError:\n" - let s:pystr .= " import sys, vim\n" - let s:pystr .= " sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))\n" - let s:pystr .= " ".s:import_cmd."\n" + call s:rcmd("try:") + call s:rcmd(" ".s:import_cmd."") + call s:rcmd("except ImportError:") + call s:rcmd(" import sys, vim") + call s:rcmd(" sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))") + call s:rcmd(" ".s:import_cmd."") execute s:pycmd s:pystr unlet s:pystr let s:launched = 1 finally unlet s:import_cmd if !exists('s:launched') + unlet s:pystr echohl ErrorMsg echomsg 'An error occurred while importing powerline package.' echomsg 'This could be caused by invalid sys.path setting,' @@ -77,46 +85,48 @@ finally echomsg 'should set g:powerline_pycmd to "py3" to make it load correctly.' endif echohl None - let s:pystr = "def powerline_troubleshoot():\n" - let s:pystr .= " import sys\n" - let s:pystr .= " if sys.version_info < (2, 6):\n" - let s:pystr .= " print('Too old python version: ' + sys.version + ' (first supported is 2.6)')\n" - let s:pystr .= " elif sys.version_info[0] == 3 and sys.version_info[1] < 2:\n" - let s:pystr .= " print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')\n" - let s:pystr .= " try:\n" - let s:pystr .= " import powerline\n" - let s:pystr .= " except ImportError:\n" - let s:pystr .= " print('Unable to import powerline, is it installed?')\n" - if expand('')[:4] isnot# '/usr/' - let s:pystr .= " else:\n" - let s:pystr .= " import os\n" - let s:pystr .= " powerline_dir = os.path.dirname(os.path.realpath(powerline.__file__))\n" - let s:pystr .= " this_dir = os.path.dirname(os.path.realpath(vim.eval('expand(\":p\")')))\n" - let s:pystr .= " this_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))\n" - let s:pystr .= " if os.path.basename(this_dir) != 'powerline':\n" - let s:pystr .= " print('Check your installation:')\n" - let s:pystr .= " print('this script is not in powerline[/bindings/vim/plugin] directory,')\n" - let s:pystr .= " print('neither it is installed system-wide')\n" - let s:pystr .= " this_dir = os.path.dirname(this_dir)\n" - let s:pystr .= " real_powerline_dir = os.path.realpath(powerline_dir)\n" - let s:pystr .= " real_this_dir = os.path.realpath(this_dir)\n" - let s:pystr .= " if real_this_dir != sys.path[-1]:\n" - let s:pystr .= " print('Check your installation:')\n" - let s:pystr .= " print('this script is symlinked somewhere where powerline is not present.')\n" - let s:pystr .= " elif real_powerline_dir != real_this_dir:\n" - let s:pystr .= " print('It appears that you have two powerline versions installed:')\n" - let s:pystr .= " print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')\n" - let s:pystr .= " print('You should remove one of this. Check out troubleshooting section,')\n" - let s:pystr .= " print('it contains some information about the alternatives.')\n" + call s:rcmd( "def powerline_troubleshoot():") + call s:rcmd( " import sys") + call s:rcmd( " if sys.version_info < (2, 6):") + call s:rcmd( " print('Too old python version: ' + sys.version + ' (first supported is 2.6)')") + call s:rcmd( " elif sys.version_info[0] == 3 and sys.version_info[1] < 2:") + call s:rcmd( " print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')") + call s:rcmd( " try:") + call s:rcmd( " import powerline") + call s:rcmd( " except ImportError:") + call s:rcmd( " print('Unable to import powerline, is it installed?')") + if strpart(expand(''), 0, 5) !=# '/usr/' + call s:rcmd(" else:") + call s:rcmd(" import os") + call s:rcmd(" powerline_dir = os.path.dirname(os.path.realpath(powerline.__file__))") + call s:rcmd(" this_dir = os.path.dirname(os.path.realpath(vim.eval('expand(\":p\")')))") + call s:rcmd(" this_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))") + call s:rcmd(" if os.path.basename(this_dir) != 'powerline':") + call s:rcmd(" print('Check your installation:')") + call s:rcmd(" print('this script is not in powerline[/bindings/vim/plugin] directory,')") + call s:rcmd(" print('neither it is installed system-wide')") + call s:rcmd(" this_dir = os.path.dirname(this_dir)") + call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") + call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") + call s:rcmd(" if real_this_dir != sys.path[-1]:") + call s:rcmd(" print('Check your installation:')") + call s:rcmd(" print('this script is symlinked somewhere where powerline is not present.')") + call s:rcmd(" elif real_powerline_dir != real_this_dir:") + call s:rcmd(" print('It appears that you have two powerline versions installed:')") + call s:rcmd(" print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')") + call s:rcmd(" print('You should remove one of this. Check out troubleshooting section,')") + call s:rcmd(" print('it contains some information about the alternatives.')") endif execute s:pycmd s:pystr unlet s:pystr unlet s:pycmd unlet s:pyeval + delfunction s:rcmd finish else unlet s:launched endif + delfunction s:rcmd endtry let s:can_replace_pyeval = !exists('g:powerline_pyeval') From 2c8932b0847f143f0b6b8b0b278e5fdd75e5956c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:21:51 +0400 Subject: [PATCH 1327/1472] Do not call mode() with an argument on old Vims --- powerline/renderers/vim.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 28cf0ee4..cc89a707 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -18,6 +18,10 @@ except ImportError: vim_mode = vim_get_func('mode', rettype=str) +if int(vim.eval('v:version')) >= 702: + _vim_mode = vim_mode + vim_mode = lambda: _vim_mode(1) + mode_translations = { chr(ord('V') - 0x40): '^V', chr(ord('S') - 0x40): '^S', @@ -89,7 +93,7 @@ class VimRenderer(Renderer): segment_info = self.segment_info.copy() if window is vim.current.window: - mode = vim_mode(1) + mode = vim_mode() mode = mode_translations.get(mode, mode) else: mode = 'nc' From 6dde8f2f952f9b95023bce86835606bdb147c8b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:27:52 +0400 Subject: [PATCH 1328/1472] Move powerline setup under :try Makes it benefit from troubleshooting below --- powerline/bindings/vim/plugin/powerline.vim | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 357d1064..70c46b30 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -60,16 +60,21 @@ function s:rcmd(s) endif endfunction try + let s:can_replace_pyeval = !exists('g:powerline_pyeval') call s:rcmd("try:") call s:rcmd(" ".s:import_cmd."") call s:rcmd("except ImportError:") call s:rcmd(" import sys, vim") call s:rcmd(" sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))") call s:rcmd(" ".s:import_cmd."") + call s:rcmd("import vim") + call s:rcmd("VimPowerline().setup(pyeval=vim.eval('s:pyeval'), pycmd=vim.eval('s:pycmd'), can_replace_pyeval=int(vim.eval('s:can_replace_pyeval')))") + call s:rcmd("del VimPowerline") execute s:pycmd s:pystr unlet s:pystr let s:launched = 1 finally + unlet s:can_replace_pyeval unlet s:import_cmd if !exists('s:launched') unlet s:pystr @@ -126,15 +131,7 @@ finally else unlet s:launched endif + unlet s:pycmd + unlet s:pyeval delfunction s:rcmd endtry - -let s:can_replace_pyeval = !exists('g:powerline_pyeval') - -execute s:pycmd 'import vim' -execute s:pycmd 'VimPowerline().setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"), can_replace_pyeval=int(vim.eval("s:can_replace_pyeval")))' -execute s:pycmd 'del VimPowerline' - -unlet s:can_replace_pyeval -unlet s:pycmd -unlet s:pyeval From 767d0afa2aa53d246e186a489274ddaa9fc4c571 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:32:52 +0400 Subject: [PATCH 1329/1472] Make troubleshooting routine actually work --- powerline/bindings/vim/plugin/powerline.vim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 70c46b30..1de4fe1f 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -122,6 +122,10 @@ finally call s:rcmd(" print('You should remove one of this. Check out troubleshooting section,')") call s:rcmd(" print('it contains some information about the alternatives.')") endif + call s:rcmd( "try:") + call s:rcmd( " powerline_troubleshoot()") + call s:rcmd( "finally:") + call s:rcmd( " del powerline_troubleshoot") execute s:pycmd s:pystr unlet s:pystr unlet s:pycmd From 6a8074a3d30b615cd49b52cd464acb827e3401d9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:35:22 +0400 Subject: [PATCH 1330/1472] Update information about Vim version --- docs/source/usage.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/usage.rst b/docs/source/usage.rst index ccaa8925..c5d829fb 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -19,7 +19,9 @@ Python headers to be installed on your system. Please consult your distribution's documentation for details on how to compile and install packages. -Vim version 7.3.661 or newer is recommended for performance reasons. +Vim version 7.4 or newer is recommended for performance reasons, but Powerline +is known to work on vim-7.0.112 (some segments may not work though as it was not +actually tested). .. _usage-terminal-emulators: From 5bc1f75b70cfef6c95873bf371b1a01843c13b9a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 13:41:54 +0400 Subject: [PATCH 1331/1472] Add support for vim.eval('v:version') in test bindings --- tests/vim.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/vim.py b/tests/vim.py index f7e0f703..15cdf7b6 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -229,6 +229,8 @@ def command(cmd): def eval(expr): if expr.startswith('g:'): return vars[expr[2:]] + elif expr.startswith('v:'): + return vvars[expr[2:]] elif expr.startswith('&'): return options[expr[1:]] elif expr.startswith('$'): From 04c293e40d7480fc79d67033ac559c77fc550ab3 Mon Sep 17 00:00:00 2001 From: sthsieh Date: Fri, 23 May 2014 23:44:19 +0800 Subject: [PATCH 1332/1472] Add fish shell vi-mode support --- powerline/bindings/fish/powerline-setup.fish | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index ff0d42bc..0e9fde04 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -33,6 +33,9 @@ function powerline-setup if test -z "$POWERLINE_COMMAND" set -g POWERLINE_COMMAND (eval $POWERLINE_CONFIG shell command) end + function --on-variable fish_bind_mode _powerline_bind_mode + set -g -x _POWERLINE_MODE $fish_bind_mode + end function --on-variable POWERLINE_COMMAND _powerline_update set -l addargs "--last_exit_code=\$status" set -l addargs "$addargs --last_pipe_status=\$status" @@ -67,6 +70,7 @@ function powerline-setup " _powerline_set_columns end + _powerline_bind_mode _powerline_update end if eval $POWERLINE_CONFIG shell --shell=fish uses tmux From 9be2a2e39b97758e56b0c11f363820b53c419537 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Sat, 24 May 2014 03:17:14 +0800 Subject: [PATCH 1333/1472] add _POWERLINE_DEFAULT_MODE detection --- powerline/bindings/fish/powerline-setup.fish | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 0e9fde04..ca9ef636 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -35,6 +35,11 @@ function powerline-setup end function --on-variable fish_bind_mode _powerline_bind_mode set -g -x _POWERLINE_MODE $fish_bind_mode + if test x$fish_key_bindings != xfish_vi_key_bindings + set -g -x _POWERLINE_DEFAULT_MODE default + else + set -g -e _POWERLINE_DEFAULT_MODE + end end function --on-variable POWERLINE_COMMAND _powerline_update set -l addargs "--last_exit_code=\$status" From 09b78884de47230afb9d96b63fe1d648fed2ecec Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 15:37:56 +0400 Subject: [PATCH 1334/1472] Set default mode in another function --- powerline/bindings/fish/powerline-setup.fish | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index ca9ef636..fad70f7b 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -35,6 +35,8 @@ function powerline-setup end function --on-variable fish_bind_mode _powerline_bind_mode set -g -x _POWERLINE_MODE $fish_bind_mode + end + function --on-variable fish_key_bindings _powerline_set_default_mode if test x$fish_key_bindings != xfish_vi_key_bindings set -g -x _POWERLINE_DEFAULT_MODE default else @@ -76,6 +78,7 @@ function powerline-setup _powerline_set_columns end _powerline_bind_mode + _powerline_set_default_mode _powerline_update end if eval $POWERLINE_CONFIG shell --shell=fish uses tmux From 68ecd81edd7ca48d29a79c694231e4a8eaf2d9af Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 16:58:35 +0400 Subject: [PATCH 1335/1472] Wait until fish_update_completions finishes --- tests/test_shells/input.fish | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index eda074e9..84685a77 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -1,4 +1,7 @@ set fish_function_path "$PWD/powerline/bindings/fish" $fish_function_path +while jobs | grep fish_update_completions + sleep 1 +end powerline-setup set POWERLINE_COMMAND "$POWERLINE_COMMAND -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" @@ -22,4 +25,3 @@ cd ../'$(echo)' cd ../'`echo`' false true is the last line -exit From 54672aace05fe15351c50b1ca807be60032aae40 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 16:59:03 +0400 Subject: [PATCH 1336/1472] Make COMMAND_PATTERN consistent with other arguments --- tests/test_shells/input.fish | 1 + tests/test_shells/test.sh | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 84685a77..673b720c 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -25,3 +25,4 @@ cd ../'$(echo)' cd ../'`echo`' false true is the last line +exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 9104fa99..052797e0 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -7,9 +7,9 @@ if test "x$1" = "x--fast" ; then fi ONLY_SHELL="$1" ONLY_TEST_TYPE="$2" -COMMAND_PATTERN="$3" +ONLY_TEST_CLIENT="$3" -if ! test -z "$ONLY_SHELL$ONLY_TEST_TYPE$COMMAND_PATTERN" ; then +if ! test -z "$ONLY_SHELL$ONLY_TEST_TYPE$ONLY_TEST_CLIENT" ; then FAST= fi @@ -18,11 +18,11 @@ export PYTHON if test "x$ONLY_SHELL" = "x--help" ; then cat << EOF Usage: - $0 [[[ONLY_SHELL | ""] (ONLY_TEST_TYPE | "")] (COMMAND_PATTERN | "")] + $0 [[[ONLY_SHELL | ""] (ONLY_TEST_TYPE | "")] (ONLY_TEST_CLIENT | "")] ONLY_SHELL: execute only tests for given shell ONLY_TEST_TYPE: execute only "daemon" or "nodaemon" tests -COMMAND_PATTERN: use only commands that match given pattern for testing +ONLY_TEST_CLIENT: use only given test client (one of C, python, render, shell) EOF exit 0 fi @@ -276,9 +276,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te if test "$TEST_CLIENT" = "shell" && ! which socat >/dev/null ; then continue fi - if test "x$COMMAND_PATTERN" != "x" && ! ( - echo "$POWERLINE_COMMAND" | grep -e"$COMMAND_PATTERN" &>/dev/null) - then + if test "x$ONLY_TEST_CLIENT" != "x" && test "x$TEST_CLIENT" != "x$ONLY_TEST_CLIENT" ; then continue fi POWERLINE_COMMAND="$POWERLINE_COMMAND --socket $ADDRESS" From 0af5e2d2cae52de83cf78f57782e8a0f253b7239 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 17:09:40 +0400 Subject: [PATCH 1337/1472] Test mode support in fish WARNING: Requires development version of fish. Since fish is not tested in travis it is probably OK. --- tests/test_shells/fish.ok | 19 +++++++++++++++++++ tests/test_shells/input.fish | 3 +++ tests/test_shells/postproc.py | 12 ++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/test_shells/fish.ok b/tests/test_shells/fish.ok index 94c4e3fc..8364f6a9 100644 --- a/tests/test_shells/fish.ok +++ b/tests/test_shells/fish.ok @@ -14,3 +14,22 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)     HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)     HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`   + USER  ⋯  shell  3rd  `echo`   +   BRANCH  +   BRANCH  +   BRANCH  + INSERT  USER  ⋯  shell  3rd  `echo`   +   BRANCH  + DEFAULT  USER  ⋯  shell  3rd  `echo`   +   BRANCH  + INSERT  USER  ⋯  shell  3rd  `echo`   +   BRANCH  + DEFAULT  USER  ⋯  shell  3rd  `echo`   +   BRANCH  + INSERT  USER  ⋯  shell  3rd  `echo`   +   BRANCH  +   BRANCH  + INSERT  USER  ⋯  shell  3rd  `echo`   +   BRANCH  +   BRANCH  +   BRANCH  diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 673b720c..9d819cfc 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -23,6 +23,9 @@ cd ../'#[bold]' cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' +set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default" +set -g fish_key_bindings fish_vi_key_bindings +ii false true is the last line exit diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index d86fe6fc..ddacb8b1 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -41,12 +41,16 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: if pid is not None: line = line.replace(pid, 'PID') if shell == 'fish': + res = '' try: - start = line.index('\033[0;') - end = line.index('\033[0m', start) - line = line[start:end + 4] + '\n' + while line.index('\033[0;'): + start = line.index('\033[0;') + end = line.index('\033[0m', start) + res += line[start:end + 4] + '\n' + line = line[end + 4:] except ValueError: - line = '' + pass + line = res elif shell == 'tcsh': try: start = line.index('\033[0;') From 58057c95b92d700a1e346c611bd649c98a5e3730 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 17:13:00 +0400 Subject: [PATCH 1338/1472] Add a directory that will make fish_update_completions not launch --- tests/test_shells/test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 052797e0..f4872ccf 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -174,6 +174,8 @@ mkdir tests/shell/3rd/'$(echo)' mkdir tests/shell/3rd/'`echo`' mkdir tests/shell/fish_home +mkdir tests/shell/fish_home/fish +mkdir tests/shell/fish_home/fish/generated_completions cp -r tests/test_shells/ipython_home tests/shell mkdir tests/shell/path From 10665f4c7dfb37501f1dad6a0de9a5f2c2c6a2ea Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 17:45:08 +0400 Subject: [PATCH 1339/1472] Do not format message before passing it to self.pl.warn --- powerline/lib/vcs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index ea11b345..ba00eca2 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -196,7 +196,7 @@ class TreeStatusCache(dict): if self.tw(key, ignore_event=getattr(repo, 'ignore_event', None)): self.pop(key, None) except OSError as e: - self.pl.warn('Failed to check %s for changes, with error: %s' % key, e) + self.pl.warn('Failed to check {0} for changes, with error: {1}', key, str(e)) return self.cache_and_get(key, repo.status) From 2d4897d5dd6ec6b7ad33304d327e344893f0e8a3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 17:46:11 +0400 Subject: [PATCH 1340/1472] Catch errors from tree_status Fixes #341 --- powerline/segments/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ecb77700..8aefb16b 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -69,7 +69,11 @@ def branch(pl, segment_info, create_watcher, status_colors=False): branch = repo.branch() scol = ['branch'] if status_colors: - status = tree_status(repo, pl) + try: + status = tree_status(repo, pl) + except Exception as e: + pl.exception('Failed to compute tree status: {0}', str(e)) + status = '?' scol.insert(0, 'branch_dirty' if status and status.strip() else 'branch_clean') return [{ 'contents': branch, From 7c587f5c2054bc8810985590f05c11210a5c52bd Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 18:07:07 +0400 Subject: [PATCH 1341/1472] Add clementine media player support Based on #438 by @printesoi Fixes #422 Closes #438 --- powerline/segments/common.py | 43 +++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ecb77700..95ce9077 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1077,33 +1077,54 @@ class NowPlayingSegment(Segment): 'total': self._convert_seconds(now_playing.get('time', 0)), } - def player_spotify_dbus(self, pl, dbus=None): + def player_dbus(self, player_name, bus_name, player_path, iface_prop, iface_player): try: import dbus except ImportError: - pl.exception('Could not add Spotify segment: requires python-dbus.') + pl.exception('Could not add {0} segment: requires python-dbus', player_name) return bus = dbus.SessionBus() - DBUS_IFACE_PROPERTIES = 'org.freedesktop.DBus.Properties' - DBUS_IFACE_PLAYER = 'org.freedesktop.MediaPlayer2' try: - player = bus.get_object('com.spotify.qt', '/') - iface = dbus.Interface(player, DBUS_IFACE_PROPERTIES) - info = iface.Get(DBUS_IFACE_PLAYER, 'Metadata') - status = iface.Get(DBUS_IFACE_PLAYER, 'PlaybackStatus') + player = bus.get_object(bus_name, player_path) + iface = dbus.Interface(player, iface_prop) + info = iface.Get(iface_player, 'Metadata') + status = iface.Get(iface_player, 'PlaybackStatus') except dbus.exceptions.DBusException: return if not info: return + album = u(info.get('xesam:album')) + title = u(info.get('xesam:title')) + artist = info.get('xesam:artist') state = self._convert_state(status) + if artist: + artist = u(artist[0]) return { 'state': state, - 'album': info.get('xesam:album'), - 'artist': info.get('xesam:artist')[0], - 'title': info.get('xesam:title'), + 'album': album, + 'artist': artist, + 'title': title, 'total': self._convert_seconds(info.get('mpris:length') / 1e6), } + def player_spotify_dbus(self, pl): + return self.player_dbus( + player_name='Spotify', + bus_name='com.spotify.qt', + player_path='/', + iface_prop='org.freedesktop.DBus.Properties', + iface_player='org.freedesktop.MediaPlayer2', + ) + + def player_clementine(self, pl): + return self.player_dbus( + player_name='Clementine', + bus_name='org.mpris.MediaPlayer2.clementine', + player_path='/org/mpris/MediaPlayer2', + iface_prop='org.freedesktop.DBus.Properties', + iface_player='org.mpris.MediaPlayer2.Player', + ) + def player_spotify_apple_script(self, pl): status_delimiter = '-~`/=' ascript = ''' From 04c0030fe1ee78e847385aa2982d5f9e5a7f2d63 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 19:07:59 +0400 Subject: [PATCH 1342/1472] Refactor cwd segment into a class and add shorten_home argument --- powerline/segments/common.py | 146 +++++++++++++++++++++-------------- tests/test_segments.py | 6 ++ 2 files changed, 93 insertions(+), 59 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 95425fcf..72f0cce0 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -82,68 +82,96 @@ def branch(pl, segment_info, create_watcher, status_colors=False): @requires_segment_info -def cwd(pl, segment_info, dir_shorten_len=None, dir_limit_depth=None, use_path_separator=False, ellipsis='⋯'): - '''Return the current working directory. +class CwdSegment(Segment): + def argspecobjs(self): + for obj in super(CwdSegment, self).argspecobjs(): + yield obj + yield 'get_shortened_path', self.get_shortened_path - Returns a segment list to create a breadcrumb-like effect. - - :param int dir_shorten_len: - shorten parent directory names to this length (e.g. - :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) - :param int dir_limit_depth: - limit directory depth to this number (e.g. - :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) - :param bool use_path_separator: - Use path separator in place of soft divider. - :param str ellipsis: - Specifies what to use in place of omitted directories. Use None to not - show this subsegment at all. - - Divider highlight group used: ``cwd:divider``. - - Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. - ''' - try: - cwd = u(segment_info['getcwd']()) - except OSError as e: - if e.errno == 2: - # user most probably deleted the directory - # this happens when removing files from Mercurial repos for example - pl.warn('Current directory not found') - cwd = "[not found]" + def omitted_args(self, name, method): + if method is self.get_shortened_path: + return (0, 1, 2) else: - raise - home = segment_info['home'] - if home: - home = u(home) - cwd = re.sub('^' + re.escape(home), '~', cwd, 1) - cwd_split = cwd.split(os.sep) - cwd_split_len = len(cwd_split) - cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] - if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: - del(cwd[0:-dir_limit_depth]) - if ellipsis is not None: - cwd.insert(0, ellipsis) - ret = [] - if not cwd[0]: - cwd[0] = '/' - draw_inner_divider = not use_path_separator - for part in cwd: - if not part: - continue + return super(CwdSegment, self).omitted_args(name, method) + + def get_shortened_path(self, pl, segment_info, shorten_home=True, **kwargs): + try: + path = u(segment_info['getcwd']()) + except OSError as e: + if e.errno == 2: + # user most probably deleted the directory + # this happens when removing files from Mercurial repos for example + pl.warn('Current directory not found') + return "[not found]" + else: + raise + if shorten_home: + home = segment_info['home'] + if home: + home = u(home) + if path.startswith(home): + path = '~' + path[len(home):] + return path + + def __call__(self, pl, segment_info, + dir_shorten_len=None, + dir_limit_depth=None, + use_path_separator=False, + ellipsis='⋯', + **kwargs): + cwd = self.get_shortened_path(pl, segment_info, **kwargs) + cwd_split = cwd.split(os.sep) + cwd_split_len = len(cwd_split) + cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] + if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: + del(cwd[0:-dir_limit_depth]) + if ellipsis is not None: + cwd.insert(0, ellipsis) + ret = [] + if not cwd[0]: + cwd[0] = '/' + draw_inner_divider = not use_path_separator + for part in cwd: + if not part: + continue + if use_path_separator: + part += os.sep + ret.append({ + 'contents': part, + 'divider_highlight_group': 'cwd:divider', + 'draw_inner_divider': draw_inner_divider, + }) + ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] if use_path_separator: - part += os.sep - ret.append({ - 'contents': part, - 'divider_highlight_group': 'cwd:divider', - 'draw_inner_divider': draw_inner_divider, - }) - ret[-1]['highlight_group'] = ['cwd:current_folder', 'cwd'] - if use_path_separator: - ret[-1]['contents'] = ret[-1]['contents'][:-1] - if len(ret) > 1 and ret[0]['contents'][0] == os.sep: - ret[0]['contents'] = ret[0]['contents'][1:] - return ret + ret[-1]['contents'] = ret[-1]['contents'][:-1] + if len(ret) > 1 and ret[0]['contents'][0] == os.sep: + ret[0]['contents'] = ret[0]['contents'][1:] + return ret + + +cwd = with_docstring(CwdSegment(), +'''Return the current working directory. + +Returns a segment list to create a breadcrumb-like effect. + +:param int dir_shorten_len: + shorten parent directory names to this length (e.g. + :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) +:param int dir_limit_depth: + limit directory depth to this number (e.g. + :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) +:param bool use_path_separator: + Use path separator in place of soft divider. +:param bool shorten_home: + Shorten home directory to ``~``. +:param str ellipsis: + Specifies what to use in place of omitted directories. Use None to not + show this subsegment at all. + +Divider highlight group used: ``cwd:divider``. + +Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. +''') def date(pl, format='%Y-%m-%d', istime=False): diff --git a/tests/test_segments.py b/tests/test_segments.py index 9666ca0b..59c6040a 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -277,6 +277,12 @@ class TestCommon(TestCase): {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3, shorten_home=False), [ + {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), [ {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} From f4e49e2ee6b8ae7b1ab5132b3f900b0002acda54 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 19:13:47 +0400 Subject: [PATCH 1343/1472] Make powerline autodoc add all Segments --- docs/source/powerline_autodoc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 4806c3cf..55e0af35 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -2,7 +2,7 @@ from sphinx.ext import autodoc from inspect import formatargspec from powerline.lint.inspect import getconfigargspec -from powerline.lib.threaded import ThreadedSegment +from powerline.segments import Segment try: from __builtin__ import unicode @@ -21,7 +21,7 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): '''Specialized documenter subclass for ThreadedSegment subclasses.''' @classmethod def can_document_member(cls, member, membername, isattr, parent): - return (isinstance(member, ThreadedSegment) or + return (isinstance(member, Segment) or super(ThreadedDocumenter, cls).can_document_member(member, membername, isattr, parent)) def format_args(self): From 86c3768e4ee73c3a82736b4444929908e0249f2b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 19:19:12 +0400 Subject: [PATCH 1344/1472] Move with_docstring function to powerline.segments --- powerline/lib/threaded.py | 5 ----- powerline/segments/__init__.py | 7 ++++++- powerline/segments/common.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index acb258c3..e4f2f0a4 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -256,8 +256,3 @@ class KwThreadedSegment(ThreadedSegment): @staticmethod def render_one(update_state, **kwargs): return update_state - - -def with_docstring(instance, doc): - instance.__doc__ = doc - return instance diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py index 8199b3cc..0a33bdbc 100644 --- a/powerline/segments/__init__.py +++ b/powerline/segments/__init__.py @@ -40,7 +40,7 @@ class Segment(object): '''List arguments which should be omitted Returns a tuple with indexes of omitted arguments. - + .. note::``segment_info``, ``create_watcher`` and ``pl`` will be omitted regardless of the below return (for ``segment_info`` and ``create_watcher``: only if object was marked to require segment @@ -56,3 +56,8 @@ class Segment(object): '''Returns a list of (additional argument name[, default value]) tuples. ''' return () + + +def with_docstring(instance, doc): + instance.__doc__ = doc + return instance diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 72f0cce0..e10108a4 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -15,12 +15,12 @@ from powerline.lib import add_divider_highlight_group from powerline.lib.shell import asrun, run_cmd from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.vcs import guess, tree_status -from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.unicode import u from powerline.theme import requires_segment_info, requires_filesystem_watcher -from powerline.segments import Segment +from powerline.segments import Segment, with_docstring from collections import namedtuple From d7a674deaac647f620b27e5dda1b80e887abae83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 19:50:37 +0400 Subject: [PATCH 1345/1472] Add support for shell-specific path shortening Fixes #502 --- powerline/bindings/zsh/__init__.py | 1 + powerline/bindings/zsh/powerline.zsh | 7 ++ powerline/config_files/themes/ascii.json | 10 +- powerline/config_files/themes/powerline.json | 10 +- .../config_files/themes/shell/__main__.json | 2 +- .../config_files/themes/shell/default.json | 1 + .../themes/shell/default_leftonly.json | 1 + powerline/config_files/themes/unicode.json | 10 +- .../config_files/themes/unicode_terminus.json | 10 +- .../themes/unicode_terminus_condensed.json | 12 +- powerline/segments/shell.py | 41 +++++++ tests/test_segments.py | 116 ++++++++++++++++++ tests/test_shells/input.zsh | 2 + tests/test_shells/zsh.daemon.ok | Bin 12372 -> 12912 bytes tests/test_shells/zsh.nodaemon.ok | 2 + 15 files changed, 198 insertions(+), 27 deletions(-) diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index ad3fa90b..9fa82794 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -135,6 +135,7 @@ class Prompt(object): 'client_id': 1, 'local_theme': self.theme, 'parser_state': zsh.getvalue('_POWERLINE_PARSER_STATE'), + 'shortened_path': zsh.getvalue('_POWERLINE_SHORTENED_PATH'), } r = '' if self.above: diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 42eafe45..0c097b66 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -103,6 +103,10 @@ _powerline_set_jobnum() { _POWERLINE_JOBNUM=${(%):-%j} } +_powerline_set_shortened_path() { + _POWERLINE_SHORTENED_PATH="${(%):-%~}" +} + _powerline_update_counter() { zpython '_powerline.precmd()' } @@ -116,6 +120,8 @@ _powerline_setup_prompt() { fi done precmd_functions+=( _powerline_set_jobnum ) + chpwd_functions+=( _powerline_set_shortened_path ) + _powerline_set_shortened_path VIRTUAL_ENV_DISABLE_PROMPT=1 @@ -138,6 +144,7 @@ _powerline_setup_prompt() { add_args+=' --last_exit_code=$?' add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' + add_args+=' --renderer_arg="shortened_path=$_POWERLINE_SHORTENED_PATH"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' local new_args_2=' --renderer_arg="parser_state=${(%%):-%_}"' new_args_2+=' --renderer_arg="local_theme=continuation"' diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index ff7c6d76..adacfbfb 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -14,16 +14,16 @@ "branch": { "before": "BR " }, + "cwd": { + "args": { + "ellipsis": "..." + } + }, "line_current_symbol": { "contents": "LN " }, - "powerline.segments.common.cwd": { - "args": { - "ellipsis": "..." - } - }, "powerline.segments.common.network_load": { "args": { "recv_format": "DL {value:>8}", diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index 6fa43e81..e076f770 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -14,16 +14,16 @@ "branch": { "before": " " }, + "cwd": { + "args": { + "ellipsis": "⋯" + } + }, "line_current_symbol": { "contents": " " }, - "powerline.segments.common.cwd": { - "args": { - "ellipsis": "⋯" - } - }, "powerline.segments.common.network_load": { "args": { "recv_format": "⬇ {value:>8}", diff --git a/powerline/config_files/themes/shell/__main__.json b/powerline/config_files/themes/shell/__main__.json index 2b37f8b6..13ae942b 100644 --- a/powerline/config_files/themes/shell/__main__.json +++ b/powerline/config_files/themes/shell/__main__.json @@ -5,7 +5,7 @@ "only_if_ssh": true } }, - "powerline.segments.common.cwd": { + "cwd": { "args": { "dir_limit_depth": 3 } diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 56e3ce70..e4f486a3 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -19,6 +19,7 @@ "priority": 50 }, { + "module": "powerline.segments.shell", "name": "cwd", "priority": 10 }, diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index ca7dd15d..350fb026 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -19,6 +19,7 @@ "priority": 40 }, { + "module": "powerline.segments.shell", "name": "cwd", "priority": 10 }, diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json index 484ac5c2..9a3faf7a 100644 --- a/powerline/config_files/themes/unicode.json +++ b/powerline/config_files/themes/unicode.json @@ -14,16 +14,16 @@ "branch": { "before": "⎇ " }, + "cwd": { + "args": { + "ellipsis": "⋯" + } + }, "line_current_symbol": { "contents": "␤ " }, - "powerline.segments.common.cwd": { - "args": { - "ellipsis": "⋯" - } - }, "powerline.segments.common.network_load": { "args": { "recv_format": "⬇ {value:>8}", diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json index aa9c679d..01b981f2 100644 --- a/powerline/config_files/themes/unicode_terminus.json +++ b/powerline/config_files/themes/unicode_terminus.json @@ -14,16 +14,16 @@ "branch": { "before": "BR " }, + "cwd": { + "args": { + "ellipsis": "…" + } + }, "line_current_symbol": { "contents": "␤ " }, - "powerline.segments.common.cwd": { - "args": { - "ellipsis": "…" - } - }, "powerline.segments.common.network_load": { "args": { "recv_format": "⇓ {value:>8}", diff --git a/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline/config_files/themes/unicode_terminus_condensed.json index ee759316..15f37a1a 100644 --- a/powerline/config_files/themes/unicode_terminus_condensed.json +++ b/powerline/config_files/themes/unicode_terminus_condensed.json @@ -14,17 +14,17 @@ "branch": { "before": "B " }, - - "line_current_symbol": { - "contents": "␤" - }, - - "powerline.segments.common.cwd": { + "cwd": { "args": { "use_path_separator": true, "ellipsis": "…" } }, + + "line_current_symbol": { + "contents": "␤" + }, + "powerline.segments.common.network_load": { "args": { "recv_format": "⇓{value:>8}", diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index a13fa09f..bfd7c9a3 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -1,6 +1,8 @@ # vim:fileencoding=utf-8:noet from powerline.theme import requires_segment_info +from powerline.segments import with_docstring +from powerline.segments.common import CwdSegment @requires_segment_info @@ -120,3 +122,42 @@ def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, rename ret[-1].update(width='auto', align='l', highlight_group=['continuation:current', 'continuation']) return ret + + +@requires_segment_info +class ShellCwdSegment(CwdSegment): + def get_shortened_path(self, pl, segment_info, use_shortened_path=True, **kwargs): + if use_shortened_path: + try: + return segment_info['shortened_path'] + except KeyError: + pass + return super(ShellCwdSegment, self).get_shortened_path(pl, segment_info, **kwargs) + + +cwd = with_docstring(ShellCwdSegment(), +'''Return the current working directory. + +Returns a segment list to create a breadcrumb-like effect. + +:param int dir_shorten_len: + shorten parent directory names to this length (e.g. + :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) +:param int dir_limit_depth: + limit directory depth to this number (e.g. + :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) +:param bool use_path_separator: + Use path separator in place of soft divider. +:param bool use_shortened_path: + Use path from shortened_path ``--renderer_arg`` argument. If this argument + is present ``shorten_home`` argument is ignored. +:param bool shorten_home: + Shorten home directory to ``~``. +:param str ellipsis: + Specifies what to use in place of omitted directories. Use None to not + show this subsegment at all. + +Divider highlight group used: ``cwd:divider``. + +Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. +''') diff --git a/tests/test_segments.py b/tests/test_segments.py index 59c6040a..c46b9216 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -162,6 +162,122 @@ class TestShell(TestCase): }, ]) + def test_cwd(self): + new_os = new_module('os', path=os.path, sep='/') + pl = Pl() + cwd = [None] + + def getcwd(): + wd = cwd[0] + if isinstance(wd, Exception): + raise wd + else: + return wd + + segment_info = {'getcwd': getcwd, 'home': None} + with replace_attr(shell, 'os', new_os): + cwd[0] = '/abc/def/ghi/foo/bar' + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'abc', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'def', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + segment_info['home'] = '/abc/def/ghi' + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + segment_info.update(shortened_path='~foo/ghi') + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info), [ + {'contents': '~foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, use_shortened_path=False), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + segment_info.pop('shortened_path') + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3, shorten_home=False), [ + {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), [ + {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis='...'), [ + {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis=None), [ + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True), [ + {'contents': '⋯/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis='...'), [ + {'contents': '.../', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis=None), [ + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [ + {'contents': '~', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'fo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2, use_path_separator=True), [ + {'contents': '~/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'fo/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} + ]) + cwd[0] = '/etc' + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, use_path_separator=False), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': 'etc', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, use_path_separator=True), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': 'etc', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + cwd[0] = '/' + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, use_path_separator=False), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, use_path_separator=True), [ + {'contents': '/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']}, + ]) + ose = OSError() + ose.errno = 2 + cwd[0] = ose + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2), [ + {'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd'], 'draw_inner_divider': True} + ]) + cwd[0] = OSError() + self.assertRaises(OSError, shell.cwd, pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2) + cwd[0] = ValueError() + self.assertRaises(ValueError, shell.cwd, pl=pl, segment_info=segment_info, dir_limit_depth=2, dir_shorten_len=2) + + def test_date(self): + pl = Pl() + with replace_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))): + self.assertEqual(common.date(pl=pl), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}]) + self.assertEqual(common.date(pl=pl, format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) + class TestCommon(TestCase): def test_hostname(self): diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 228a9ca9..f31af608 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -39,5 +39,7 @@ do break done 1 +hash -d foo=$PWD:h ; cd . +true true is the last line exit diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index d57f21828c6a9bf88c39fb4aeac747aae4a596c6..7151fc4f61565aaf05d1d23297ceb0d2948f20ae 100644 GIT binary patch delta 64 zcmcbT@F8VGh{5C}4VTH4I$tF-5{okwbW;@4^7Cy~0>WLaG8C*8l2a7)CMRmR0#zun S)B&X?E3!wimK2qyasdDk2NnPT delta 7 Ocmey6awTCyhyef*djq`y diff --git a/tests/test_shells/zsh.nodaemon.ok b/tests/test_shells/zsh.nodaemon.ok index c4b4f6d2..9853d9b6 100644 --- a/tests/test_shells/zsh.nodaemon.ok +++ b/tests/test_shells/zsh.nodaemon.ok @@ -33,3 +33,5 @@ abc 1) def 2) ghi 3) jkl  Select variant  1 def + INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd . + INSERT  ~foo  3rd  true From 45d20530530050fb6d9301b6b59906f6d691371b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 19:57:54 +0400 Subject: [PATCH 1346/1472] Do not specify $PYTHON when running python client It is not needed: in the environment where it is run only python is the one linked in tests/shell/path. --- tests/test_shells/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index f4872ccf..f717fb7b 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -255,7 +255,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te for POWERLINE_COMMAND in \ $PWD/scripts/powerline \ $PWD/scripts/powerline-render \ - "$PYTHON $PWD/client/powerline.py" \ + $PWD/client/powerline.py \ $PWD/client/powerline.sh do case "$POWERLINE_COMMAND" in From 7f9eef4ce112475de1e524f475f09d92256f7bdb Mon Sep 17 00:00:00 2001 From: Zero Cho Date: Thu, 30 May 2013 10:50:04 -0700 Subject: [PATCH 1347/1472] Add player support for Rdio Mac app Conflicts: powerline/segments/common.py --- powerline/segments/common.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index e10108a4..f9dd288f 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1221,6 +1221,41 @@ class NowPlayingSegment(Segment): 'elapsed': now_playing[3], 'total': now_playing[4], } + + def player_rdio(self, pl): + now_playing = self._run_cmd(['osascript', + '-e', 'tell application "System Events"', + '-e', 'set rdio_active to the count(every process whose name is "Rdio")', + '-e', 'if rdio_active is 0 then', + '-e', 'return', + '-e', 'end if', + '-e', 'end tell', + '-e', 'tell application "Rdio"', + '-e', 'set rdio_name to the name of the current track', + '-e', 'set rdio_artist to the artist of the current track', + '-e', 'set rdio_album to the album of the current track', + '-e', 'set rdio_duration to the duration of the current track', + '-e', 'set rdio_state to the player state', + '-e', 'set rdio_elapsed to the player position', + '-e', 'return rdio_name & "\n" & rdio_artist & "\n" & rdio_album & "\n" & rdio_elapsed & "\n" & rdio_duration & "\n" & rdio_state', + '-e', 'end tell']) + if not now_playing: + return + now_playing = now_playing.split('\n') + if len(now_playing) != 6: + return + state = self._convert_state(now_playing[5]) + total = self._convert_seconds(now_playing[4]) + elapsed = self._convert_seconds(float(now_playing[3]) * float(now_playing[4]) / 100) + return { + 'title': now_playing[0], + 'artist': now_playing[1], + 'album': now_playing[2], + 'elapsed': elapsed, + 'total': total, + 'state': state, + 'state_symbol': self.STATE_SYMBOLS.get(state) + } now_playing = NowPlayingSegment() From 86c9e998d15f9ec5f350b449a83f5e353aae9042 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 20:17:47 +0400 Subject: [PATCH 1348/1472] Refactor player_rdio to use the same approach spotify uses --- powerline/segments/common.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index f9dd288f..989305db 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1223,22 +1223,25 @@ class NowPlayingSegment(Segment): } def player_rdio(self, pl): - now_playing = self._run_cmd(['osascript', - '-e', 'tell application "System Events"', - '-e', 'set rdio_active to the count(every process whose name is "Rdio")', - '-e', 'if rdio_active is 0 then', - '-e', 'return', - '-e', 'end if', - '-e', 'end tell', - '-e', 'tell application "Rdio"', - '-e', 'set rdio_name to the name of the current track', - '-e', 'set rdio_artist to the artist of the current track', - '-e', 'set rdio_album to the album of the current track', - '-e', 'set rdio_duration to the duration of the current track', - '-e', 'set rdio_state to the player state', - '-e', 'set rdio_elapsed to the player position', - '-e', 'return rdio_name & "\n" & rdio_artist & "\n" & rdio_album & "\n" & rdio_elapsed & "\n" & rdio_duration & "\n" & rdio_state', - '-e', 'end tell']) + status_delimiter = '-~`/=' + ascript = ''' + tell application "System Events" + set rdio_active to the count(every process whose name is "Rdio") + if rdio_active is 0 then + return + end if + end tell + tell application "Rdio" + set rdio_name to the name of the current track + set rdio_artist to the artist of the current track + set rdio_album to the album of the current track + set rdio_duration to the duration of the current track + set rdio_state to the player state + set rdio_elapsed to the player position + return rdio_name & "{0}" & rdio_artist & "{0}" & rdio_album & "{0}" & rdio_elapsed & "{0}" & rdio_duration & "{0}" & rdio_state + end tell + '''.format(status_delimiter) + now_playing = asrun(pl, ascript) if not now_playing: return now_playing = now_playing.split('\n') From a9c397e5e652f64b171e7f1e0f3e0b180bccd6e6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 20:19:28 +0400 Subject: [PATCH 1349/1472] Increase indentation of osascript code in spotify player --- powerline/segments/common.py | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 989305db..6432bffb 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1160,27 +1160,27 @@ class NowPlayingSegment(Segment): def player_spotify_apple_script(self, pl): status_delimiter = '-~`/=' ascript = ''' - tell application "System Events" - set process_list to (name of every process) - end tell - - if process_list contains "Spotify" then - tell application "Spotify" - if player state is playing or player state is paused then - set track_name to name of current track - set artist_name to artist of current track - set album_name to album of current track - set track_length to duration of current track - set now_playing to "" & player state & "{0}" & album_name & "{0}" & artist_name & "{0}" & track_name & "{0}" & track_length - return now_playing - else - return player state - end if - + tell application "System Events" + set process_list to (name of every process) end tell - else - return "stopped" - end if + + if process_list contains "Spotify" then + tell application "Spotify" + if player state is playing or player state is paused then + set track_name to name of current track + set artist_name to artist of current track + set album_name to album of current track + set track_length to duration of current track + set now_playing to "" & player state & "{0}" & album_name & "{0}" & artist_name & "{0}" & track_name & "{0}" & track_length + return now_playing + else + return player state + end if + + end tell + else + return "stopped" + end if '''.format(status_delimiter) spotify = asrun(pl, ascript) From 88515ab472c982fd19edebcfcbde148341557575 Mon Sep 17 00:00:00 2001 From: "Matthew M. Keeler" Date: Sun, 8 Sep 2013 10:29:21 -0500 Subject: [PATCH 1350/1472] Show Count of Attached Tmux Sessions - This segment displays the number of attached tmux clients to the currently running session. - The minimum argument is used to specify a threshold for when the segment should be visible. Fixes #661 Closes #662 Conflicts: docs/source/index.rst powerline/config_files/colorschemes/shell/default.json powerline/config_files/colorschemes/shell/solarized.json powerline/config_files/colorschemes/tmux/default.json powerline/config_files/colorschemes/vim/default.json powerline/config_files/colorschemes/vim/solarized.json powerline/config_files/colorschemes/wm/default.json tests/test_segments.py --- docs/source/configuration/segments/tmux.rst | 6 ++++ .../config_files/colorschemes/default.json | 3 +- .../colorschemes/shell/default.json | 13 +++---- .../config_files/colorschemes/solarized.json | 3 +- .../colorschemes/vim/default.json | 1 + .../colorschemes/vim/solarized.json | 1 + powerline/segments/tmux.py | 34 +++++++++++++++++++ tests/lib/__init__.py | 8 +++++ tests/test_segments.py | 27 ++++++++++++--- 9 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 docs/source/configuration/segments/tmux.rst create mode 100644 powerline/segments/tmux.py diff --git a/docs/source/configuration/segments/tmux.rst b/docs/source/configuration/segments/tmux.rst new file mode 100644 index 00000000..1a4a78fc --- /dev/null +++ b/docs/source/configuration/segments/tmux.rst @@ -0,0 +1,6 @@ +************* +Tmux segments +************* + +.. automodule:: powerline.segments.tmux + :members: diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json index 8aed60a3..7f234506 100644 --- a/powerline/config_files/colorschemes/default.json +++ b/powerline/config_files/colorschemes/default.json @@ -38,6 +38,7 @@ "cwd": { "fg": "gray9", "bg": "gray4", "attr": [] }, "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attr": ["bold"] }, "cwd:divider": { "fg": "gray7", "bg": "gray4", "attr": [] }, - "virtualenv": { "fg": "white", "bg": "darkcyan", "attr": [] } + "virtualenv": { "fg": "white", "bg": "darkcyan", "attr": [] }, + "attached_clients": { "fg": "gray8", "bg": "gray0", "attr": [] } } } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 754addda..bb9e0d59 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -1,12 +1,13 @@ { "name": "Default color scheme for shell prompts", "groups": { - "hostname": { "fg": "brightyellow", "bg": "mediumorange", "attr": [] }, - "jobnum": { "fg": "brightyellow", "bg": "mediumorange", "attr": [] }, - "exit_fail": { "fg": "white", "bg": "darkestred", "attr": [] }, - "exit_success": { "fg": "white", "bg": "darkestgreen", "attr": [] }, - "environment": { "fg": "white", "bg": "darkestgreen", "attr": [] }, - "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } + "hostname": { "fg": "brightyellow", "bg": "mediumorange", "attr": [] }, + "jobnum": { "fg": "brightyellow", "bg": "mediumorange", "attr": [] }, + "exit_fail": { "fg": "white", "bg": "darkestred", "attr": [] }, + "exit_success": { "fg": "white", "bg": "darkestgreen", "attr": [] }, + "environment": { "fg": "white", "bg": "darkestgreen", "attr": [] }, + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, + "attached_clients": { "fg": "white", "bg": "darkestgreen", "attr": [] } }, "mode_translations": { "vicmd": { diff --git a/powerline/config_files/colorschemes/solarized.json b/powerline/config_files/colorschemes/solarized.json index a5ed3f64..c01f2b43 100644 --- a/powerline/config_files/colorschemes/solarized.json +++ b/powerline/config_files/colorschemes/solarized.json @@ -12,6 +12,7 @@ "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, "hostname": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "environment": { "fg": "oldlace", "bg": "green", "attr": [] } + "environment": { "fg": "oldlace", "bg": "green", "attr": [] }, + "attached_clients": { "fg": "oldlace", "bg": "green", "attr": [] } } } diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 3f2afb31..53583b2e 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -27,6 +27,7 @@ "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10", "attr": [] }, "col_current": { "fg": "gray6", "bg": "gray10", "attr": [] }, "modified_buffers": { "fg": "brightyellow", "bg": "gray2", "attr": [] }, + "attached_clients": { "fg": "gray8", "bg": "gray2", "attr": [] }, "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] }, "current_tag": { "fg": "gray9", "bg": "gray2", "attr": [] } diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index f7f393fe..127ed78a 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -28,6 +28,7 @@ "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "lightyellow", "attr": [] }, "col_current": { "fg": "azure4", "bg": "lightyellow", "attr": [] }, "environment": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, + "attached_clients": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, "current_tag": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] } diff --git a/powerline/segments/tmux.py b/powerline/segments/tmux.py new file mode 100644 index 00000000..a1fbd3b6 --- /dev/null +++ b/powerline/segments/tmux.py @@ -0,0 +1,34 @@ +# vim:fileencoding=utf-8:noet + +import os +from subprocess import Popen, PIPE + + +def attached_clients(pl, minimum=1): + '''Return the number of tmux clients attached to the currently active session + + :param int minimum: + The minimum number of attached clients that must be present for this segment to be visible + ''' + try: + with open(os.devnull, "w") as devnull: + find_session_name = ["tmux", "list-panes", "-F", "#{session_name}"] + session_name_process = Popen(find_session_name, stdout=PIPE, stderr=devnull) + session_output, err = session_name_process.communicate() + + if 0 == len(session_output): + return None + + session_name = session_output.rstrip().split(os.linesep)[0] + + find_clients = ["tmux", "list-clients", "-t", session_name] + attached_clients_process = Popen(find_clients, stdout=PIPE, stderr=devnull) + attached_clients_output, err = attached_clients_process.communicate() + + attached_count = len(attached_clients_output.rstrip().split(os.linesep)) + + except Exception as e: + sys.stderr.write('Could not execute attached_clients: ({0})\n'.format(e)) + return None + + return None if attached_count < minimum else str(attached_count) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 641642c2..bd271103 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -44,6 +44,14 @@ def urllib_read(query_url): else: raise NotImplementedError +class Process(object): + def __init__(self, output, err): + self.output = output + self.err = err + + def communicate(self): + return self.output, self.err + class ModuleReplace(object): def __init__(self, name, new): diff --git a/tests/test_segments.py b/tests/test_segments.py index c46b9216..46716519 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -2,13 +2,17 @@ from __future__ import unicode_literals -from powerline.segments import shell, common -from powerline.lib.vcs import get_fallback_create_watcher -import tests.vim as vim_module import sys import os + from functools import partial -from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl + +from powerline.segments import shell, tmux, common +from powerline.lib.vcs import get_fallback_create_watcher + +import tests.vim as vim_module + +from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl, Process from tests import TestCase, SkipTest @@ -279,6 +283,21 @@ class TestShell(TestCase): self.assertEqual(common.date(pl=pl, format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) +class TestTmux(TestCase): + def test_attached_clients(self): + def popen_mock(parameters, **kwargs): + if "list-panes" == parameters[1]: + return Process("session_name", "") + elif "list-clients" == parameters[1]: + clients = ["/dev/pts/2: 0 [191x51 xterm-256color] (utf8)", "/dev/pts/3: 0 [191x51 xterm-256color] (utf8)"] + return Process(os.linesep.join(clients), "") + + pl = Pl() + with replace_attr(tmux, 'Popen', popen_mock): + self.assertEqual(tmux.attached_clients(pl=pl, ), "2") + self.assertEqual(tmux.attached_clients(pl=pl, minimum=3), None) + + class TestCommon(TestCase): def test_hostname(self): pl = Pl() From eee150f97c01b458a205f5e5c448003d51541e8e Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 22 Aug 2014 23:47:13 +0400 Subject: [PATCH 1351/1472] Move some functions to from bindings.config to bindings.tmux --- powerline/bindings/config.py | 47 ++----------------------- powerline/bindings/tmux/__init__.py | 53 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 45 deletions(-) create mode 100644 powerline/bindings/tmux/__init__.py diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index fac81ae0..d5ec8400 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -2,58 +2,15 @@ from __future__ import absolute_import, unicode_literals, print_function -from collections import namedtuple import os -import subprocess import re import sys from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY from powerline.lib.config import ConfigLoader from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config -from powerline.lib.shell import run_cmd, which - - -TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) - - -def get_tmux_executable_name(): - '''Returns tmux executable name - - It should be defined in POWERLINE_TMUX_EXE environment variable, otherwise - it is simply “tmux”. - ''' - - return os.environ.get('POWERLINE_TMUX_EXE', 'tmux') - - -def _run_tmux(runner, args): - return runner([get_tmux_executable_name()] + list(args)) - - -def run_tmux_command(*args): - '''Run tmux command, ignoring the output''' - _run_tmux(subprocess.check_call, args) - - -def get_tmux_output(pl, *args): - '''Run tmux command and return its output''' - return _run_tmux(lambda cmd: run_cmd(pl, cmd), args) - - -NON_DIGITS = re.compile('[^0-9]+') -DIGITS = re.compile('[0-9]+') -NON_LETTERS = re.compile('[^a-z]+') - - -def get_tmux_version(pl): - version_string = get_tmux_output(pl, '-V') - _, version_string = version_string.split(' ') - version_string = version_string.strip() - major, minor = version_string.split('.') - suffix = DIGITS.subn('', minor)[0] or None - minor = NON_DIGITS.subn('', minor)[0] - return TmuxVersionInfo(int(major), int(minor), suffix) +from powerline.lib.shell import which +from powerline.bindings.tmux import TmuxVersionInfo, run_tmux_command, get_tmux_version CONFIG_FILE_NAME = re.compile(r'powerline_tmux_(?P\d+)\.(?P\d+)(?P[a-z]+)?(?:_(?Pplus|minus))?\.conf') diff --git a/powerline/bindings/tmux/__init__.py b/powerline/bindings/tmux/__init__.py new file mode 100644 index 00000000..2adf08f9 --- /dev/null +++ b/powerline/bindings/tmux/__init__.py @@ -0,0 +1,53 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import, unicode_literals, division, print_function + +import re +import os +import subprocess + +from collections import namedtuple + +from powerline.lib.shell import run_cmd + + +TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) + + +def get_tmux_executable_name(): + '''Returns tmux executable name + + It should be defined in POWERLINE_TMUX_EXE environment variable, otherwise + it is simply “tmux”. + ''' + + return os.environ.get('POWERLINE_TMUX_EXE', 'tmux') + + +def _run_tmux(runner, args): + return runner([get_tmux_executable_name()] + list(args)) + + +def run_tmux_command(*args): + '''Run tmux command, ignoring the output''' + _run_tmux(subprocess.check_call, args) + + +def get_tmux_output(pl, *args): + '''Run tmux command and return its output''' + return _run_tmux(lambda cmd: run_cmd(pl, cmd), args) + + +NON_DIGITS = re.compile('[^0-9]+') +DIGITS = re.compile('[0-9]+') +NON_LETTERS = re.compile('[^a-z]+') + + +def get_tmux_version(pl): + version_string = get_tmux_output(pl, '-V') + _, version_string = version_string.split(' ') + version_string = version_string.strip() + major, minor = version_string.split('.') + suffix = DIGITS.subn('', minor)[0] or None + minor = NON_DIGITS.subn('', minor)[0] + return TmuxVersionInfo(int(major), int(minor), suffix) From e4565dd3e87ede4af117bd3ea63f23f234c6c6e0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 21:07:19 +0400 Subject: [PATCH 1352/1472] Make attached_clients segment use provided tmux bindings --- powerline/segments/tmux.py | 28 +++++++--------------------- tests/test_segments.py | 17 ++++++++--------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/powerline/segments/tmux.py b/powerline/segments/tmux.py index a1fbd3b6..dd7d36ae 100644 --- a/powerline/segments/tmux.py +++ b/powerline/segments/tmux.py @@ -1,7 +1,6 @@ # vim:fileencoding=utf-8:noet -import os -from subprocess import Popen, PIPE +from powerline.bindings.tmux import get_tmux_output def attached_clients(pl, minimum=1): @@ -10,25 +9,12 @@ def attached_clients(pl, minimum=1): :param int minimum: The minimum number of attached clients that must be present for this segment to be visible ''' - try: - with open(os.devnull, "w") as devnull: - find_session_name = ["tmux", "list-panes", "-F", "#{session_name}"] - session_name_process = Popen(find_session_name, stdout=PIPE, stderr=devnull) - session_output, err = session_name_process.communicate() - - if 0 == len(session_output): - return None - - session_name = session_output.rstrip().split(os.linesep)[0] - - find_clients = ["tmux", "list-clients", "-t", session_name] - attached_clients_process = Popen(find_clients, stdout=PIPE, stderr=devnull) - attached_clients_output, err = attached_clients_process.communicate() - - attached_count = len(attached_clients_output.rstrip().split(os.linesep)) - - except Exception as e: - sys.stderr.write('Could not execute attached_clients: ({0})\n'.format(e)) + session_output = get_tmux_output('list-panes', '-F', '#{session_name}') + if not session_output: return None + session_name = session_output.rstrip().split('\n')[0] + + attached_clients_output = get_tmux_output('list-clients', '-t', session_name) + attached_count = len(attached_clients_output.rstrip().split('\n')) return None if attached_count < minimum else str(attached_count) diff --git a/tests/test_segments.py b/tests/test_segments.py index 46716519..9320fb52 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -12,7 +12,7 @@ from powerline.lib.vcs import get_fallback_create_watcher import tests.vim as vim_module -from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl, Process +from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl from tests import TestCase, SkipTest @@ -285,16 +285,15 @@ class TestShell(TestCase): class TestTmux(TestCase): def test_attached_clients(self): - def popen_mock(parameters, **kwargs): - if "list-panes" == parameters[1]: - return Process("session_name", "") - elif "list-clients" == parameters[1]: - clients = ["/dev/pts/2: 0 [191x51 xterm-256color] (utf8)", "/dev/pts/3: 0 [191x51 xterm-256color] (utf8)"] - return Process(os.linesep.join(clients), "") + def get_tmux_output(cmd, *args): + if cmd == 'list-panes': + return 'session_name\n' + elif cmd == 'list-clients': + return '/dev/pts/2: 0 [191x51 xterm-256color] (utf8)\n/dev/pts/3: 0 [191x51 xterm-256color] (utf8)' pl = Pl() - with replace_attr(tmux, 'Popen', popen_mock): - self.assertEqual(tmux.attached_clients(pl=pl, ), "2") + with replace_attr(tmux, 'get_tmux_output', get_tmux_output): + self.assertEqual(tmux.attached_clients(pl=pl), '2') self.assertEqual(tmux.attached_clients(pl=pl, minimum=3), None) From dc7f8c22e633f1e68712298925bf73fa16472edf Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 21:07:46 +0400 Subject: [PATCH 1353/1472] Wrap docstring and add dot --- powerline/segments/tmux.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/segments/tmux.py b/powerline/segments/tmux.py index dd7d36ae..ab8e3e95 100644 --- a/powerline/segments/tmux.py +++ b/powerline/segments/tmux.py @@ -7,7 +7,8 @@ def attached_clients(pl, minimum=1): '''Return the number of tmux clients attached to the currently active session :param int minimum: - The minimum number of attached clients that must be present for this segment to be visible + The minimum number of attached clients that must be present for this + segment to be visible. ''' session_output = get_tmux_output('list-panes', '-F', '#{session_name}') if not session_output: From 69939f351cd9c9d555fa1cd091b67314558e862b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 21:08:20 +0400 Subject: [PATCH 1354/1472] Add __future__ import --- powerline/segments/tmux.py | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/segments/tmux.py b/powerline/segments/tmux.py index ab8e3e95..3a027aa4 100644 --- a/powerline/segments/tmux.py +++ b/powerline/segments/tmux.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import absolute_import, unicode_literals, division, print_function from powerline.bindings.tmux import get_tmux_output From abc9c5ef90cb54ca431fb6cb8234059370ef6ae5 Mon Sep 17 00:00:00 2001 From: pearofducks Date: Sat, 20 Apr 2013 21:14:32 +0200 Subject: [PATCH 1355/1472] Add a prefix to solarized color names Conflicts: powerline/config_files/colors.json powerline/config_files/colorschemes/shell/solarized.json powerline/config_files/colorschemes/vim/solarized.json --- powerline/config_files/colors.json | 17 ++ .../colorschemes/shell/solarized.json | 10 +- .../config_files/colorschemes/solarized.json | 26 +-- .../colorschemes/vim/solarized.json | 154 +++++++++--------- .../colorschemes/vim/solarizedlight.json | 154 +++++++++--------- 5 files changed, 189 insertions(+), 172 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 68b69f6e..ecd25277 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -64,6 +64,23 @@ "orange": [9, "cb4b16"], "yellow": [3, "b58900"], + "solarized:base03": [8, "002b36"], + "solarized:base02": [0, "073642"], + "solarized:base01": [10, "586e75"], + "solarized:base00": [11, "657b83"], + "solarized:base0": [12, "839496"], + "solarized:base1": [14, "93a1a1"], + "solarized:base2": [7, "eee8d5"], + "solarized:base3": [15, "fdf6e3"], + "solarized:yellow": [3, "b58900"], + "solarized:orange": [9, "cb4b16"], + "solarized:red": [1, "dc322f"], + "solarized:magenta": [5, "d33682"], + "solarized:violet": [13, "6c71c4"], + "solarized:blue": [4, "268bd2"], + "solarized:cyan": [6, "2aa198"], + "solarized:green": [2, "859900"], + "lightyellowgreen": 106, "gold3": 178, "orangered": 202, diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index e7f79569..e9059bd6 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,15 +1,15 @@ { "name": "Solarized dark for shell", "groups": { - "jobnum": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "exit_fail": { "fg": "oldlace", "bg": "red", "attr": [] }, - "exit_success": { "fg": "oldlace", "bg": "green", "attr": [] }, - "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] } + "jobnum": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": [] }, + "exit_fail": { "fg": "solarized:base3", "bg": "solarized:red", "attr": [] }, + "exit_success": { "fg": "solarized:base3", "bg": "solarized:green", "attr": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attr": ["bold"] } }, "mode_translations": { "vicmd": { "groups": { - "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attr": ["bold"] } } } } diff --git a/powerline/config_files/colorschemes/solarized.json b/powerline/config_files/colorschemes/solarized.json index c01f2b43..9c015ccd 100644 --- a/powerline/config_files/colorschemes/solarized.json +++ b/powerline/config_files/colorschemes/solarized.json @@ -1,18 +1,18 @@ { "name": "Solarized dark", "groups": { - "background": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, - "user": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, - "superuser": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, - "virtualenv": { "fg": "oldlace", "bg": "green", "attr": [] }, - "branch": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "branch_dirty": { "fg": "yellow", "bg": "royalblue5", "attr": [] }, - "branch_clean": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "cwd": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, - "cwd:current_folder": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "cwd:divider": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, - "hostname": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "environment": { "fg": "oldlace", "bg": "green", "attr": [] }, - "attached_clients": { "fg": "oldlace", "bg": "green", "attr": [] } + "background": { "fg": "solarized:base3", "bg": "solarized:base02", "attr": [] }, + "user": { "fg": "solarized:base3", "bg": "solarized:blue", "attr": ["bold"] }, + "superuser": { "fg": "solarized:base3", "bg": "solarized:red", "attr": ["bold"] }, + "virtualenv": { "fg": "solarized:base3", "bg": "solarized:green", "attr": [] }, + "branch": { "fg": "solarized:base1", "bg": "solarized:base02", "attr": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base02", "attr": [] }, + "branch_clean": { "fg": "solarized:base1", "bg": "solarized:base02", "attr": [] }, + "cwd": { "fg": "solarized:base2", "bg": "solarized:base01", "attr": [] }, + "cwd:current_folder": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": ["bold"] }, + "cwd:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attr": [] }, + "hostname": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": [] }, + "environment": { "fg": "solarized:base3", "bg": "solarized:green", "attr": [] }, + "attached_clients": { "fg": "solarized:base3", "bg": "solarized:green", "attr": [] } } } diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 127ed78a..63038fbc 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -1,115 +1,115 @@ { "name": "Solarized dark for vim", "groups": { - "information:additional": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, - "information:unimportant": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "background": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, - "background:divider": { "fg": "lightskyblue4", "bg": "royalblue5", "attr": [] }, - "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, - "visual_range": { "fg": "green", "bg": "oldlace", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "darkgreencopper", "attr": ["bold"] }, - "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "darkgreencopper", "attr": [] }, - "branch_dirty": { "fg": "yellow", "bg": "darkgreencopper", "attr": [] }, - "branch:divider": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, - "file_name": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "window_title": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "file_name_no_file": { "fg": "oldlace", "bg": "darkgreencopper", "attr": ["bold"] }, - "file_format": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "file_vcs_status": { "fg": "red", "bg": "darkgreencopper", "attr": [] }, - "file_vcs_status_M": { "fg": "yellow", "bg": "darkgreencopper", "attr": [] }, - "file_vcs_status_A": { "fg": "green", "bg": "darkgreencopper", "attr": [] }, - "line_percent": { "fg": "oldlace", "bg": "lightskyblue4", "attr": [] }, - "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4", "attr": [] }, - "position": { "fg": "oldlace", "bg": "lightskyblue4", "attr": [] }, - "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightskyblue4", "attr": [] }, - "line_current": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "lightyellow", "attr": [] }, - "col_current": { "fg": "azure4", "bg": "lightyellow", "attr": [] }, - "environment": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "attached_clients": { "fg": "gray61", "bg": "royalblue5", "attr": [] }, - "error": { "fg": "oldlace", "bg": "red", "attr": ["bold"] }, - "warning": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "current_tag": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] } + "information:additional": { "fg": "solarized:base2", "bg": "solarized:base01", "attr": [] }, + "information:unimportant": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": [] }, + "background": { "fg": "solarized:base3", "bg": "solarized:base02", "attr": [] }, + "background:divider": { "fg": "solarized:base00", "bg": "solarized:base02", "attr": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attr": ["bold"] }, + "visual_range": { "fg": "solarized:green", "bg": "solarized:base3", "attr": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base01", "attr": ["bold"] }, + "paste_indicator": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base01", "attr": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base01", "attr": [] }, + "branch:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attr": [] }, + "file_name": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": ["bold"] }, + "window_title": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": [] }, + "file_name_no_file": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": ["bold"] }, + "file_format": { "fg": "solarized:base1", "bg": "solarized:base02", "attr": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base01", "attr": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base01", "attr": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base01", "attr": [] }, + "line_percent": { "fg": "solarized:base3", "bg": "solarized:base00", "attr": [] }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base00", "attr": [] }, + "position": { "fg": "solarized:base3", "bg": "solarized:base00", "attr": [] }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base00", "attr": [] }, + "line_current": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "solarized:base2", "attr": [] }, + "col_current": { "fg": "solarized:base0", "bg": "solarized:base2", "attr": [] }, + "environment": { "fg": "solarized:base1", "bg": "solarized:base02", "attr": [] }, + "attached_clients": { "fg": "solarized:base1", "bg": "solarized:base02", "attr": [] }, + "error": { "fg": "solarized:base3", "bg": "solarized:red", "attr": ["bold"] }, + "warning": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] }, + "current_tag": { "fg": "solarized:base3", "bg": "solarized:base02", "attr": ["bold"] } }, "mode_translations": { "nc": { "colors": { - "darkgreencopper": "royalblue5", - "lightskyblue4": "royalblue5", - "azure4": "darkgreencopper", - "gray61": "lightskyblue4", - "lightyellow": "azure4", - "oldlace": "gray61" + "solarized:base01": "solarized:base02", + "solarized:base00": "solarized:base02", + "solarized:base0": "solarized:base01", + "solarized:base1": "solarized:base00", + "solarized:base2": "solarized:base0", + "solarized:base3": "solarized:base1" } }, "tab_nc": { "colors": { - "darkgreencopper": "royalblue5", - "lightskyblue4": "royalblue5", - "azure4": "darkgreencopper", - "gray61": "lightskyblue4", - "lightyellow": "azure4", - "oldlace": "gray61" + "solarized:base01": "solarized:base02", + "solarized:base00": "solarized:base02", + "solarized:base0": "solarized:base01", + "solarized:base1": "solarized:base00", + "solarized:base2": "solarized:base0", + "solarized:base3": "solarized:base1" } }, "buf_nc": { "colors": { - "darkgreencopper": "royalblue5", - "lightskyblue4": "royalblue5", - "azure4": "darkgreencopper", - "gray61": "lightskyblue4", - "lightyellow": "azure4", - "oldlace": "gray61" + "solarized:base01": "solarized:base02", + "solarized:base00": "solarized:base02", + "solarized:base0": "solarized:base01", + "solarized:base1": "solarized:base00", + "solarized:base2": "solarized:base0", + "solarized:base3": "solarized:base1" } }, "i": { "groups": { - "background": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, - "background:divider": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, - "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, - "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "lightyellow", "attr": [] }, - "branch": { "fg": "darkgreencopper", "bg": "lightyellow", "attr": [] }, - "branch:divider": { "fg": "lightskyblue4", "bg": "lightyellow", "attr": [] }, - "file_directory": { "fg": "darkgreencopper", "bg": "lightyellow", "attr": [] }, - "file_name": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, - "file_size": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "file_name_no_file": { "fg": "royalblue5", "bg": "lightyellow", "attr": ["bold"] }, - "file_name_empty": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "file_format": { "fg": "lightyellow", "bg": "darkgreencopper", "attr": [] }, - "file_vcs_status": { "fg": "red", "bg": "lightyellow", "attr": [] }, - "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, - "file_vcs_status_A": { "fg": "green", "bg": "lightyellow", "attr": [] }, - "line_percent": { "fg": "oldlace", "bg": "gray61", "attr": [] }, - "line_percent_gradient": { "fg": "oldlace", "bg": "gray61", "attr": [] }, - "position": { "fg": "oldlace", "bg": "gray61", "attr": [] }, - "position_gradient": { "fg": "oldlace", "bg": "gray61", "attr": [] }, - "line_current": { "fg": "gray13", "bg": "oldlace", "attr": ["bold"] }, - "line_current_symbol": { "fg": "gray13", "bg": "oldlace", "attr": [] }, - "col_current": { "fg": "azure4", "bg": "oldlace", "attr": [] } + "background": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": [] }, + "background:divider": { "fg": "solarized:base2", "bg": "solarized:base01", "attr": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attr": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base2", "attr": ["bold"] }, + "paste_indicator": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base2", "attr": [] }, + "branch": { "fg": "solarized:base01", "bg": "solarized:base2", "attr": [] }, + "branch:divider": { "fg": "solarized:base00", "bg": "solarized:base2", "attr": [] }, + "file_directory": { "fg": "solarized:base01", "bg": "solarized:base2", "attr": [] }, + "file_name": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": ["bold"] }, + "file_size": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": [] }, + "file_name_no_file": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": ["bold"] }, + "file_name_empty": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": [] }, + "file_format": { "fg": "solarized:base2", "bg": "solarized:base01", "attr": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base2", "attr": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base2", "attr": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base2", "attr": [] }, + "line_percent": { "fg": "solarized:base3", "bg": "solarized:base1", "attr": [] }, + "line_percent_gradient": { "fg": "solarized:base3", "bg": "solarized:base1", "attr": [] }, + "position": { "fg": "solarized:base3", "bg": "solarized:base1", "attr": [] }, + "position_gradient": { "fg": "solarized:base3", "bg": "solarized:base1", "attr": [] }, + "line_current": { "fg": "solarized:base03", "bg": "solarized:base3", "attr": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base03", "bg": "solarized:base3", "attr": [] }, + "col_current": { "fg": "solarized:base0", "bg": "solarized:base3", "attr": [] } } }, "v": { "groups": { - "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] } } }, "V": { "groups": { - "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] } } }, "^V": { "groups": { - "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] } } }, "R": { "groups": { - "mode": { "fg": "oldlace", "bg": "red", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attr": ["bold"] } } } } diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index cab94968..da38bb88 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -1,115 +1,115 @@ { "name": "Solarized light for vim", "groups": { - "information:additional": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "information:unimportant": { "fg": "gray61", "bg": "darkgreencopper", "attr": [] }, - "background": { "fg": "gray13", "bg": "darkgreencopper", "attr": [] }, - "background:divider": { "fg": "azure4", "bg": "darkgreencopper", "attr": [] }, - "mode": { "fg": "oldlace", "bg": "green", "attr": ["bold"] }, - "visual_range": { "fg": "green", "bg": "oldlace", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "lightyellow", "attr": ["bold"] }, - "paste_indicator": { "fg": "red", "bg": "lightyellow", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "lightyellow", "attr": [] }, - "branch_dirty": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, - "branch:divider": { "fg": "gray61", "bg": "lightyellow", "attr": [] }, - "file_name": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "window_title": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "file_size": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "file_name_no_file": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "file_name_empty": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "file_vcs_status": { "fg": "red", "bg": "lightyellow", "attr": [] }, - "file_vcs_status_M": { "fg": "yellow", "bg": "lightyellow", "attr": [] }, - "file_vcs_status_A": { "fg": "green", "bg": "lightyellow", "attr": [] }, - "line_percent": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow", "attr": [] }, - "position": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "position_gradient": { "fg": "green_yellow_orange_red", "bg": "lightyellow", "attr": [] }, - "line_current": { "fg": "oldlace", "bg": "royalblue5", "attr": ["bold"] }, - "line_current_symbol": { "fg": "oldlace", "bg": "royalblue5", "attr": [] }, - "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "royalblue5", "attr": [] }, - "col_current": { "fg": "lightskyblue4", "bg": "royalblue5", "attr": [] }, - "error": { "fg": "gray13", "bg": "red", "attr": ["bold"] }, - "warning": { "fg": "gray13", "bg": "lightyellow", "attr": ["bold"] }, - "current_tag": { "fg": "gray13", "bg": "darkgreencopper", "attr": ["bold"] } + "information:additional": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": [] }, + "information:unimportant": { "fg": "solarized:base1", "bg": "solarized:base01", "attr": [] }, + "background": { "fg": "solarized:base03", "bg": "solarized:base01", "attr": [] }, + "background:divider": { "fg": "solarized:base0", "bg": "solarized:base01", "attr": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attr": ["bold"] }, + "visual_range": { "fg": "solarized:green", "bg": "solarized:base3", "attr": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base2", "attr": ["bold"] }, + "paste_indicator": { "fg": "solarized:red", "bg": "solarized:base2", "attr": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base2", "attr": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base2", "attr": [] }, + "branch:divider": { "fg": "solarized:base1", "bg": "solarized:base2", "attr": [] }, + "file_name": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": ["bold"] }, + "window_title": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "file_size": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "file_name_no_file": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": ["bold"] }, + "file_name_empty": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base2", "attr": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base2", "attr": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base2", "attr": [] }, + "line_percent": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base2", "attr": [] }, + "position": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base2", "attr": [] }, + "line_current": { "fg": "solarized:base3", "bg": "solarized:base02", "attr": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base3", "bg": "solarized:base02", "attr": [] }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "solarized:base02", "attr": [] }, + "col_current": { "fg": "solarized:base00", "bg": "solarized:base02", "attr": [] }, + "error": { "fg": "solarized:base03", "bg": "solarized:red", "attr": ["bold"] }, + "warning": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": ["bold"] }, + "current_tag": { "fg": "solarized:base03", "bg": "solarized:base01", "attr": ["bold"] } }, "mode_translations": { "nc": { "colors": { - "lightyellow": "darkgreencopper", - "azure4": "darkgreencopper", - "lightskyblue4": "lightyellow", - "gray61": "azure4", - "royalblue5": "lightskyblue4", - "gray13": "gray61" + "solarized:base2": "solarized:base01", + "solarized:base0": "solarized:base01", + "solarized:base00": "solarized:base2", + "solarized:base1": "solarized:base0", + "solarized:base02": "solarized:base00", + "solarized:base03": "solarized:base1" } }, "tab_nc": { "colors": { - "lightyellow": "darkgreencopper", - "azure4": "darkgreencopper", - "lightskyblue4": "lightyellow", - "gray61": "azure4", - "royalblue5": "lightskyblue4", - "gray13": "gray61" + "solarized:base2": "solarized:base01", + "solarized:base0": "solarized:base01", + "solarized:base00": "solarized:base2", + "solarized:base1": "solarized:base0", + "solarized:base02": "solarized:base00", + "solarized:base03": "solarized:base1" } }, "buf_nc": { "colors": { - "lightyellow": "darkgreencopper", - "azure4": "darkgreencopper", - "lightskyblue4": "lightyellow", - "gray61": "azure4", - "royalblue5": "lightskyblue4", - "gray13": "gray61" + "solarized:base2": "solarized:base01", + "solarized:base0": "solarized:base01", + "solarized:base00": "solarized:base2", + "solarized:base1": "solarized:base0", + "solarized:base02": "solarized:base00", + "solarized:base03": "solarized:base1" } }, "i": { "groups": { - "background": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, - "background:divider": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "mode": { "fg": "oldlace", "bg": "blue", "attr": ["bold"] }, - "modified_indicator": { "fg": "yellow", "bg": "royalblue5", "attr": ["bold"] }, - "paste_indicator": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] }, - "readonly_indicator": { "fg": "red", "bg": "royalblue5", "attr": [] }, - "branch": { "fg": "lightyellow", "bg": "royalblue5", "attr": [] }, - "branch:divider": { "fg": "azure4", "bg": "royalblue5", "attr": [] }, - "file_directory": { "fg": "lightyellow", "bg": "royalblue5", "attr": [] }, - "file_name": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, - "file_size": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": [] }, - "file_name_no_file": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": ["bold"] }, - "file_name_empty": { "fg": "darkgreencopper", "bg": "royalblue5", "attr": [] }, - "file_format": { "fg": "royalblue5", "bg": "lightyellow", "attr": [] }, - "file_vcs_status": { "fg": "red", "bg": "royalblue5", "attr": [] }, - "file_vcs_status_M": { "fg": "yellow", "bg": "royalblue5", "attr": [] }, - "file_vcs_status_A": { "fg": "green", "bg": "royalblue5", "attr": [] }, - "line_percent": { "fg": "gray13", "bg": "gray61", "attr": [] }, - "line_percent_gradient": { "fg": "gray13", "bg": "gray61", "attr": [] }, - "position": { "fg": "gray13", "bg": "gray61", "attr": [] }, - "position_gradient": { "fg": "gray13", "bg": "gray61", "attr": [] }, - "line_current": { "fg": "oldlace", "bg": "gray13", "attr": ["bold"] }, - "line_current_symbol": { "fg": "oldlace", "bg": "gray13", "attr": [] }, - "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "gray13", "attr": [] }, - "col_current": { "fg": "lightskyblue4", "bg": "gray13", "attr": [] } + "background": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, + "background:divider": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attr": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base02", "attr": ["bold"] }, + "paste_indicator": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base02", "attr": [] }, + "branch": { "fg": "solarized:base2", "bg": "solarized:base02", "attr": [] }, + "branch:divider": { "fg": "solarized:base0", "bg": "solarized:base02", "attr": [] }, + "file_directory": { "fg": "solarized:base2", "bg": "solarized:base02", "attr": [] }, + "file_name": { "fg": "solarized:base01", "bg": "solarized:base02", "attr": ["bold"] }, + "file_size": { "fg": "solarized:base01", "bg": "solarized:base02", "attr": [] }, + "file_name_no_file": { "fg": "solarized:base01", "bg": "solarized:base02", "attr": ["bold"] }, + "file_name_empty": { "fg": "solarized:base01", "bg": "solarized:base02", "attr": [] }, + "file_format": { "fg": "solarized:base02", "bg": "solarized:base2", "attr": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base02", "attr": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base02", "attr": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base02", "attr": [] }, + "line_percent": { "fg": "solarized:base03", "bg": "solarized:base1", "attr": [] }, + "line_percent_gradient": { "fg": "solarized:base03", "bg": "solarized:base1", "attr": [] }, + "position": { "fg": "solarized:base03", "bg": "solarized:base1", "attr": [] }, + "position_gradient": { "fg": "solarized:base03", "bg": "solarized:base1", "attr": [] }, + "line_current": { "fg": "solarized:base3", "bg": "solarized:base03", "attr": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base3", "bg": "solarized:base03", "attr": [] }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "solarized:base03", "attr": [] }, + "col_current": { "fg": "solarized:base00", "bg": "solarized:base03", "attr": [] } } }, "v": { "groups": { - "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] } } }, "V": { "groups": { - "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] } } }, "^V": { "groups": { - "mode": { "fg": "oldlace", "bg": "orange", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attr": ["bold"] } } }, "R": { "groups": { - "mode": { "fg": "oldlace", "bg": "red", "attr": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attr": ["bold"] } } } } From a70ec39a1f63d38fb5e9317f0f5a164ce7c2d08b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 21:55:13 +0400 Subject: [PATCH 1356/1472] Make powerline-lint support `id:id` groups --- powerline/lint/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index d9eb1112..f495cc18 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -315,9 +315,9 @@ class Spec(object): def ident(self, msg_func=None): msg_func = ( msg_func - or (lambda value: 'String "{0}" is not an alphanumeric/underscore identifier'.format(value)) + or (lambda value: 'String "{0}" is not an alphanumeric/underscore colon-separated identifier'.format(value)) ) - return self.re('^\w+$', msg_func) + return self.re('^\w+(?::\w+)?$', msg_func) def oneof(self, collection, msg_func=None): msg_func = msg_func or (lambda value: '"{0}" must be one of {1!r}'.format(value, list(collection))) @@ -701,7 +701,7 @@ def check_group(group, data, context, echoerr): color_spec = Spec().type(unicode).func(check_color).copy name_spec = Spec().type(unicode).len('gt', 0).optional().copy -group_name_spec = Spec().re('^\w+(?::\w+)?$').copy +group_name_spec = Spec().ident().copy group_spec = Spec().either(Spec( fg=color_spec(), bg=color_spec(), From 9d63fb42d1d4f5275fb7b7634b26fed38f997b06 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 22:00:36 +0400 Subject: [PATCH 1357/1472] Remove unneeded colors --- powerline/config_files/colors.json | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index ecd25277..31ce92d3 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -16,6 +16,7 @@ "darkestblue": 24, "darkblue": 31, + "red": 1, "darkestred": 52, "darkred": 88, "mediumred": 124, @@ -45,25 +46,6 @@ "gray9": 250, "gray10": 252, - "gray61": [14, "93a1a1"], - "gray13": [8, "002b36"], - - "royalblue5": [0, "073642"], - "darkgreencopper": [10, "586e75"], - "lightskyblue4": [11, "657b83"], - "azure4": [12, "839496"], - "lightyellow": [7, "eee8d5"], - "oldlace": [15, "fdf6e3"], - - "green": [2, "719e07"], - "cyan": [6, "2aa198"], - "blue": [4, "268bd2"], - "red": [1, "dc322f"], - "magenta": [5, "d33682"], - "violet": [13, "6c71c4"], - "orange": [9, "cb4b16"], - "yellow": [3, "b58900"], - "solarized:base03": [8, "002b36"], "solarized:base02": [0, "073642"], "solarized:base01": [10, "586e75"], From 407014417fe5c2557c5e8bea048b0b0a412a3577 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 22:01:10 +0400 Subject: [PATCH 1358/1472] Move solarized colors below other colors --- powerline/config_files/colors.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/powerline/config_files/colors.json b/powerline/config_files/colors.json index 31ce92d3..6bfadf7b 100644 --- a/powerline/config_files/colors.json +++ b/powerline/config_files/colors.json @@ -46,6 +46,15 @@ "gray9": 250, "gray10": 252, + "lightyellowgreen": 106, + "gold3": 178, + "orangered": 202, + + "steelblue": 67, + "darkorange3": 166, + "skyblue1": 117, + "khaki1": 228, + "solarized:base03": [8, "002b36"], "solarized:base02": [0, "073642"], "solarized:base01": [10, "586e75"], @@ -61,16 +70,7 @@ "solarized:violet": [13, "6c71c4"], "solarized:blue": [4, "268bd2"], "solarized:cyan": [6, "2aa198"], - "solarized:green": [2, "859900"], - - "lightyellowgreen": 106, - "gold3": 178, - "orangered": 202, - - "steelblue": 67, - "darkorange3": 166, - "skyblue1": 117, - "khaki1": 228 + "solarized:green": [2, "859900"] }, "gradients": { "dark_GREEN_Orange_red": [ From 242a6a0f120e8823952df075462338597f83c966 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 22:19:10 +0400 Subject: [PATCH 1359/1472] Add `None` mark to module object Applicable only when it fallbacks to the default value. This may output shown by travis more informative. And in any case old code contained a bug. --- powerline/lint/__init__.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index f495cc18..89027a3a 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,21 +1,24 @@ # vim:fileencoding=utf-8:noet +import itertools +import sys +import os +import re +import logging + +from collections import defaultdict +from copy import copy + from powerline.lint.markedjson import load from powerline import generate_config_finder, get_config_paths, load_config from powerline.lib.config import ConfigLoader from powerline.lint.markedjson.error import echoerr, MarkedError +from powerline.lint.markedjson.markedvalue import MarkedUnicode from powerline.segments.vim import vim_modes from powerline.lint.inspect import getconfigargspec from powerline.lib.threaded import ThreadedSegment from powerline.lib import mergedicts_copy from powerline.lib.unicode import unicode -import itertools -import sys -import os -import re -from collections import defaultdict -from copy import copy -import logging def open_file(path): @@ -875,13 +878,17 @@ def check_full_segment_data(segment, data, context, echoerr): def import_segment(name, data, context, echoerr, module=None): if not module: - module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext'])) + module = context[-2][1].get( + 'module', context[0][1].get( + 'default_module', MarkedUnicode( + 'powerline.segments.' + data['ext'], None))) with WithPath(data['import_paths']): try: func = getattr(__import__(str(module), fromlist=[str(name)]), str(name)) except ImportError: echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=name.mark, problem='failed to import module {0}'.format(module), problem_mark=module.mark) return None @@ -893,6 +900,7 @@ def import_segment(name, data, context, echoerr, module=None): if not callable(func): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), + context_mark=name.mark, problem='imported "function" {0} from module {1} is not callable'.format(name, module), problem_mark=module.mark) return None From 26cc26a69a0f4d09c417692d2c40c7927b52e114 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 23:04:50 +0400 Subject: [PATCH 1360/1472] Mark default value of default_module value --- powerline/lint/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 89027a3a..fbc82331 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1223,7 +1223,8 @@ def get_all_possible_functions(data, context, echoerr): if segment.get('type', 'function') == 'function': module = segment.get( 'module', - theme_config.get('default_module', 'powerline.segments.' + data['ext']) + theme_config.get('default_module', MarkedUnicode( + 'powerline.segments.' + data['ext'], None)) ) func = import_segment(name, data, context, echoerr, module=module) if func: From 8b30e9d33ebacec02c3d1ea16ff6cb1468ddc5ca Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 23:06:30 +0400 Subject: [PATCH 1361/1472] Also in another place, just in case --- powerline/lint/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index fbc82331..9dda3c0a 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -855,7 +855,8 @@ def check_full_segment_data(segment, data, context, echoerr): names = [segment['name']] if segment.get('type', 'function') == 'function': - module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.' + ext)) + module = segment.get('module', context[0][1].get('default_module', MarkedUnicode( + 'powerline.segments.' + ext, None))) names.insert(0, unicode(module) + '.' + unicode(names[0])) segment_copy = segment.copy() From b5fae89d6c11cb20eb3d51276c4628600844e534 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 23:18:44 +0400 Subject: [PATCH 1362/1472] Check whether all values that should have mark attribute have it --- powerline/lint/__init__.py | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 9dda3c0a..49e03d96 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -41,6 +41,20 @@ def context_key(context): return key_sep.join((c[0] for c in context)) +def havemarks(*args): + for i, v in enumerate(args): + if not hasattr(v, 'mark'): + raise AssertionError('Value #{0} has no attribute `mark`'.format(i)) + + +def context_has_marks(context): + for i, v in enumerate(context): + if not hasattr(v[0], 'mark'): + raise AssertionError('Key #{0} in context has no attribute `mark`'.format(i)) + if not hasattr(v[1], 'mark'): + raise AssertionError('Value #{0} in context has no attribute `mark`'.format(i)) + + class EchoErr(object): __slots__ = ('echoerr', 'logger',) @@ -130,6 +144,7 @@ class Spec(object): return self def check_type(self, value, context_mark, data, context, echoerr, types): + havemarks(value) if type(value.value) not in types: echoerr( context=self.cmsg.format(key=context_key(context)), @@ -145,6 +160,7 @@ class Spec(object): return True, False def check_func(self, value, context_mark, data, context, echoerr, func, msg_func): + havemarks(value) proceed, echo, hadproblem = func(value, data, context, echoerr) if echo and hadproblem: echoerr(context=self.cmsg.format(key=context_key(context)), @@ -154,9 +170,11 @@ class Spec(object): return proceed, hadproblem def check_list(self, value, context_mark, data, context, echoerr, item_func, msg_func): + havemarks(value) i = 0 hadproblem = False for item in value: + havemarks(item) if isinstance(item_func, int): spec = self.specs[item_func] proceed, fhadproblem = spec.match( @@ -181,6 +199,7 @@ class Spec(object): return True, hadproblem def check_either(self, value, context_mark, data, context, echoerr, start, end): + havemarks(value) new_echoerr = DelayedEchoErr(echoerr) hadproblem = False @@ -196,6 +215,7 @@ class Spec(object): return False, hadproblem def check_tuple(self, value, context_mark, data, context, echoerr, start, end): + havemarks(value) hadproblem = False for (i, item, spec) in zip(itertools.count(), value, self.specs[start:end]): proceed, ihadproblem = spec.match( @@ -360,6 +380,7 @@ class Spec(object): return True, hadproblem def match(self, value, context_mark=None, data=None, context=EMPTYTUPLE, echoerr=echoerr): + havemarks(value) proceed, hadproblem = self.match_checks(value, context_mark, data, context, echoerr) if proceed: if self.keys or self.uspecs: @@ -385,6 +406,7 @@ class Spec(object): problem='required key is missing: {0}'.format(key), problem_mark=value.mark) for key in value.keys(): + havemarks(key) if key not in self.keys: for keyfunc, vali in self.uspecs: valspec = self.specs[vali] @@ -429,6 +451,7 @@ class WithPath(object): def check_matcher_func(ext, match_name, data, context, echoerr): + havemarks(match_name) import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])] match_module, separator, match_function = match_name.rpartition('.') @@ -470,6 +493,7 @@ def check_matcher_func(ext, match_name, data, context, echoerr): def check_ext(ext, data, context, echoerr): + havemarks(ext) hadsomedirs = False hadproblem = False if ext not in data['lists']['exts']: @@ -490,6 +514,7 @@ def check_ext(ext, data, context, echoerr): def check_config(d, theme, data, context, echoerr): + context_has_marks(context) if len(context) == 4: ext = context[-2][0] else: @@ -512,6 +537,8 @@ def check_config(d, theme, data, context, echoerr): def check_top_theme(theme, data, context, echoerr): + context_has_marks(context) + havemarks(theme) if theme not in data['configs']['top_themes']: echoerr(context='Error while checking extension configuration (key {key})'.format(key=context_key(context)), context_mark=context[-2][0].mark, @@ -615,6 +642,7 @@ colors_spec = (Spec( def check_color(color, data, context, echoerr): + havemarks(color) if (color not in data['colors_config'].get('colors', {}) and color not in data['colors_config'].get('gradients', {})): echoerr( @@ -632,6 +660,7 @@ def check_translated_group_name(group, data, context, echoerr): def check_group(group, data, context, echoerr): + havemarks(group) if not isinstance(group, unicode): return True, False, False colorscheme = data['colorscheme'] @@ -671,6 +700,7 @@ def check_group(group, data, context, echoerr): new_data['colorscheme'] = new_colorscheme else: new_data = data + havemarks(config) try: group_data = config['groups'][group] except KeyError: @@ -781,7 +811,10 @@ highlight_keys = set(('highlight_group', 'name')) def check_key_compatibility(segment, data, context, echoerr): + context_has_marks(context) + havemarks(segment) segment_type = segment.get('type', 'function') + havemarks(segment_type) if segment_type not in type_keys: echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), @@ -828,6 +861,7 @@ def check_key_compatibility(segment, data, context, echoerr): def check_segment_module(module, data, context, echoerr): + havemarks(module) with WithPath(data['import_paths']): try: __import__(str(module)) @@ -878,11 +912,14 @@ def check_full_segment_data(segment, data, context, echoerr): def import_segment(name, data, context, echoerr, module=None): + context_has_marks(context) + havemarks(name) if not module: module = context[-2][1].get( 'module', context[0][1].get( 'default_module', MarkedUnicode( 'powerline.segments.' + data['ext'], None))) + havemarks(module) with WithPath(data['import_paths']): try: @@ -910,6 +947,7 @@ def import_segment(name, data, context, echoerr, module=None): def check_segment_name(name, data, context, echoerr): + havemarks(name) ext = data['ext'] if context[-2][1].get('type', 'function') == 'function': func = import_segment(name, data, context, echoerr) @@ -1007,6 +1045,7 @@ def check_segment_name(name, data, context, echoerr): def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): + havemarks(hl_group) ext = data['ext'] if ext not in data['colorscheme_configs']: # No colorschemes. Error was already reported, no need to report it @@ -1018,12 +1057,14 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): r.append(colorscheme) elif not allow_gradients or allow_gradients == 'force': group_config = cconfig['groups'][hl_group] + havemarks(group_config) hadgradient = False for ckey in ('fg', 'bg'): color = group_config.get(ckey) if not color: # No color. Error was already reported. continue + havemarks(color) # Gradients are only allowed for function segments. Note that # whether *either* color or gradient exists should have been # already checked @@ -1054,6 +1095,7 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): def check_highlight_group(hl_group, data, context, echoerr): + havemarks(hl_group) r = hl_exists(hl_group, data, context, echoerr) if r: echoerr( @@ -1067,6 +1109,7 @@ def check_highlight_group(hl_group, data, context, echoerr): def check_highlight_groups(hl_groups, data, context, echoerr): + havemarks(hl_groups) rs = [hl_exists(hl_group, data, context, echoerr) for hl_group in hl_groups] if all(rs): echoerr( @@ -1103,6 +1146,7 @@ def list_themes(data, context): def check_segment_data_key(key, data, context, echoerr): + havemarks(key) has_module_name = '.' in key found = False for ext, theme in list_themes(data, context): @@ -1141,6 +1185,7 @@ threaded_args_specs = { def check_args_variant(func, args, data, context, echoerr): + havemarks(args) argspec = getconfigargspec(func) present_args = set(args) all_args = set(argspec.args) @@ -1181,6 +1226,7 @@ def check_args_variant(func, args, data, context, echoerr): def check_args(get_functions, args, data, context, echoerr): + context_has_marks(context) new_echoerr = DelayedEchoErr(echoerr) count = 0 hadproblem = False From 721dadd57c9a6fa5b9332bce9589be1f59775c8f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 23:23:31 +0400 Subject: [PATCH 1363/1472] Use init_context to initialize config context --- powerline/lint/__init__.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 49e03d96..66259e73 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -37,6 +37,10 @@ key_sep = JStr('/') list_sep = JStr(', ') +def init_context(config): + return ((MarkedUnicode('', config.mark), config),) + + def context_key(context): return key_sep.join((c[0] for c in context)) @@ -1474,7 +1478,7 @@ def check(paths=None, debug=False): if main_spec.match( main_config, data={'configs': configs, 'lists': lists}, - context=(('', main_config),), + context=init_context(main_config), echoerr=ee )[1]: hadproblem = True @@ -1492,7 +1496,7 @@ def check(paths=None, debug=False): sys.stderr.write(str(e) + '\n') hadproblem = True else: - if colors_spec.match(colors_config, context=(('', colors_config),), echoerr=ee)[1]: + if colors_spec.match(colors_config, context=init_context(colors_config), echoerr=ee)[1]: hadproblem = True if lhadproblem[0]: @@ -1517,7 +1521,7 @@ def check(paths=None, debug=False): hadproblem = True top_colorscheme_configs[colorscheme] = config data['colorscheme'] = colorscheme - if top_colorscheme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + if top_colorscheme_spec.match(config, context=init_context(config), data=data, echoerr=ee)[1]: hadproblem = True ext_colorscheme_configs = defaultdict(lambda: {}) @@ -1549,7 +1553,7 @@ def check(paths=None, debug=False): spec = shell_colorscheme_spec else: spec = colorscheme_spec - if spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + if spec.match(config, context=init_context(config), data=data, echoerr=ee)[1]: hadproblem = True colorscheme_configs = {} @@ -1618,7 +1622,7 @@ def check(paths=None, debug=False): else: data['theme_type'] = 'regular' spec = theme_spec - if spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + if spec.match(config, context=init_context(config), data=data, echoerr=ee)[1]: hadproblem = True for top_theme, config in top_theme_configs.items(): @@ -1633,7 +1637,7 @@ def check(paths=None, debug=False): } data['theme_type'] = 'top' data['theme'] = top_theme - if top_theme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]: + if top_theme_spec.match(config, context=init_context(config), data=data, echoerr=ee)[1]: hadproblem = True return hadproblem From 59e039ab5b00f507b38639aa4106a2118347f45e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 24 Aug 2014 23:24:32 +0400 Subject: [PATCH 1364/1472] Make error messages from context_has_marks and havemarks more verbose --- powerline/lint/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 66259e73..51703da6 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -48,15 +48,15 @@ def context_key(context): def havemarks(*args): for i, v in enumerate(args): if not hasattr(v, 'mark'): - raise AssertionError('Value #{0} has no attribute `mark`'.format(i)) + raise AssertionError('Value #{0} ({1!r}) has no attribute `mark`'.format(i, v)) def context_has_marks(context): for i, v in enumerate(context): if not hasattr(v[0], 'mark'): - raise AssertionError('Key #{0} in context has no attribute `mark`'.format(i)) + raise AssertionError('Key #{0} ({1!r}) in context has no attribute `mark`'.format(i, v[0])) if not hasattr(v[1], 'mark'): - raise AssertionError('Value #{0} in context has no attribute `mark`'.format(i)) + raise AssertionError('Value #{0} ({1!r}) in context has no attribute `mark`'.format(i, v[1])) class EchoErr(object): From 3a608d838df2e6d318d224941171e4595a455dc8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Aug 2014 00:36:43 +0400 Subject: [PATCH 1365/1472] Record docstring marks --- powerline/lint/__init__.py | 51 ++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 51703da6..6f38de9b 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -12,7 +12,7 @@ from copy import copy from powerline.lint.markedjson import load from powerline import generate_config_finder, get_config_paths, load_config from powerline.lib.config import ConfigLoader -from powerline.lint.markedjson.error import echoerr, MarkedError +from powerline.lint.markedjson.error import echoerr, MarkedError, Mark from powerline.lint.markedjson.markedvalue import MarkedUnicode from powerline.segments.vim import vim_modes from powerline.lint.inspect import getconfigargspec @@ -964,12 +964,24 @@ def check_segment_name(name, data, context, echoerr): if func.__doc__: H_G_USED_STR = 'Highlight groups used: ' + LHGUS = len(H_G_USED_STR) D_H_G_USED_STR = 'Divider highlight group used: ' - for line in func.__doc__.split('\n'): + LDHGUS = len(D_H_G_USED_STR) + pointer = 0 + mark_name = '<{0} docstring>'.format(name) + for i, line in enumerate(func.__doc__.split('\n')): if H_G_USED_STR in line: - hl_groups.append(line[line.index(H_G_USED_STR) + len(H_G_USED_STR):]) + idx = line.index(H_G_USED_STR) + LHGUS + hl_groups.append(( + line[idx:], + (mark_name, i + 1, idx + 1, func.__doc__), + pointer + idx + )) elif D_H_G_USED_STR in line: - divider_hl_group = line[line.index(D_H_G_USED_STR) + len(D_H_G_USED_STR) + 2:-3] + idx = line.index(D_H_G_USED_STR) + LDHGUS + 2 + mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx) + divider_hl_group = MarkedUnicode(line[idx:-3], mark) + pointer += len(line) + len('\n') hadproblem = False @@ -988,11 +1000,28 @@ def check_segment_name(name, data, context, echoerr): if hl_groups: greg = re.compile(r'``([^`]+)``( \(gradient\))?') - hl_groups = [ - [greg.match(subs).groups() for subs in s.split(' or ')] - for s in (list_sep.join(hl_groups)).split(', ') - ] - for required_pack in hl_groups: + parsed_hl_groups = [] + for line, mark_args, pointer in hl_groups: + for s in line.split(', '): + required_pack = [] + sub_pointer = pointer + for subs in s.split(' or '): + match = greg.match(subs) + try: + if not match: + continue + hl_group = MarkedUnicode( + match.group(1), + Mark(*mark_args, pointer=sub_pointer + match.start(1)) + ) + gradient = bool(match.group(2)) + required_pack.append((hl_group, gradient)) + finally: + sub_pointer += len(subs) + len(' or ') + parsed_hl_groups.append(required_pack) + pointer += len(s) + len(', ') + del hl_group, gradient + for required_pack in parsed_hl_groups: rs = [ hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) for hl_group, gradient in required_pack @@ -1080,7 +1109,7 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): echoerr( context='Error while checking highlight group in theme (key {key})'.format( key=context_key(context)), - context_mark=getattr(hl_group, 'mark', None), + context_mark=hl_group.mark, problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), problem_mark=color.mark ) @@ -1090,7 +1119,7 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): echoerr( context='Error while checking highlight group in theme (key {key})'.format( key=context_key(context)), - context_mark=getattr(hl_group, 'mark', None), + context_mark=hl_group.mark, problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), problem_mark=group_config.mark ) From f8bea417fe0a09e50938d0fc837bd1ab97ac3a0b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Aug 2014 00:37:25 +0400 Subject: [PATCH 1366/1472] Add MarkedDict.keydict attribute Useful for preserving marked keys --- powerline/lint/markedjson/markedvalue.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 6c619c56..c7929cd2 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -64,9 +64,20 @@ class MarkedFloat(float): class MarkedDict(dict): __new__ = gen_new(dict) - __init__ = gen_init(dict) __getnewargs__ = gen_getnewargs(dict) + def __init__(self, value, mark): + dict.__init__(self, value) + self.keydict = dict(((key, key) for key in self)) + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + self.keydict[key] = key + + def update(self, *args, **kwargs): + dict.update(self, *args, **kwargs) + self.keydict = dict(((key, key) for key in self)) + def copy(self): return MarkedDict(super(MarkedDict, self).copy(), self.mark) From 1cc1e3562455c02d33e73f54c4cb1a9a8136f151 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Aug 2014 00:38:04 +0400 Subject: [PATCH 1367/1472] Check whether values have marks recursively --- powerline/lint/__init__.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 6f38de9b..af599245 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -45,18 +45,22 @@ def context_key(context): return key_sep.join((c[0] for c in context)) -def havemarks(*args): +def havemarks(*args, **kwargs): + origin = kwargs.get('origin', '') for i, v in enumerate(args): if not hasattr(v, 'mark'): - raise AssertionError('Value #{0} ({1!r}) has no attribute `mark`'.format(i, v)) + raise AssertionError('Value #{0}/{1} ({2!r}) has no attribute `mark`'.format(origin, i, v)) + if isinstance(v, dict): + for key, val in v.items(): + havemarks(key, val, origin=(origin + '[' + unicode(i) + ']/' + unicode(key))) + elif isinstance(v, list): + havemarks(*v, origin=(origin + '[' + unicode(i) + ']')) def context_has_marks(context): for i, v in enumerate(context): - if not hasattr(v[0], 'mark'): - raise AssertionError('Key #{0} ({1!r}) in context has no attribute `mark`'.format(i, v[0])) - if not hasattr(v[1], 'mark'): - raise AssertionError('Value #{0} ({1!r}) in context has no attribute `mark`'.format(i, v[1])) + havemarks(v[0], origin='context key') + havemarks(v[1], origin='context val') class EchoErr(object): From 9d7c5dd390113fe1aabab20cbc6ad6a55159098d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Aug 2014 00:38:32 +0400 Subject: [PATCH 1368/1472] Add marks where they were forgotten --- powerline/lint/__init__.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index af599245..aac214ae 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -94,6 +94,10 @@ class DelayedEchoErr(EchoErr): __bool__ = __nonzero__ +def new_context_item(key, value): + return ((value.keydict[key], value[key]),) + + class Spec(object): def __init__(self, **keys): self.specs = [] @@ -189,7 +193,7 @@ class Spec(object): item, value.mark, data, - context + (('list item ' + unicode(i), item),), + context + ((MarkedUnicode('list item ' + unicode(i), item.mark), item),), echoerr ) else: @@ -230,7 +234,7 @@ class Spec(object): item, value.mark, data, - context + (('tuple item ' + unicode(i), item),), + context + ((MarkedUnicode('tuple item ' + unicode(i), item.mark), item),), echoerr ) if ihadproblem: @@ -399,7 +403,7 @@ class Spec(object): value[key], value.mark, data, - context + ((key, value[key]),), + context + new_context_item(key, value), echoerr ) if mhadproblem: @@ -430,7 +434,7 @@ class Spec(object): value[key], value.mark, data, - context + ((key, value[key]),), + context + new_context_item(key, value), echoerr ) if vhadproblem: @@ -821,7 +825,7 @@ highlight_keys = set(('highlight_group', 'name')) def check_key_compatibility(segment, data, context, echoerr): context_has_marks(context) havemarks(segment) - segment_type = segment.get('type', 'function') + segment_type = segment.get('type', MarkedUnicode('function', None)) havemarks(segment_type) if segment_type not in type_keys: @@ -909,9 +913,7 @@ def check_full_segment_data(segment, data, context, echoerr): for name in names: try: val = segment_data[name][key] - # HACK to keep marks - l = list(segment_data[name]) - k = l[l.index(key)] + k = segment_data[name].keydict[key] segment_copy[k] = val except KeyError: pass @@ -1251,7 +1253,7 @@ def check_args_variant(func, args, data, context, echoerr): args[key], args.mark, data, - context + ((key, args[key]),), + context + new_context_item(key, args), echoerr ) if khadproblem: From aeea3331adc97209d3e3099baa9847b6f7646316 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 25 Aug 2014 00:48:24 +0400 Subject: [PATCH 1369/1472] Fix python-2.6 support It was calling __setitem__ from copy.deepcopy on an unitialized dictionary. --- powerline/lint/markedjson/markedvalue.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index c7929cd2..6a9600dc 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -63,12 +63,15 @@ class MarkedFloat(float): class MarkedDict(dict): - __new__ = gen_new(dict) + __init__ = gen_init(dict) __getnewargs__ = gen_getnewargs(dict) - def __init__(self, value, mark): - dict.__init__(self, value) - self.keydict = dict(((key, key) for key in self)) + def __new__(arg_cls, value, mark): + r = super(arg_cls, arg_cls).__new__(arg_cls, value) + r.mark = mark + r.value = value + r.keydict = dict(((key, key) for key in r)) + return r def __setitem__(self, key, value): dict.__setitem__(self, key, value) From c4aa72fc1f024aeb1925273c2eae4cc4f5aeaeee Mon Sep 17 00:00:00 2001 From: Pierre Carru Date: Tue, 26 Aug 2014 10:37:32 +0100 Subject: [PATCH 1370/1472] Make shell client work in OS X: - use gnu env for `env -0`, - use filesystem socket. --- client/powerline.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/client/powerline.sh b/client/powerline.sh index 2c85fe77..b112ec21 100755 --- a/client/powerline.sh +++ b/client/powerline.sh @@ -1,11 +1,21 @@ #!/bin/sh +test "${OSTYPE#darwin}" = "${OSTYPE}" && darwin=n || darwin=y + if test "$1" = "--socket" ; then shift ADDRESS="$1" shift else ADDRESS="powerline-ipc-${UID:-`id -u`}" + test "$darwin" = y && ADDRESS="/tmp/$ADDRESS" +fi + +if test "$darwin" = y; then + ENV=genv +else + ENV=env + ADDRESS="abstract-client:$ADDRESS" fi # Warning: env -0 does not work in busybox. Consider switching to parsing @@ -16,8 +26,8 @@ fi printf '%s\0' "$argv" done printf '%s\0' "$PWD" - env -0 -) | socat -lf/dev/null -t 10 - abstract-client:"$ADDRESS" + $ENV -0 +) | socat -lf/dev/null -t 10 - "$ADDRESS" if test $? -ne 0 ; then powerline-render "$@" From 37546f4ad2bf6c773e1f159cb043921f14bb41a8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 19:31:10 +0400 Subject: [PATCH 1371/1472] Update shell powerline client OSX requirements in documentation --- docs/source/installation/osx.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index 204666a7..70523b78 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -17,6 +17,11 @@ Python package . +.. note:: + In case you want or have to use ``powerline.sh`` socat-based client you + should also install GNU env named ``genv``. This may be achieved by running + ``brew install coreutils``. + 2. Install Powerline using the following command:: pip install --user git+git://github.com/Lokaltog/powerline From 81d8a9e180a821f44a24bc44a70896f9c968eac4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 19:57:45 +0400 Subject: [PATCH 1372/1472] Explicitly print exception from :try block Reason: :try takes any output to the stderr as the exception, so traceback is never printed even though nothing is caught. --- powerline/bindings/vim/plugin/powerline.vim | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 1de4fe1f..d8bc43bd 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -62,14 +62,19 @@ endfunction try let s:can_replace_pyeval = !exists('g:powerline_pyeval') call s:rcmd("try:") - call s:rcmd(" ".s:import_cmd."") - call s:rcmd("except ImportError:") - call s:rcmd(" import sys, vim") - call s:rcmd(" sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))") - call s:rcmd(" ".s:import_cmd."") - call s:rcmd("import vim") - call s:rcmd("VimPowerline().setup(pyeval=vim.eval('s:pyeval'), pycmd=vim.eval('s:pycmd'), can_replace_pyeval=int(vim.eval('s:can_replace_pyeval')))") - call s:rcmd("del VimPowerline") + call s:rcmd(" try:") + call s:rcmd(" ".s:import_cmd."") + call s:rcmd(" except ImportError:") + call s:rcmd(" import sys, vim") + call s:rcmd(" sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))") + call s:rcmd(" ".s:import_cmd."") + call s:rcmd(" import vim") + call s:rcmd(" VimPowerline().setup(pyeval=vim.eval('s:pyeval'), pycmd=vim.eval('s:pycmd'), can_replace_pyeval=int(vim.eval('s:can_replace_pyeval')))") + call s:rcmd(" del VimPowerline") + call s:rcmd("except Exception:") + call s:rcmd(" import traceback, sys") + call s:rcmd(" traceback.print_exc(file=sys.stdout)") + call s:rcmd(" raise") execute s:pycmd s:pystr unlet s:pystr let s:launched = 1 From dcf442e86f0f261dc0cc6062063550207af5fc68 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 20:00:11 +0400 Subject: [PATCH 1373/1472] Balance error message text --- powerline/bindings/vim/plugin/powerline.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index d8bc43bd..244bb24b 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -119,8 +119,8 @@ finally call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") call s:rcmd(" if real_this_dir != sys.path[-1]:") - call s:rcmd(" print('Check your installation:')") - call s:rcmd(" print('this script is symlinked somewhere where powerline is not present.')") + call s:rcmd(" print('Check your installation: this script is symlinked somewhere')") + call s:rcmd(" print('where powerline is not present.')") call s:rcmd(" elif real_powerline_dir != real_this_dir:") call s:rcmd(" print('It appears that you have two powerline versions installed:')") call s:rcmd(" print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')") From e316c81f1d370e6dfaf8176b4783103d7ebf8d83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 20:06:49 +0400 Subject: [PATCH 1374/1472] Do not emit a warning about symlinks when it is not the problem --- powerline/bindings/vim/plugin/powerline.vim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 244bb24b..94ddf062 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -62,11 +62,13 @@ endfunction try let s:can_replace_pyeval = !exists('g:powerline_pyeval') call s:rcmd("try:") + call s:rcmd(" powerline_appended_path = None") call s:rcmd(" try:") call s:rcmd(" ".s:import_cmd."") call s:rcmd(" except ImportError:") call s:rcmd(" import sys, vim") - call s:rcmd(" sys.path.append(vim.eval('expand(\":h:h:h:h:h\")'))") + call s:rcmd(" powerline_appended_path = vim.eval('expand(\":h:h:h:h:h\")')") + call s:rcmd(" sys.path.append(powerline_appended_path)") call s:rcmd(" ".s:import_cmd."") call s:rcmd(" import vim") call s:rcmd(" VimPowerline().setup(pyeval=vim.eval('s:pyeval'), pycmd=vim.eval('s:pycmd'), can_replace_pyeval=int(vim.eval('s:can_replace_pyeval')))") @@ -118,9 +120,10 @@ finally call s:rcmd(" this_dir = os.path.dirname(this_dir)") call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") - call s:rcmd(" if real_this_dir != sys.path[-1]:") + call s:rcmd(" if powerline_appended_path is not None and real_this_dir != powerline_appended_path:") call s:rcmd(" print('Check your installation: this script is symlinked somewhere')") - call s:rcmd(" print('where powerline is not present.')") + call s:rcmd(" print('where powerline is not present: {0!r} != {1!r}.'.format(") + call s:rcmd(" real_this_dir, sys.path[-1]))") call s:rcmd(" elif real_powerline_dir != real_this_dir:") call s:rcmd(" print('It appears that you have two powerline versions installed:')") call s:rcmd(" print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')") From c3ba6d0c9911662e4c601054a168c289559c40d2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 20:13:12 +0400 Subject: [PATCH 1375/1472] Do not remove one component from directory path --- powerline/bindings/vim/plugin/powerline.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 94ddf062..8344e949 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -117,7 +117,6 @@ finally call s:rcmd(" print('Check your installation:')") call s:rcmd(" print('this script is not in powerline[/bindings/vim/plugin] directory,')") call s:rcmd(" print('neither it is installed system-wide')") - call s:rcmd(" this_dir = os.path.dirname(this_dir)") call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") call s:rcmd(" if powerline_appended_path is not None and real_this_dir != powerline_appended_path:") From 39a7142cb7c59e12db91d40c2e88e61cd1ea491c Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 20:15:58 +0400 Subject: [PATCH 1376/1472] Normalize all paths before doing anything else --- powerline/bindings/vim/plugin/powerline.vim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 8344e949..41ea2120 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -110,9 +110,13 @@ finally if strpart(expand(''), 0, 5) !=# '/usr/' call s:rcmd(" else:") call s:rcmd(" import os") - call s:rcmd(" powerline_dir = os.path.dirname(os.path.realpath(powerline.__file__))") - call s:rcmd(" this_dir = os.path.dirname(os.path.realpath(vim.eval('expand(\":p\")')))") - call s:rcmd(" this_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))") + call s:rcmd(" powerline_dir = os.path.realpath(os.path.normpath(powerline.__file__))") + call s:rcmd(" powerline_dir = os.path.dirname(powerline.__file__)") + call s:rcmd(" this_dir = os.path.realpath(os.path.normpath(vim.eval('expand(\":p\")')))") + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings/vim/plugin + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings/vim + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline call s:rcmd(" if os.path.basename(this_dir) != 'powerline':") call s:rcmd(" print('Check your installation:')") call s:rcmd(" print('this script is not in powerline[/bindings/vim/plugin] directory,')") From 644dec76e0f62f431666e75f4f13000b51dfa89d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 20:19:04 +0400 Subject: [PATCH 1377/1472] Move one if check into python code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason: this removes one useless indentation level that breaks “tabs for indentation, spaces for alignment” rule. --- powerline/bindings/vim/plugin/powerline.vim | 79 ++++++++++----------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 41ea2120..8f839236 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -97,46 +97,45 @@ finally echomsg 'should set g:powerline_pycmd to "py3" to make it load correctly.' endif echohl None - call s:rcmd( "def powerline_troubleshoot():") - call s:rcmd( " import sys") - call s:rcmd( " if sys.version_info < (2, 6):") - call s:rcmd( " print('Too old python version: ' + sys.version + ' (first supported is 2.6)')") - call s:rcmd( " elif sys.version_info[0] == 3 and sys.version_info[1] < 2:") - call s:rcmd( " print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')") - call s:rcmd( " try:") - call s:rcmd( " import powerline") - call s:rcmd( " except ImportError:") - call s:rcmd( " print('Unable to import powerline, is it installed?')") - if strpart(expand(''), 0, 5) !=# '/usr/' - call s:rcmd(" else:") - call s:rcmd(" import os") - call s:rcmd(" powerline_dir = os.path.realpath(os.path.normpath(powerline.__file__))") - call s:rcmd(" powerline_dir = os.path.dirname(powerline.__file__)") - call s:rcmd(" this_dir = os.path.realpath(os.path.normpath(vim.eval('expand(\":p\")')))") - call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings/vim/plugin - call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings/vim - call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings - call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline - call s:rcmd(" if os.path.basename(this_dir) != 'powerline':") - call s:rcmd(" print('Check your installation:')") - call s:rcmd(" print('this script is not in powerline[/bindings/vim/plugin] directory,')") - call s:rcmd(" print('neither it is installed system-wide')") - call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") - call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") - call s:rcmd(" if powerline_appended_path is not None and real_this_dir != powerline_appended_path:") - call s:rcmd(" print('Check your installation: this script is symlinked somewhere')") - call s:rcmd(" print('where powerline is not present: {0!r} != {1!r}.'.format(") - call s:rcmd(" real_this_dir, sys.path[-1]))") - call s:rcmd(" elif real_powerline_dir != real_this_dir:") - call s:rcmd(" print('It appears that you have two powerline versions installed:')") - call s:rcmd(" print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')") - call s:rcmd(" print('You should remove one of this. Check out troubleshooting section,')") - call s:rcmd(" print('it contains some information about the alternatives.')") - endif - call s:rcmd( "try:") - call s:rcmd( " powerline_troubleshoot()") - call s:rcmd( "finally:") - call s:rcmd( " del powerline_troubleshoot") + call s:rcmd("def powerline_troubleshoot():") + call s:rcmd(" import sys") + call s:rcmd(" if sys.version_info < (2, 6):") + call s:rcmd(" print('Too old python version: ' + sys.version + ' (first supported is 2.6)')") + call s:rcmd(" elif sys.version_info[0] == 3 and sys.version_info[1] < 2:") + call s:rcmd(" print('Too old python 3 version: ' + sys.version + ' (first supported is 3.2)')") + call s:rcmd(" try:") + call s:rcmd(" import powerline") + call s:rcmd(" except ImportError:") + call s:rcmd(" print('Unable to import powerline, is it installed?')") + call s:rcmd(" else:") + call s:rcmd(" if not vim.eval('expand(\"\")').startswith('/usr/'):") + call s:rcmd(" import os") + call s:rcmd(" powerline_dir = os.path.realpath(os.path.normpath(powerline.__file__))") + call s:rcmd(" powerline_dir = os.path.dirname(powerline.__file__)") + call s:rcmd(" this_dir = os.path.realpath(os.path.normpath(vim.eval('expand(\":p\")')))") + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings/vim/plugin + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings/vim + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline/bindings + call s:rcmd(" this_dir = os.path.dirname(this_dir)") " powerline + call s:rcmd(" if os.path.basename(this_dir) != 'powerline':") + call s:rcmd(" print('Check your installation:')") + call s:rcmd(" print('this script is not in powerline[/bindings/vim/plugin] directory,')") + call s:rcmd(" print('neither it is installed system-wide')") + call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") + call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") + call s:rcmd(" if powerline_appended_path is not None and real_this_dir != powerline_appended_path:") + call s:rcmd(" print('Check your installation: this script is symlinked somewhere')") + call s:rcmd(" print('where powerline is not present: {0!r} != {1!r}.'.format(") + call s:rcmd(" real_this_dir, sys.path[-1]))") + call s:rcmd(" elif real_powerline_dir != real_this_dir:") + call s:rcmd(" print('It appears that you have two powerline versions installed:')") + call s:rcmd(" print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')") + call s:rcmd(" print('You should remove one of this. Check out troubleshooting section,')") + call s:rcmd(" print('it contains some information about the alternatives.')") + call s:rcmd("try:") + call s:rcmd(" powerline_troubleshoot()") + call s:rcmd("finally:") + call s:rcmd(" del powerline_troubleshoot") execute s:pycmd s:pystr unlet s:pystr unlet s:pycmd From d88d87f6af2849dcead7130173185f28338dd35f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 21:13:56 +0400 Subject: [PATCH 1378/1472] Do not make daemon output zero byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This byte is useless and can be seen in zsh output (strange that it did not cause bugs so far, at least none I know about). It may have been needed if any client was supposed to hold connection with daemon for a few runs, but all current clients work in “request → output response → exit” fashion without caring about terminating newline and definitely without preserving connection to daemon accross runs. --- scripts/powerline-daemon | 2 +- tests/test_shells/zsh.daemon.ok | Bin 12912 -> 12880 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 593002df..7e037f9c 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -137,7 +137,7 @@ def do_read(conn, timeout=2.0): def do_write(conn, result): try: - eintr_retry_call(conn.sendall, result + b'\0') + eintr_retry_call(conn.sendall, result) except Exception: pass diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 7151fc4f61565aaf05d1d23297ceb0d2948f20ae..277e4ca538b3a515a2f5242617e1de445534772c 100644 GIT binary patch delta 260 zcmey6av^2IPR7l<822->CZ{Oq>1|HrT+KK+o}X{CC95OT=4$q%jGG-f1(;aV5_5`E zfwGSnCtLD5ZLZ<{$hbL={|F;5Sew3*ii>BEk<#XajMEq=UlUT;{9Wh>>+W2akHQlkab7;1iD?5A8M;@?vj7UIFV6$^AyFIjGGfRwnB+nY6lrN$7t+g z1S+!M9H!O9xcQ-W598!K-MLID`I8Sa%1qv)m%*z51gXgx`3fqDNy#u#PLOC)QEFl~ aOl0zA{aKs+4D1*u_Zr@1Eh#EZtLTN_jsra)Y!esknFs87e`{nIJF=D5oWO7^LB?&=C-|QRD!KDik{f zqO2uOfGB1uFz>AN2}VqdCuyl|u9SZVR5y`PdUKQFOpuO=YFm-0R<(mbjcJKF#i>x4 zO}iOq~uQy_AHM O4DYfrloXYwasdFSflzS( From 70e7088b04704d3c31bd57045b234e0b56939239 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 21:30:02 +0400 Subject: [PATCH 1379/1472] Do not replace ascii with UTF-8 in powerline-daemon and python client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assuming ascii was expected to be used when LANG=C it did not work in any case: in this case preferred encoding is “ANSI_X3.4-1968”. Same for `sys.getfilesystemencoding()`. --- client/powerline.py | 2 -- scripts/powerline-daemon | 2 -- 2 files changed, 4 deletions(-) diff --git a/client/powerline.py b/client/powerline.py index d874b86a..adf99f08 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -45,8 +45,6 @@ except Exception: os.execvp('powerline-render', args) fenc = sys.getfilesystemencoding() or 'utf-8' -if fenc == 'ascii': - fenc = 'utf-8' tobytes = lambda s: s if isinstance(s, bytes) else s.encode(fenc) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 7e037f9c..26ccdfad 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -143,8 +143,6 @@ def do_write(conn, result): encoding = getpreferredencoding() or 'UTF-8' -if encoding.lower() == 'ascii': - encoding = 'UTF-8' def safe_bytes(o, encoding=encoding): From 5434852977b21e11a991cf6ae18c06208370cd85 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 21:33:07 +0400 Subject: [PATCH 1380/1472] A few style improvements in Python client --- client/powerline.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/powerline.py b/client/powerline.py index adf99f08..83253881 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -37,6 +37,7 @@ def eintr_retry_call(func, *args, **kwargs): continue raise + try: eintr_retry_call(sock.connect, address) except Exception: @@ -46,7 +47,13 @@ except Exception: fenc = sys.getfilesystemencoding() or 'utf-8' -tobytes = lambda s: s if isinstance(s, bytes) else s.encode(fenc) + +def tobytes(s): + if isinstance(s, bytes): + return s + else: + return s.encode(fenc) + args = [tobytes('%x' % (len(sys.argv) - 1))] args.extend((tobytes(s) for s in sys.argv[1:])) From 7871cfcda47a467a7ac17d90f6fa03c075260dac Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 21:34:18 +0400 Subject: [PATCH 1381/1472] Use posix.environ in Python client if available This way there will be no need in converting keys and values to bytes objects on \*nix systems. --- client/powerline.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/powerline.py b/client/powerline.py index 83253881..aa7e57a8 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -5,8 +5,12 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct import sys import socket -import os import errno +import os +try: + from posix import environ +except ImportError: + from os import environ if len(sys.argv) < 2: @@ -69,7 +73,7 @@ else: args.append(cwd) -args.extend((tobytes(k) + b'=' + tobytes(v) for k, v in os.environ.items())) +args.extend((tobytes(k) + b'=' + tobytes(v) for k, v in environ.items())) EOF = b'\0\0' From 484b9af6ed47854ed3e545cb19c44ff4fb286037 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 21:36:19 +0400 Subject: [PATCH 1382/1472] Use the same function for getting encoding in Python client and daemon --- client/powerline.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/powerline.py b/client/powerline.py index aa7e57a8..ac3d6356 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -7,6 +7,9 @@ import sys import socket import errno import os + +from locale import getpreferredencoding + try: from posix import environ except ImportError: @@ -49,7 +52,7 @@ except Exception: args = ['powerline-render'] + sys.argv[1:] os.execvp('powerline-render', args) -fenc = sys.getfilesystemencoding() or 'utf-8' +fenc = getpreferredencoding() or 'utf-8' def tobytes(s): From 7e7803f680df7d25671ac9bac3d00dc6e879246a Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 21:44:29 +0400 Subject: [PATCH 1383/1472] Make powerline-render respect user locale as well Closes #1023 --- powerline/shell.py | 8 ++++---- scripts/powerline-daemon | 6 +++--- scripts/powerline-render | 13 +++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/powerline/shell.py b/powerline/shell.py index 7f006e58..d1f717e6 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -81,7 +81,7 @@ def finish_args(args): args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg)) -def write_output(args, powerline, segment_info, write): +def write_output(args, powerline, segment_info, write, encoding): if args.renderer_arg: segment_info.update(args.renderer_arg) if args.side.startswith('above'): @@ -90,8 +90,8 @@ def write_output(args, powerline, segment_info, write): segment_info=segment_info, mode=segment_info['environ'].get('_POWERLINE_MODE'), ): - write(line) - write('\n') + write(line.encode(encoding, 'replace')) + write(b'\n') args.side = args.side[len('above'):] if args.side: @@ -101,4 +101,4 @@ def write_output(args, powerline, segment_info, write): segment_info=segment_info, mode=segment_info['environ'].get('_POWERLINE_MODE'), ) - write(rendered) + write(rendered.encode(encoding, 'replace')) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 26ccdfad..d14eac04 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -12,7 +12,7 @@ from signal import signal, SIGTERM from time import sleep from functools import partial from locale import getpreferredencoding -from io import StringIO +from io import BytesIO from powerline.shell import get_argparser, finish_args, ShellPowerline, write_output from powerline.lib.monotonic import monotonic @@ -98,8 +98,8 @@ def render(args, environ, cwd): powerline.pl.exception('Failed to render {0}: {1}', str(key), str(e)) else: return 'Failed to render {0}: {1}'.format(str(key), str(e)) - s = StringIO() - write_output(args, powerline, segment_info, s.write) + s = BytesIO() + write_output(args, powerline, segment_info, s.write, encoding) s.seek(0) return s.read() diff --git a/scripts/powerline-render b/scripts/powerline-render index bdedb1b7..52f85a13 100755 --- a/scripts/powerline-render +++ b/scripts/powerline-render @@ -4,6 +4,8 @@ import sys import os +from locale import getpreferredencoding + try: from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output except ImportError: @@ -11,11 +13,10 @@ except ImportError: from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output # NOQA -def write(output): - try: - sys.stdout.write(output) - except UnicodeEncodeError: - sys.stdout.write(output.encode('utf-8')) +if sys.version_info < (3,): + write = sys.stdout.write +else: + write = sys.stdout.buffer.write if __name__ == '__main__': @@ -23,4 +24,4 @@ if __name__ == '__main__': finish_args(args) powerline = ShellPowerline(args, run_once=True) segment_info = {'args': args, 'environ': os.environ} - write_output(args, powerline, segment_info, write) + write_output(args, powerline, segment_info, write, getpreferredencoding()) From d32b798c1190f71923fc9abb8557aff02a5ee312 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 21:58:57 +0400 Subject: [PATCH 1384/1472] Add `use_non_breaking_spaces` theme option, use it in ascii theme --- docs/source/configuration/reference.rst | 13 +++++++++++++ powerline/config_files/themes/ascii.json | 1 + powerline/lint/__init__.py | 1 + powerline/renderer.py | 10 ++++++---- tests/test_local_overrides.vim | 2 +- tests/test_shells/ipython.ok | 20 ++++++++++---------- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 12966b45..a0edf36f 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -287,6 +287,19 @@ ascii Theme without any unicode characters at all after it (on the left side). These spaces will not be added if divider is not drawn. +``use_non_breaking_spaces`` + Determines whether non-breaking spaces should be used in place of the + regular ones. This option is needed because regular spaces are not displayed + properly when using powerline with some font configuration. Defaults to + ``True``. + + .. note:: + Unlike all other options this one is only checked once at startup using + whatever theme is :ref:`the default `. If this option + is set in the local themes it will be ignored. This option may also be + ignored in some bindings. + + ``dividers`` Defines the dividers used in all Powerline extensions. This option should usually only be changed if you don't have a patched font, or if diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index adacfbfb..c8ff0d71 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -1,4 +1,5 @@ { + "use_non_breaking_spaces": false, "dividers": { "left": { "hard": " ", diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index aac214ae..61ecca38 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1388,6 +1388,7 @@ common_theme_spec = Spec( top_theme_spec = common_theme_spec().update( dividers=dividers_spec(), spaces=spaces_spec(), + use_non_breaking_spaces=Spec().type(bool).optional(), segment_data=Spec().unknown_spec( Spec().func(check_segment_data_key), segment_data_value_spec(), diff --git a/powerline/renderer.py b/powerline/renderer.py index 4ac0a9db..b129a710 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -73,7 +73,7 @@ class Renderer(object): python-2) regular string or ``None``. ''' - character_translations = {ord(' '): NBSP} + character_translations = {} '''Character translations for use in escape() function. See documentation of ``unicode.translate`` for details. @@ -101,6 +101,9 @@ class Renderer(object): self.theme_config = theme_config theme_kwargs['pl'] = pl self.pl = pl + if theme_config.get('use_non_breaking_spaces', True): + self.character_translations = self.character_translations.copy() + self.character_translations[ord(' ')] = NBSP self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs @@ -442,11 +445,10 @@ class Renderer(object): segment['_rendered_hl'] = contents_highlighted yield segment - @classmethod - def escape(cls, string): + def escape(self, string): '''Method that escapes segment contents. ''' - return string.translate(cls.character_translations) + return string.translate(self.character_translations) def hlstyle(fg=None, bg=None, attr=None): '''Output highlight style string. diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index 5b80d26c..bf2ad8f8 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -30,7 +30,7 @@ catch cquit endtry -if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                                 %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_247_10329757_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold#  1%#Pl_22_24576_252_13684944_NONE#:1  ' +if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#unix%#Pl_240_5789784_236_3158064_NONE# %#Pl_247_10329757_240_5789784_NONE# 100%%%#Pl_252_13684944_240_5789784_NONE# %#Pl_235_2500134_252_13684944_NONE# LN %#Pl_235_2500134_252_13684944_bold# 1%#Pl_22_24576_252_13684944_NONE#:1 ' call writefile(['Unexpected result', result], 'message.fail') cquit endif diff --git a/tests/test_shells/ipython.ok b/tests/test_shells/ipython.ok index 2ccd6e14..2de654fc 100644 --- a/tests/test_shells/ipython.ok +++ b/tests/test_shells/ipython.ok @@ -1,14 +1,14 @@ - In [2]  bool 42 -     2>  bool(42) - Out[2]  True + In [2]  bool 42 + 2>  bool(42) + Out[2]  True - In [3]  bool 44 -     3>  bool(44) - Out[3]  True + In [3]  bool 44 + 3>  bool(44) + Out[3]  True - In [4]  class Test(object): -          pass -          + In [4]  class Test(object): +   pass +   - In [5]  exit + In [5]  exit From 289a08c539f61af24bd13eb631f1a26ac1c0db31 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 22:04:48 +0400 Subject: [PATCH 1385/1472] Replace .replace(' ', NBSP) call with self.escape call Dividers need to be escaped as well. Fixes #1027 --- powerline/renderer.py | 2 +- tests/test_shells/bash.daemon.ok | 3 ++- tests/test_shells/bash.nodaemon.ok | 3 ++- tests/test_shells/busybox.daemon.ok | 3 ++- tests/test_shells/busybox.nodaemon.ok | 3 ++- tests/test_shells/dash.daemon.ok | 3 ++- tests/test_shells/dash.nodaemon.ok | 3 ++- tests/test_shells/input.bash | 1 + tests/test_shells/input.busybox | 1 + tests/test_shells/input.dash | 1 + tests/test_shells/input.mksh | 1 + tests/test_shells/input.zsh | 1 + tests/test_shells/ipython.ok | 20 ++++++++++---------- tests/test_shells/mksh.daemon.ok | 3 ++- tests/test_shells/mksh.nodaemon.ok | 3 ++- tests/test_shells/zsh.daemon.ok | 3 ++- tests/test_shells/zsh.nodaemon.ok | 3 ++- 17 files changed, 36 insertions(+), 21 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index b129a710..b4a3e9a6 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -385,7 +385,7 @@ class Renderer(object): # Pad segments first if draw_divider: - divider_raw = theme.get_divider(side, divider_type).replace(' ', NBSP) + divider_raw = self.escape(theme.get_divider(side, divider_type)) if side == 'left': contents_raw = ( outer_padding + (segment['_space_left'] * ' ') diff --git a/tests/test_shells/bash.daemon.ok b/tests/test_shells/bash.daemon.ok index 1354b641..e9132e60 100644 --- a/tests/test_shells/bash.daemon.ok +++ b/tests/test_shells/bash.daemon.ok @@ -25,4 +25,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/bash.nodaemon.ok b/tests/test_shells/bash.nodaemon.ok index cb0f6b6d..09b21b5c 100644 --- a/tests/test_shells/bash.nodaemon.ok +++ b/tests/test_shells/bash.nodaemon.ok @@ -25,4 +25,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/busybox.daemon.ok b/tests/test_shells/busybox.daemon.ok index 4a5bf072..60939a71 100644 --- a/tests/test_shells/busybox.daemon.ok +++ b/tests/test_shells/busybox.daemon.ok @@ -24,4 +24,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/busybox.nodaemon.ok b/tests/test_shells/busybox.nodaemon.ok index 1f6f7449..b3a90542 100644 --- a/tests/test_shells/busybox.nodaemon.ok +++ b/tests/test_shells/busybox.nodaemon.ok @@ -24,4 +24,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/dash.daemon.ok b/tests/test_shells/dash.daemon.ok index bbf3de59..93b364ed 100644 --- a/tests/test_shells/dash.daemon.ok +++ b/tests/test_shells/dash.daemon.ok @@ -23,4 +23,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/dash.nodaemon.ok b/tests/test_shells/dash.nodaemon.ok index 3a81cfcc..79770982 100644 --- a/tests/test_shells/dash.nodaemon.ok +++ b/tests/test_shells/dash.nodaemon.ok @@ -23,4 +23,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index e2d69dfa..7e503679 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -25,6 +25,7 @@ cd ../'#[bold]' cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" false true is the last line exit diff --git a/tests/test_shells/input.busybox b/tests/test_shells/input.busybox index b5e94004..803bd5c1 100644 --- a/tests/test_shells/input.busybox +++ b/tests/test_shells/input.busybox @@ -25,6 +25,7 @@ cd ../'#[bold]' cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" false true is the last line exit diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash index b5e94004..803bd5c1 100644 --- a/tests/test_shells/input.dash +++ b/tests/test_shells/input.dash @@ -25,6 +25,7 @@ cd ../'#[bold]' cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" false true is the last line exit diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh index 4ac08441..ae6bc326 100644 --- a/tests/test_shells/input.mksh +++ b/tests/test_shells/input.mksh @@ -26,6 +26,7 @@ cd ../'#[bold]' cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" false true is the last line exit diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index f31af608..56bbfc49 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -40,6 +40,7 @@ do done 1 hash -d foo=$PWD:h ; cd . +POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC ) true true is the last line exit diff --git a/tests/test_shells/ipython.ok b/tests/test_shells/ipython.ok index 2de654fc..166604df 100644 --- a/tests/test_shells/ipython.ok +++ b/tests/test_shells/ipython.ok @@ -1,14 +1,14 @@ - In [2]  bool 42 - 2>  bool(42) - Out[2]  True + In [2]  bool 42 + 2>  bool(42) + Out[2]  True - In [3]  bool 44 - 3>  bool(44) - Out[3]  True + In [3]  bool 44 + 3>  bool(44) + Out[3]  True - In [4]  class Test(object): -   pass -   + In [4]  class Test(object): +   pass +   - In [5]  exit + In [5]  exit diff --git a/tests/test_shells/mksh.daemon.ok b/tests/test_shells/mksh.daemon.ok index 569a62e2..be403a46 100644 --- a/tests/test_shells/mksh.daemon.ok +++ b/tests/test_shells/mksh.daemon.ok @@ -27,4 +27,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/mksh.nodaemon.ok b/tests/test_shells/mksh.nodaemon.ok index a6d94036..d152cde7 100644 --- a/tests/test_shells/mksh.nodaemon.ok +++ b/tests/test_shells/mksh.nodaemon.ok @@ -27,4 +27,5 @@ def   BRANCH  ⋯  shell  3rd  #[bold]  cd ../'(echo)'   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`' -  BRANCH  ⋯  shell  3rd  `echo`  false +  BRANCH  ⋯  shell  3rd  `echo`  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.dividers.left.hard=\$ABC" +  BRANCH $ABC⋯  shell  3rd  `echo` $ABCfalse diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 277e4ca5..2e5d05d9 100644 --- a/tests/test_shells/zsh.daemon.ok +++ b/tests/test_shells/zsh.daemon.ok @@ -34,4 +34,5 @@ abc                    Select variant  1 def  INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd . - INSERT  ~foo  3rd  true + INSERT  ~foo  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC ) + INSERT $ABC~foo  3rd $ABCtrue diff --git a/tests/test_shells/zsh.nodaemon.ok b/tests/test_shells/zsh.nodaemon.ok index 9853d9b6..27a734a3 100644 --- a/tests/test_shells/zsh.nodaemon.ok +++ b/tests/test_shells/zsh.nodaemon.ok @@ -34,4 +34,5 @@ abc  Select variant  1 def  INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd . - INSERT  ~foo  3rd  true + INSERT  ~foo  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC ) + INSERT $ABC~foo  3rd $ABCtrue From 285a6aa3ce779544b7b72ce470f98d49b377a4e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 22:24:27 +0400 Subject: [PATCH 1386/1472] Fall back to ASCII-only theme in case of non-unicode locale --- docs/source/configuration/reference.rst | 3 ++- powerline/__init__.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index a0edf36f..9277e4f9 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -94,7 +94,8 @@ Common configuration is a subdictionary that is a value of ``common`` key in ``default_top_theme`` String, determines which top-level theme will be used as the default. - Defaults to ``powerline``. See `Themes`_ section for more details. + Defaults to ``powerline`` in unicode locales and ``ascii`` in non-unicode + locales. See `Themes`_ section for more details. Extension-specific configuration -------------------------------- diff --git a/powerline/__init__.py b/powerline/__init__.py index 1bd5a5d3..428e3d46 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -5,14 +5,15 @@ import os import sys import logging +from locale import getpreferredencoding +from threading import Lock, Event + from powerline.colorscheme import Colorscheme from powerline.lib.config import ConfigLoader from powerline.lib.unicode import safe_unicode, FailedUnicode from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR from powerline.lib import mergedicts -from threading import Lock, Event - def _config_loader_condition(path): if path and os.path.isfile(path): @@ -234,8 +235,14 @@ def finish_common_config(common_config): Copy of common configuration with all configuration keys and expanded paths. ''' + encoding = getpreferredencoding().lower() + if encoding.startswith('utf') or encoding.startswith('ucs'): + default_top_theme = 'powerline' + else: + default_top_theme = 'ascii' + common_config = common_config.copy() - common_config.setdefault('default_top_theme', 'powerline') + common_config.setdefault('default_top_theme', default_top_theme) common_config.setdefault('paths', []) common_config.setdefault('watcher', 'auto') common_config.setdefault('log_level', 'WARNING') From dfb08fad5ac893cca1d8267880c9904a53a3996a Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 22:28:07 +0400 Subject: [PATCH 1387/1472] =?UTF-8?q?Separate=20some=20issues=20into=20?= =?UTF-8?q?=E2=80=9CVim=20issues=E2=80=9D=20section=20in=20troubleshooting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/troubleshooting.rst | 59 +++++++++++++++++---------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index a51a4142..2ca42a5f 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -120,6 +120,37 @@ make powerline package importable anywhere: use pip install --user --editable path/to/powerline +I am suffering bad lags before displaying shell prompt +------------------------------------------------------ + +To get rid of these lags there currently are two options: + +* Run ``powerline-daemon``. Powerline does not automatically start it for you. +* Compile and install ``libzpython`` module that lives in + https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. + +Prompt is spoiled after completing files in ksh +----------------------------------------------- + +This is exactly why powerline has official mksh support, but not official ksh +support. If you know the solution feel free to share it in `powerline bug +tracker`_. + +When using z powerline shows wrong number of jobs +------------------------------------------------- + +This happens because `z `_ is launching some jobs in +the background from ``$POWERLINE_COMMAND`` and these jobs fail to finish before +powerline prompt is run. + +Solution to this problem is simple: be sure that :file:`z.sh` is sourced +strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background +jobs are spawned by `z `_ after powerline has done +its job. + +Vim issues +========== + My vim statusline has strange characters like ``^B`` in it! ----------------------------------------------------------- @@ -148,34 +179,6 @@ My vim statusline is not displayed completely and has too much spaces * Alternative: set :ref:`ambiwidth ` to 2, remove fancy dividers (they suck when ``ambiwidth`` is set to double). -When using z powerline shows wrong number of jobs -------------------------------------------------- - -This happens because `z `_ is launching some jobs in -the background from ``$POWERLINE_COMMAND`` and these jobs fail to finish before -powerline prompt is run. - -Solution to this problem is simple: be sure that :file:`z.sh` is sourced -strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background -jobs are spawned by `z `_ after powerline has done -its job. - -I am suffering bad lags before displaying shell prompt ------------------------------------------------------- - -To get rid of these lags there currently are two options: - -* Run ``powerline-daemon``. Powerline does not automatically start it for you. -* Compile and install ``libzpython`` module that lives in - https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. - -Prompt is spoiled after completing files in ksh ------------------------------------------------ - -This is exactly why powerline has official mksh support, but not official ksh -support. If you know the solution feel free to share it in `powerline bug -tracker`_. - Powerline loses color after editing vimrc ----------------------------------------- From fe6cd47bdd6766f831c436f5bf325b384017604e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 22:28:44 +0400 Subject: [PATCH 1388/1472] =?UTF-8?q?Also=20add=20=E2=80=9CShell=20issues?= =?UTF-8?q?=E2=80=9D=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/troubleshooting.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 2ca42a5f..ea78c28b 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -120,6 +120,9 @@ make powerline package importable anywhere: use pip install --user --editable path/to/powerline +Shell issues +============ + I am suffering bad lags before displaying shell prompt ------------------------------------------------------ From e02647560a89b134394984c70760a1a9b02ef300 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 22:30:59 +0400 Subject: [PATCH 1389/1472] Move LANG setting to run function, remove useless BINDFILE variable --- tests/test_shells/test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index f717fb7b..00479dcf 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -60,6 +60,7 @@ run() { local additional_prompts= fi env -i \ + LANG=en_US.UTF-8 \ PATH="$local_path" \ TERM="${TERM}" \ COLUMNS="${COLUMNS}" \ @@ -87,7 +88,7 @@ run_test() { run "${TEST_TYPE}" "${TEST_CLIENT}" "${SH}" \ screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ - env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "$@" + "$@" while ! screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH ; do sleep 0.1s done From 8f7081176891773b878477563857c9d0ec356b53 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 22:33:14 +0400 Subject: [PATCH 1390/1472] Add another answer to troubleshooting section --- docs/source/troubleshooting.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index ea78c28b..120d7107 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -151,6 +151,14 @@ strictly after :file:`powerline/bindings/bash/powerline.sh`. This way background jobs are spawned by `z `_ after powerline has done its job. +When using shell I do not see powerline fancy characters +-------------------------------------------------------- + +If your locale encoding is not unicode (any encoding that starts with “utf” or +“ucs” will work, case is ignored) powerline falls back to ascii-only theme. You +should set up your system to use unicode locale or forget about powerline fancy +characters. + Vim issues ========== From 9658e45eee5946174749207f8b7123c04a21490f Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 27 Aug 2014 23:42:04 +0400 Subject: [PATCH 1391/1472] Use &encoding as powerline encoding in Vim Among other benefits (i.e. correct fall back to ascii theme when Vim uses non-unicode encoding) this should also fix travis tests: apparently travis is not setting LANG or similar environment variables to some unicode locale, so tests are failing. --- powerline/__init__.py | 12 +++++++++--- powerline/bindings/config.py | 4 +++- powerline/vim.py | 11 +++++++++-- tests/test_local_overrides.vim | 1 + tests/test_plugin_file.vim | 2 +- tests/test_tabline.vim | 1 + tests/vim.py | 1 + 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 428e3d46..1bc6fa06 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -225,7 +225,7 @@ def create_logger(common_config): return logger -def finish_common_config(common_config): +def finish_common_config(encoding, common_config): '''Add default values to common config and expand ~ in paths :param dict common_config: @@ -235,7 +235,7 @@ def finish_common_config(common_config): Copy of common configuration with all configuration keys and expanded paths. ''' - encoding = getpreferredencoding().lower() + encoding = encoding.lower() if encoding.startswith('utf') or encoding.startswith('ucs'): default_top_theme = 'powerline' else: @@ -407,6 +407,12 @@ class Powerline(object): self.setup_kwargs = {} self.imported_modules = set() + get_encoding = staticmethod(getpreferredencoding) + '''Get encoding used by the current application + + Usually returns encoding of the current locale. + ''' + def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): '''(Re)create renderer object. Can be used after Powerline object was successfully initialized. If any of the below parameters except @@ -431,7 +437,7 @@ class Powerline(object): if load_main: self._purge_configs('main') config = self.load_main_config() - self.common_config = finish_common_config(config['common']) + self.common_config = finish_common_config(self.get_encoding(), config['common']) if self.common_config != self.prev_common_config: common_config_differs = True diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index d5ec8400..042a6e9c 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -6,6 +6,8 @@ import os import re import sys +from locale import getpreferredencoding + from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY from powerline.lib.config import ConfigLoader from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config @@ -83,7 +85,7 @@ def get_main_config(args): def create_powerline_logger(args): config = get_main_config(args) - common_config = finish_common_config(config['common']) + common_config = finish_common_config(getpreferredencoding(), config['common']) logger = create_logger(common_config) return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config') diff --git a/powerline/vim.py b/powerline/vim.py index ecea537b..56159f00 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -3,11 +3,14 @@ from __future__ import absolute_import import sys + +from itertools import count + +import vim + from powerline.bindings.vim import vim_get_func, vim_getvar from powerline import Powerline, FailedUnicode from powerline.lib import mergedicts -import vim -from itertools import count if not hasattr(vim, 'bindeval'): import json @@ -71,6 +74,10 @@ class VimPowerline(Powerline): self.setup_kwargs.setdefault('_local_themes', []).append((key, config)) return True + @staticmethod + def get_encoding(): + return vim.eval('&encoding') + def load_main_config(self): return _override_from(super(VimPowerline, self).load_main_config(), 'powerline_config_overrides') diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index bf2ad8f8..050e393e 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -1,4 +1,5 @@ #!/usr/bin/vim -S +set encoding=utf-8 let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' let g:powerline_config_overrides = {'common': {'default_top_theme': 'ascii'}} let g:powerline_theme_overrides__default = {'segment_data': {'line_current_symbol': {'contents': 'LN '}, 'branch': {'before': 'B '}}} diff --git a/tests/test_plugin_file.vim b/tests/test_plugin_file.vim index 76e08bd8..6c99c5d0 100755 --- a/tests/test_plugin_file.vim +++ b/tests/test_plugin_file.vim @@ -1,5 +1,5 @@ #!/usr/bin/vim -S -set nocompatible +set encoding=utf-8 tabedit abc tabedit def try diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 8e2cca40..42e27e76 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -1,4 +1,5 @@ #!/usr/bin/vim -S +set encoding=utf-8 source powerline/bindings/vim/plugin/powerline.vim edit abc tabedit def diff --git a/tests/vim.py b/tests/vim.py index 15cdf7b6..d8ac109f 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -9,6 +9,7 @@ options = { 'paste': 0, 'ambiwidth': 'single', 'columns': 80, + 'encoding': 'utf-8', } _last_bufnr = 0 _highlights = {} From 323f0c4e2275e2215fe18d0da2ae5cb90a3f48cb Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 28 Aug 2014 23:58:22 +0400 Subject: [PATCH 1392/1472] Use __import__ checks for skipping bzr/mercurial Closes #1015 --- tests/test_lib.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 07e356a0..4b078ca5 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,23 +1,39 @@ # vim:fileencoding=utf-8:noet from __future__ import division +import threading +import os +import re + +from time import sleep +from subprocess import call, PIPE + from powerline.lib import mergedicts, add_divider_highlight_group, REMOVE_THIS_KEY from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.vcs import guess, get_fallback_create_watcher from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.lib.monotonic import monotonic from powerline.lib.vcs.git import git_directory -import threading -import os -import sys -import re -import platform -from time import sleep -from subprocess import call, PIPE + from tests.lib import Pl from tests import TestCase +try: + __import__('bzrlib') +except ImportError: + use_bzr = False +else: + use_bzr = True + +try: + __import__('mercurial') +except ImportError: + use_mercurial = False +else: + use_mercurial = True + + def thread_number(): return len(threading.enumerate()) @@ -375,10 +391,6 @@ class TestLib(TestCase): self.assertEqual(humanize_bytes(1000000000, si_prefix=False), '953.7 MiB') -use_mercurial = use_bzr = (sys.version_info < (3, 0) - and platform.python_implementation() == 'CPython') - - class TestVCS(TestCase): def do_branch_rename_test(self, repo, q): st = monotonic() From 70fabdc02b39467320a8935a496b87d71c50c159 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 28 Aug 2014 23:59:47 +0400 Subject: [PATCH 1393/1472] Use SkipTest for skipping tests --- tests/test_lib.py | 170 +++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 84 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 4b078ca5..612d120d 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -16,7 +16,7 @@ from powerline.lib.monotonic import monotonic from powerline.lib.vcs.git import git_directory from tests.lib import Pl -from tests import TestCase +from tests import TestCase, SkipTest try: @@ -460,94 +460,96 @@ class TestVCS(TestCase): os.remove(dotgit) os.rename(spacegit, dotgit) - if use_mercurial: - def test_mercurial(self): - create_watcher = get_fallback_create_watcher() - repo = guess(path=HG_REPO, create_watcher=create_watcher) - self.assertNotEqual(repo, None) - self.assertEqual(repo.branch(), 'default') - self.assertEqual(repo.status(), None) - with open(os.path.join(HG_REPO, 'file'), 'w') as f: - f.write('abc') - f.flush() - self.assertEqual(repo.status(), ' U') - self.assertEqual(repo.status('file'), 'U') - call(['hg', 'add', '.'], cwd=HG_REPO, stdout=PIPE) - self.assertEqual(repo.status(), 'D ') - self.assertEqual(repo.status('file'), 'A') - os.remove(os.path.join(HG_REPO, 'file')) - - if use_bzr: - def test_bzr(self): - create_watcher = get_fallback_create_watcher() - repo = guess(path=BZR_REPO, create_watcher=create_watcher) - self.assertNotEqual(repo, None, 'No bzr repo found. Do you have bzr installed?') - self.assertEqual(repo.branch(), 'test_powerline') - self.assertEqual(repo.status(), None) - with open(os.path.join(BZR_REPO, 'file'), 'w') as f: - f.write('abc') + def test_mercurial(self): + if not use_mercurial: + raise SkipTest('Mercurial is not available') + create_watcher = get_fallback_create_watcher() + repo = guess(path=HG_REPO, create_watcher=create_watcher) + self.assertNotEqual(repo, None) + self.assertEqual(repo.branch(), 'default') + self.assertEqual(repo.status(), None) + with open(os.path.join(HG_REPO, 'file'), 'w') as f: + f.write('abc') + f.flush() self.assertEqual(repo.status(), ' U') - self.assertEqual(repo.status('file'), '? ') - call(['bzr', 'add', '-q', '.'], cwd=BZR_REPO, stdout=PIPE) + self.assertEqual(repo.status('file'), 'U') + call(['hg', 'add', '.'], cwd=HG_REPO, stdout=PIPE) self.assertEqual(repo.status(), 'D ') - self.assertEqual(repo.status('file'), '+N') - call(['bzr', 'commit', '-q', '-m', 'initial commit'], cwd=BZR_REPO) - self.assertEqual(repo.status(), None) - with open(os.path.join(BZR_REPO, 'file'), 'w') as f: - f.write('def') - self.assertEqual(repo.status(), 'D ') - self.assertEqual(repo.status('file'), ' M') - self.assertEqual(repo.status('notexist'), None) - with open(os.path.join(BZR_REPO, 'ignored'), 'w') as f: - f.write('abc') - self.assertEqual(repo.status('ignored'), '? ') - # Test changing the .bzrignore file should update status - with open(os.path.join(BZR_REPO, '.bzrignore'), 'w') as f: - f.write('ignored') - self.assertEqual(repo.status('ignored'), None) - # Test changing the dirstate file should invalidate the cache for - # all files in the repo - with open(os.path.join(BZR_REPO, 'file2'), 'w') as f: - f.write('abc') - call(['bzr', 'add', 'file2'], cwd=BZR_REPO, stdout=PIPE) - call(['bzr', 'commit', '-q', '-m', 'file2 added'], cwd=BZR_REPO) - with open(os.path.join(BZR_REPO, 'file'), 'a') as f: - f.write('hello') - with open(os.path.join(BZR_REPO, 'file2'), 'a') as f: - f.write('hello') - self.assertEqual(repo.status('file'), ' M') - self.assertEqual(repo.status('file2'), ' M') - call(['bzr', 'commit', '-q', '-m', 'multi'], cwd=BZR_REPO) - self.assertEqual(repo.status('file'), None) - self.assertEqual(repo.status('file2'), None) + self.assertEqual(repo.status('file'), 'A') + os.remove(os.path.join(HG_REPO, 'file')) - # Test changing branch - call(['bzr', 'nick', 'branch1'], cwd=BZR_REPO, stdout=PIPE, stderr=PIPE) - self.do_branch_rename_test(repo, 'branch1') + def test_bzr(self): + if not use_bzr: + raise SkipTest('Bazaar is not available') + create_watcher = get_fallback_create_watcher() + repo = guess(path=BZR_REPO, create_watcher=create_watcher) + self.assertNotEqual(repo, None, 'No bzr repo found. Do you have bzr installed?') + self.assertEqual(repo.branch(), 'test_powerline') + self.assertEqual(repo.status(), None) + with open(os.path.join(BZR_REPO, 'file'), 'w') as f: + f.write('abc') + self.assertEqual(repo.status(), ' U') + self.assertEqual(repo.status('file'), '? ') + call(['bzr', 'add', '-q', '.'], cwd=BZR_REPO, stdout=PIPE) + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), '+N') + call(['bzr', 'commit', '-q', '-m', 'initial commit'], cwd=BZR_REPO) + self.assertEqual(repo.status(), None) + with open(os.path.join(BZR_REPO, 'file'), 'w') as f: + f.write('def') + self.assertEqual(repo.status(), 'D ') + self.assertEqual(repo.status('file'), ' M') + self.assertEqual(repo.status('notexist'), None) + with open(os.path.join(BZR_REPO, 'ignored'), 'w') as f: + f.write('abc') + self.assertEqual(repo.status('ignored'), '? ') + # Test changing the .bzrignore file should update status + with open(os.path.join(BZR_REPO, '.bzrignore'), 'w') as f: + f.write('ignored') + self.assertEqual(repo.status('ignored'), None) + # Test changing the dirstate file should invalidate the cache for + # all files in the repo + with open(os.path.join(BZR_REPO, 'file2'), 'w') as f: + f.write('abc') + call(['bzr', 'add', 'file2'], cwd=BZR_REPO, stdout=PIPE) + call(['bzr', 'commit', '-q', '-m', 'file2 added'], cwd=BZR_REPO) + with open(os.path.join(BZR_REPO, 'file'), 'a') as f: + f.write('hello') + with open(os.path.join(BZR_REPO, 'file2'), 'a') as f: + f.write('hello') + self.assertEqual(repo.status('file'), ' M') + self.assertEqual(repo.status('file2'), ' M') + call(['bzr', 'commit', '-q', '-m', 'multi'], cwd=BZR_REPO) + self.assertEqual(repo.status('file'), None) + self.assertEqual(repo.status('file2'), None) - # Test branch name/status changes when swapping repos - for x in ('b1', 'b2'): - d = os.path.join(BZR_REPO, x) - os.mkdir(d) - call(['bzr', 'init', '-q'], cwd=d) - call(['bzr', 'nick', '-q', x], cwd=d) - repo = guess(path=d, create_watcher=create_watcher) - self.assertEqual(repo.branch(), x) + # Test changing branch + call(['bzr', 'nick', 'branch1'], cwd=BZR_REPO, stdout=PIPE, stderr=PIPE) + self.do_branch_rename_test(repo, 'branch1') + + # Test branch name/status changes when swapping repos + for x in ('b1', 'b2'): + d = os.path.join(BZR_REPO, x) + os.mkdir(d) + call(['bzr', 'init', '-q'], cwd=d) + call(['bzr', 'nick', '-q', x], cwd=d) + repo = guess(path=d, create_watcher=create_watcher) + self.assertEqual(repo.branch(), x) + self.assertFalse(repo.status()) + if x == 'b1': + open(os.path.join(d, 'dirty'), 'w').close() + self.assertTrue(repo.status()) + os.rename(os.path.join(BZR_REPO, 'b1'), os.path.join(BZR_REPO, 'b')) + os.rename(os.path.join(BZR_REPO, 'b2'), os.path.join(BZR_REPO, 'b1')) + os.rename(os.path.join(BZR_REPO, 'b'), os.path.join(BZR_REPO, 'b2')) + for x, y in (('b1', 'b2'), ('b2', 'b1')): + d = os.path.join(BZR_REPO, x) + repo = guess(path=d, create_watcher=create_watcher) + self.do_branch_rename_test(repo, y) + if x == 'b1': self.assertFalse(repo.status()) - if x == 'b1': - open(os.path.join(d, 'dirty'), 'w').close() - self.assertTrue(repo.status()) - os.rename(os.path.join(BZR_REPO, 'b1'), os.path.join(BZR_REPO, 'b')) - os.rename(os.path.join(BZR_REPO, 'b2'), os.path.join(BZR_REPO, 'b1')) - os.rename(os.path.join(BZR_REPO, 'b'), os.path.join(BZR_REPO, 'b2')) - for x, y in (('b1', 'b2'), ('b2', 'b1')): - d = os.path.join(BZR_REPO, x) - repo = guess(path=d, create_watcher=create_watcher) - self.do_branch_rename_test(repo, y) - if x == 'b1': - self.assertFalse(repo.status()) - else: - self.assertTrue(repo.status()) + else: + self.assertTrue(repo.status()) old_HGRCPATH = None old_cwd = None From 53ab31eeb869d239112a3cd46a028c9e02a5ec58 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 00:28:41 +0400 Subject: [PATCH 1394/1472] Use setUpClass/tearDownClass to deal with VCS repositories --- tests/test_lib.py | 89 ++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 612d120d..ea16e38c 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -34,6 +34,11 @@ else: use_mercurial = True +GIT_REPO = 'git_repo' + os.environ.get('PYTHON', '') +HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') +BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') + + def thread_number(): return len(threading.enumerate()) @@ -551,55 +556,43 @@ class TestVCS(TestCase): else: self.assertTrue(repo.status()) -old_HGRCPATH = None -old_cwd = None + @classmethod + def setUpClass(cls): + cls.powerline_old_cwd = os.getcwd() + os.chdir(os.path.dirname(__file__)) + call(['git', 'init', '--quiet', GIT_REPO]) + assert os.path.isdir(GIT_REPO) + call(['git', 'config', '--local', 'user.name', 'Foo'], cwd=GIT_REPO) + call(['git', 'config', '--local', 'user.email', 'bar@example.org'], cwd=GIT_REPO) + call(['git', 'commit', '--allow-empty', '--message', 'Initial commit', '--quiet'], cwd=GIT_REPO) + if use_mercurial: + cls.powerline_old_HGRCPATH = os.environ.get('HGRCPATH') + os.environ['HGRCPATH'] = '' + call(['hg', 'init', HG_REPO]) + with open(os.path.join(HG_REPO, '.hg', 'hgrc'), 'w') as hgrc: + hgrc.write('[ui]\n') + hgrc.write('username = Foo \n') + if use_bzr: + call(['bzr', 'init', '--quiet', BZR_REPO]) + call(['bzr', 'config', 'email=Foo '], cwd=BZR_REPO) + call(['bzr', 'config', 'nickname=test_powerline'], cwd=BZR_REPO) + call(['bzr', 'config', 'create_signatures=0'], cwd=BZR_REPO) - -GIT_REPO = 'git_repo' + os.environ.get('PYTHON', '') -HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') -BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') - - -def setUpModule(): - global old_cwd - global old_HGRCPATH - old_cwd = os.getcwd() - os.chdir(os.path.dirname(__file__)) - call(['git', 'init', '--quiet', GIT_REPO]) - assert os.path.isdir(GIT_REPO) - call(['git', 'config', '--local', 'user.name', 'Foo'], cwd=GIT_REPO) - call(['git', 'config', '--local', 'user.email', 'bar@example.org'], cwd=GIT_REPO) - call(['git', 'commit', '--allow-empty', '--message', 'Initial commit', '--quiet'], cwd=GIT_REPO) - if use_mercurial: - old_HGRCPATH = os.environ.get('HGRCPATH') - os.environ['HGRCPATH'] = '' - call(['hg', 'init', HG_REPO]) - with open(os.path.join(HG_REPO, '.hg', 'hgrc'), 'w') as hgrc: - hgrc.write('[ui]\n') - hgrc.write('username = Foo \n') - if use_bzr: - call(['bzr', 'init', '--quiet', BZR_REPO]) - call(['bzr', 'config', 'email=Foo '], cwd=BZR_REPO) - call(['bzr', 'config', 'nickname=test_powerline'], cwd=BZR_REPO) - call(['bzr', 'config', 'create_signatures=0'], cwd=BZR_REPO) - - -def tearDownModule(): - global old_cwd - global old_HGRCPATH - for repo_dir in [GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): - for root, dirs, files in list(os.walk(repo_dir, topdown=False)): - for file in files: - os.remove(os.path.join(root, file)) - for dir in dirs: - os.rmdir(os.path.join(root, dir)) - os.rmdir(repo_dir) - if use_mercurial: - if old_HGRCPATH is None: - os.environ.pop('HGRCPATH') - else: - os.environ['HGRCPATH'] = old_HGRCPATH - os.chdir(old_cwd) + @classmethod + def tearDownClass(cls): + for repo_dir in [GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): + for root, dirs, files in list(os.walk(repo_dir, topdown=False)): + for file in files: + os.remove(os.path.join(root, file)) + for dir in dirs: + os.rmdir(os.path.join(root, dir)) + os.rmdir(repo_dir) + if use_mercurial: + if cls.powerline_old_HGRCPATH is None: + os.environ.pop('HGRCPATH') + else: + os.environ['HGRCPATH'] = cls.powerline_old_HGRCPATH + os.chdir(cls.powerline_old_cwd) if __name__ == '__main__': From 125f3097240ba2b050ad3cfd623789b2a2a42846 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 18:32:58 +0400 Subject: [PATCH 1395/1472] Fix width used for tabline Fixes #1033 --- powerline/renderers/vim.py | 7 +++++-- tests/test_tabline.vim | 14 +++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index cc89a707..89f54630 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -2,7 +2,7 @@ from __future__ import absolute_import, unicode_literals -from powerline.bindings.vim import vim_get_func, environ, current_tabpage +from powerline.bindings.vim import vim_get_func, vim_getoption, environ, current_tabpage from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme @@ -108,7 +108,10 @@ class VimRenderer(Renderer): ) segment_info['tabnr'] = segment_info['tabpage'].number segment_info['bufnr'] = segment_info['buffer'].number - winwidth = segment_info['window'].width + if is_tabline: + winwidth = int(vim_getoption('columns')) + else: + winwidth = segment_info['window'].width statusline = super(VimRenderer, self).render( mode=mode, diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 42e27e76..b4bc4112 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -23,7 +23,7 @@ tabonly! try let result = eval(&tabline[2:]) catch - call writefile(['Exception while evaluating &tabline', v:exception], 'message.fail') + call writefile(['Exception while evaluating &tabline (2)', v:exception], 'message.fail') cquit endtry @@ -32,4 +32,16 @@ if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_25 cquit endif +try + vsplit + let result = eval(&tabline[2:]) +catch + call writefile(['Exception while evaluating &tabline (3)', v:exception], 'message.fail') +endtry + +if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' + call writefile(['Unexpected tabline (3)', result], 'message.fail') + cquit +endif + qall! From 8fb09bd8b3453902ca57dbdbe004b4b6d63dcf1f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 18:40:25 +0400 Subject: [PATCH 1396/1472] Add mergedefaults function that does not override existing keys --- powerline/lib/__init__.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 252b1e2e..fcf9bf8f 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -15,7 +15,9 @@ def wraps_saveargs(wrapped): def mergedicts(d1, d2): - '''Recursively merge two dictionaries. First dictionary is modified in-place. + '''Recursively merge two dictionaries + + First dictionary is modified in-place. ''' for k in d2: if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): @@ -26,6 +28,18 @@ def mergedicts(d1, d2): d1[k] = d2[k] +def mergedefaults(d1, d2): + '''Recursively merge two dictionaries, keeping existing values + + First dictionary is modified in-place. + ''' + for k in d2: + if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): + mergedefaults(d1[k], d2[k]) + else: + d1.setdefault(k, d2[k]) + + def mergedicts_copy(d1, d2): '''Recursively merge two dictionaries. From af10fa1e80c572c044104c7c44bbac06a174c48d Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 18:40:43 +0400 Subject: [PATCH 1397/1472] Fix typo in mergedicts_copy docstring --- powerline/lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index fcf9bf8f..7a0d7add 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -44,7 +44,7 @@ def mergedicts_copy(d1, d2): '''Recursively merge two dictionaries. Dictionaries are not modified. Copying happens only if necessary. Assumes - that first dictionary support .copy() method. + that first dictionary supports .copy() method. ''' ret = d1.copy() for k in d2: From e273287eab6e73af01b53203393b3deb7bf90295 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 19:17:14 +0400 Subject: [PATCH 1398/1472] Check for presence of powerline_requires_* attributes In powerline.lint.inspect their value is checked, while in other place `hasattr` is used. This commit removes this inconsistency. --- powerline/lint/inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 58d0dc05..575b4794 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -31,8 +31,8 @@ def getconfigargspec(obj): if len(arg) > 1: defaults.append(arg[1]) - requires_segment_info = getattr(obj, 'powerline_requires_segment_info', False) - requires_filesystem_watcher = getattr(obj, 'powerline_requires_filesystem_watcher', False) + requires_segment_info = hasattr(obj, 'powerline_requires_segment_info') + requires_filesystem_watcher = hasattr(obj, 'powerline_requires_filesystem_watcher') for name, method in argspecobjs: argspec = getargspec(method) From cde39f00c4b266105d130df088f5c914dab11b8c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 19:17:53 +0400 Subject: [PATCH 1399/1472] Fix errors reported by syntastic in powerline.lint.inspect --- powerline/lint/inspect.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 575b4794..4e7f1a10 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -2,9 +2,7 @@ from __future__ import absolute_import from inspect import ArgSpec, getargspec -from itertools import count -from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment from powerline.segments import Segment From 67845dc96e147241347b594615db2383c714ac1d Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 19:36:30 +0400 Subject: [PATCH 1400/1472] Add documentation for segment attributes --- docs/source/develop/segments.rst | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 1715d3a7..a17fd1b1 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -29,6 +29,41 @@ object it should receive the following arguments: And also any other argument(s) specified by user in :ref:`args key ` (no additional arguments by default). +Object representing segment may have the following attributes used by +powerline: + +``powerline_requires_segment_info`` + This attribute controls whether segment will receive ``segment_info`` + argument: if it is present argument will be received. + +``powerline_requires_filesystem_watcher`` + This attribute controls whether segment will receive ``create_watcher`` + argument: if it is present argument will be received. + +``startup`` + This attribute must be a callable which accepts the following keyword + arguments: + + * ``pl``: :py:class:`powerline.PowerlineLogger` instance which is to be used + for logging. + * ``shutdown_event``: :py:class:`Event` object which will be set when + powerline will be shut down. + * Any arguments found in user configuration for the given segment (i.e. + :ref:`args key `). + + This function is called at powerline startup when using long-running + processes (e.g. powerline in vim, in zsh with libzpython, in ipython or in + powerline daemon) and not called when ``powerline-render`` executable is + used (more specific: when :py:class:`powerline.Powerline` constructor + received true ``run_once`` argument). + +``shutdown`` + This attribute must be a callable that accepts no arguments and shuts down + threads and frees any other resources allocated in ``startup`` method of the + segment in question. + + This function is not called when ``startup`` method is not called. + This callable object should may return either a string (``unicode`` in Python2 or ``str`` in Python3, *not* ``str`` in Python2 or ``bytes`` in Python3) object or a list of dictionaries. String object is a short form of the following return From 356cb43ea27d1f0d7597cf2ce0aefebc62bcc3d3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 20:11:26 +0400 Subject: [PATCH 1401/1472] Add `powerline_segment_datas` attribute Fixes #1035 --- docs/source/configuration/reference.rst | 2 + docs/source/develop/segments.rst | 11 +++++ powerline/__init__.py | 10 +++-- powerline/segment.py | 19 ++++++-- powerline/theme.py | 11 ++++- tests/test_configuration.py | 59 ++++++++++++++++++++++--- 6 files changed, 99 insertions(+), 13 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 9277e4f9..d4e8e432 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -265,6 +265,8 @@ extension-specific key ` or from :ref:`default_top_theme common configuration key `. Powerline ships with the following top themes: +.. _config-top_themes-list: + ========================== ==================================================== Theme Description ========================== ==================================================== diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index a17fd1b1..fe802818 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -40,6 +40,17 @@ powerline: This attribute controls whether segment will receive ``create_watcher`` argument: if it is present argument will be received. +``powerline_segment_datas`` + This attribute must be a dictionary containing ``top_theme: segment_data`` + mapping where ``top_theme`` is any theme name (it is expected that all of + the names from :ref:`top-level themes list ` are + present) and ``segment_data`` is a dictionary like the one that is contained + inside :ref:`segment_data dictionary in configuration + `. This attribute should be used to specify + default theme-specific values for *third-party* segments: powerline + theme-specific values go directly to :ref:`top-level themes + `. + ``startup`` This attribute must be a callable which accepts the following keyword arguments: diff --git a/powerline/__init__.py b/powerline/__init__.py index 1bc6fa06..d94329d2 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -485,13 +485,15 @@ class Powerline(object): self.ext_config = config['ext'][self.ext] + top_theme = ( + self.ext_config.get('top_theme') + or self.common_config['default_top_theme'] + ) self.theme_levels = ( - os.path.join('themes', ( - self.ext_config.get('top_theme') - or self.common_config['default_top_theme'] - )), + os.path.join('themes', top_theme), os.path.join('themes', self.ext, '__main__'), ) + self.renderer_options['theme_kwargs']['top_theme'] = top_theme if self.ext_config != self.prev_ext_config: ext_config_differs = True diff --git a/powerline/segment.py b/powerline/segment.py index d75be664..7513a92b 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -4,7 +4,7 @@ from __future__ import absolute_import, unicode_literals, division, print_functi from powerline.lib.watcher import create_file_watcher -def list_segment_key_values(segment, theme_configs, key, module=None, default=None): +def list_segment_key_values(segment, theme_configs, segment_data, key, module=None, default=None): try: yield segment[key] except KeyError: @@ -32,6 +32,11 @@ def list_segment_key_values(segment, theme_configs, key, module=None, default=No yield segment_data[name][key] except KeyError: pass + if segment_data is not None: + try: + yield segment_data[key] + except KeyError: + pass yield default @@ -173,14 +178,15 @@ def process_segment(pl, side, segment_info, parsed_segments, segment, mode): parsed_segments.append(segment) -def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr): +def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr, top_theme): data = { 'default_module': default_module or 'powerline.segments.' + ext, 'get_module_attr': get_module_attr, + 'segment_data': None, } def get_key(merge, segment, module, key, default=None): - return get_segment_key(merge, segment, theme_configs, key, module, default) + return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, module, default) data['get_key'] = get_key def get(segment, side): @@ -199,6 +205,13 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge if not get_key(False, segment, module, 'display', True): return None + segment_datas = getattr(_contents_func, 'powerline_segment_datas', None) + if segment_datas: + try: + data['segment_data'] = segment_datas[top_theme] + except KeyError: + pass + if segment_type == 'function': highlight_group = [module + '.' + segment['name'], segment['name']] else: diff --git a/powerline/theme.py b/powerline/theme.py index fe494457..2c15886d 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -30,6 +30,7 @@ class Theme(object): common_config, pl, get_module_attr, + top_theme, main_theme_config=None, run_once=False, shutdown_event=None): @@ -54,7 +55,15 @@ class Theme(object): theme_configs = [theme_config] if main_theme_config: theme_configs.append(main_theme_config) - get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module'), get_module_attr) + get_segment = gen_segment_getter( + pl, + ext, + common_config, + theme_configs, + theme_config.get('default_module'), + get_module_attr, + top_theme + ) for segdict in itertools.chain((theme_config['segments'],), theme_config['segments'].get('above', ())): self.segments.append(new_empty_segment_line()) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 39bb9d20..87817ed6 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,13 +1,18 @@ # vim:fileencoding=utf-8:noet from __future__ import unicode_literals, absolute_import, division -import tests.vim as vim_module -from tests import TestCase -from tests.lib.config_mock import get_powerline, get_powerline_raw, swap_attributes -from functools import wraps -from copy import deepcopy + import sys import os +from functools import wraps +from copy import deepcopy + +import tests.vim as vim_module + +from tests import TestCase +from tests.lib.config_mock import get_powerline, get_powerline_raw, swap_attributes +from tests.lib import Args + def highlighted_string(s, group, **kwargs): ret = { @@ -428,6 +433,50 @@ class TestModes(TestRender): self.assertRenderEqual(p, '{56} s2{6-}>>{--}', mode='m3') +class TestSegmentAttributes(TestRender): + @add_args + def test_no_attributes(self, p, config): + def m1(divider=',', **kwargs): + return divider.join(kwargs.keys()) + divider + sys.modules['bar'] = Args(m1=m1) + config['themes/test/default']['segments'] = { + 'left': [ + { + 'name': 'm1', + 'module': 'bar' + } + ] + } + self.assertRenderEqual(p, '{56} pl,{6-}>>{--}') + + @add_args + def test_segment_datas(self, p, config): + def m1(divider=',', **kwargs): + return divider.join(kwargs.keys()) + divider + m1.powerline_segment_datas = { + 'powerline': { + 'args': { + 'divider': ';' + } + }, + 'ascii': { + 'args': { + 'divider': '--' + } + } + } + sys.modules['bar'] = Args(m1=m1) + config['themes/test/default']['segments'] = { + 'left': [ + { + 'name': 'm1', + 'module': 'bar' + } + ] + } + self.assertRenderEqual(p, '{56} pl;{6-}>>{--}') + + class TestVim(TestCase): def test_environ_update(self): # Regression test: test that segment obtains environment from vim, not From c8d052ad234a281a119b030f29e561b91ad06ee4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 20:33:06 +0400 Subject: [PATCH 1402/1472] Add `unicode_text` argument for fuzzy_time to top-level themes --- powerline/config_files/themes/ascii.json | 5 +++++ powerline/config_files/themes/powerline.json | 5 +++++ powerline/config_files/themes/unicode.json | 5 +++++ powerline/config_files/themes/unicode_terminus.json | 5 +++++ .../config_files/themes/unicode_terminus_condensed.json | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index c8ff0d71..d1c4de04 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -80,6 +80,11 @@ } } }, + "powerline.segments.common.fuzzy_time": { + "args": { + "unicode_text": false + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index e076f770..7f83ec3b 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -79,6 +79,11 @@ } } }, + "powerline.segments.common.fuzzy_time": { + "args": { + "unicode_text": true + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json index 9a3faf7a..78f84e0b 100644 --- a/powerline/config_files/themes/unicode.json +++ b/powerline/config_files/themes/unicode.json @@ -79,6 +79,11 @@ } } }, + "powerline.segments.common.fuzzy_time": { + "args": { + "unicode_text": true + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json index 01b981f2..e1077d2e 100644 --- a/powerline/config_files/themes/unicode_terminus.json +++ b/powerline/config_files/themes/unicode_terminus.json @@ -79,6 +79,11 @@ } } }, + "powerline.segments.common.fuzzy_time": { + "args": { + "unicode_text": true + } + }, "powerline.segments.vim.mode": { "args": { diff --git a/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline/config_files/themes/unicode_terminus_condensed.json index 15f37a1a..4b32d0ca 100644 --- a/powerline/config_files/themes/unicode_terminus_condensed.json +++ b/powerline/config_files/themes/unicode_terminus_condensed.json @@ -80,6 +80,11 @@ } } }, + "powerline.segments.common.fuzzy_time": { + "args": { + "unicode_text": true + } + }, "powerline.segments.vim.mode": { "args": { From e808dd76c42f7b45a5c6c46e5e74e3b55debdd49 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 20:35:40 +0400 Subject: [PATCH 1403/1472] Specify temp_format argument for weather segment in ascii theme --- powerline/config_files/themes/ascii.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index d1c4de04..fa1927ba 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -77,7 +77,8 @@ "windy": "WINDY", "not_available": "NA", "unknown": "UKN" - } + }, + "temp_format": "{temp:.0f} C" } }, "powerline.segments.common.fuzzy_time": { From 849c34ea727c56c6c6eaae5c81bb400fc9b290f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 20:47:44 +0400 Subject: [PATCH 1404/1472] Replace all unicode defaults with defaults from ASCII theme Closes #1034 --- powerline/segments/common.py | 40 ++++++++--------- powerline/segments/vim/__init__.py | 16 +++---- tests/test_segments.py | 72 ++++++++++++++++-------------- 3 files changed, 66 insertions(+), 62 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index fff542e2..ee6e0b75 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -116,7 +116,7 @@ class CwdSegment(Segment): dir_shorten_len=None, dir_limit_depth=None, use_path_separator=False, - ellipsis='⋯', + ellipsis='...', **kwargs): cwd = self.get_shortened_path(pl, segment_info, **kwargs) cwd_split = cwd.split(os.sep) @@ -198,7 +198,7 @@ UNICODE_TEXT_TRANSLATION = { } -def fuzzy_time(pl, unicode_text=True): +def fuzzy_time(pl, unicode_text=False): '''Display the current time as fuzzy time, e.g. "quarter past six". :param bool unicode_text: @@ -423,18 +423,18 @@ weather_conditions_codes = ( # ('sunny', (32, 36)), # ('night', (31, 33))): weather_conditions_icons = { - 'day': '〇', - 'blustery': '⚑', - 'rainy': '☔', - 'cloudy': '☁', - 'snowy': '❅', - 'stormy': '☈', - 'foggy': '〰', - 'sunny': '☼', - 'night': '☾', - 'windy': '☴', - 'not_available': '�', - 'unknown': '⚠', + 'day': 'DAY', + 'blustery': 'WIND', + 'rainy': 'RAIN', + 'cloudy': 'CLOUDS', + 'snowy': 'SNOW', + 'stormy': 'STORM', + 'foggy': 'FOG', + 'sunny': 'SUN', + 'night': 'NIGHT', + 'windy': 'WINDY', + 'not_available': 'NA', + 'unknown': 'UKN', } temp_conversions = { @@ -859,7 +859,7 @@ class NetworkLoadSegment(KwThreadedSegment): idata['last'] = (monotonic(), _get_bytes(interface)) return idata.copy() - def render_one(self, idata, recv_format='⬇ {value:>8}', sent_format='⬆ {value:>8}', suffix='B/s', si_prefix=False, **kwargs): + def render_one(self, idata, recv_format='DL {value:>8}', sent_format='UL {value:>8}', suffix='B/s', si_prefix=False, **kwargs): if not idata or 'prev' not in idata: return None @@ -1000,10 +1000,10 @@ Highlight groups used: ``email_alert_gradient`` (gradient), ``email_alert``. STATE_SYMBOLS = { - 'fallback': '♫', - 'play': '▶', - 'pause': '▮▮', - 'stop': '■', + 'fallback': '', + 'play': '>', + 'pause': '~', + 'stop': 'X', } @@ -1411,7 +1411,7 @@ def _get_capacity(pl): return _get_capacity(pl) -def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='♥', empty_heart='♥'): +def battery(pl, format='{capacity:3.0%}', steps=5, gamify=False, full_heart='O', empty_heart='O'): '''Return battery charge status. :param str format: diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 65118b2f..c973af1d 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -41,16 +41,16 @@ vim_funcs = { vim_modes = { 'n': 'NORMAL', - 'no': 'N·OPER', + 'no': 'N-OPER', 'v': 'VISUAL', - 'V': 'V·LINE', - '^V': 'V·BLCK', + 'V': 'V-LINE', + '^V': 'V-BLCK', 's': 'SELECT', - 'S': 'S·LINE', - '^S': 'S·BLCK', + 'S': 'S-LINE', + '^S': 'S-BLCK', 'i': 'INSERT', 'R': 'REPLACE', - 'Rv': 'V·RPLCE', + 'Rv': 'V-RPLCE', 'c': 'COMMND', 'cv': 'VIM EX', 'ce': 'EX', @@ -107,7 +107,7 @@ def mode(pl, segment_info, override=None): @window_cached @requires_segment_info -def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='C:{vcols}', v_text_multiline='L:{rows}', V_text='L:{rows}'): +def visual_range(pl, segment_info, CTRL_V_text='{rows} x {vcols}', v_text_oneline='C:{vcols}', v_text_multiline='L:{rows}', V_text='L:{rows}'): '''Return the current visual selection range. :param str CTRL_V_text: @@ -197,7 +197,7 @@ def paste_indicator(pl, segment_info, text='PASTE'): @requires_segment_info -def readonly_indicator(pl, segment_info, text=''): +def readonly_indicator(pl, segment_info, text='RO'): '''Return a read-only indicator. :param string text: diff --git a/tests/test_segments.py b/tests/test_segments.py index 9320fb52..0f143bda 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -212,28 +212,28 @@ class TestShell(TestCase): {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3, shorten_home=False), [ - {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), [ - {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) - self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis='...'), [ - {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis='---'), [ + {'contents': '---', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis=None), [ {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True), [ - {'contents': '⋯/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': '.../', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) - self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis='...'), [ - {'contents': '.../', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis='---'), [ + {'contents': '---/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(shell.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis=None), [ @@ -412,28 +412,28 @@ class TestCommon(TestCase): {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=3, shorten_home=False), [ - {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'ghi', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'foo', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1), [ - {'contents': '⋯', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis='...'), [ - {'contents': '...', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis='---'), [ + {'contents': '---', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, ellipsis=None), [ {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': True, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True), [ - {'contents': '⋯/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + {'contents': '.../', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) - self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis='...'), [ - {'contents': '.../', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, + self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis='---'), [ + {'contents': '---/', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False}, {'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'draw_inner_divider': False, 'highlight_group': ['cwd:current_folder', 'cwd']} ]) self.assertEqual(common.cwd(pl=pl, segment_info=segment_info, dir_limit_depth=1, use_path_separator=True, ellipsis=None), [ @@ -491,13 +491,17 @@ class TestCommon(TestCase): time.minute = 59 self.assertEqual(common.fuzzy_time(pl=pl), 'round about midnight') time.minute = 33 - self.assertEqual(common.fuzzy_time(pl=pl), 'twenty‐five to twelve') + self.assertEqual(common.fuzzy_time(pl=pl), 'twenty-five to twelve') time.minute = 60 - self.assertEqual(common.fuzzy_time(pl=pl), 'twelve o’clock') + self.assertEqual(common.fuzzy_time(pl=pl), 'twelve o\'clock') time.minute = 33 self.assertEqual(common.fuzzy_time(pl=pl, unicode_text=False), 'twenty-five to twelve') time.minute = 60 self.assertEqual(common.fuzzy_time(pl=pl, unicode_text=False), 'twelve o\'clock') + time.minute = 33 + self.assertEqual(common.fuzzy_time(pl=pl, unicode_text=True), 'twenty‐five to twelve') + time.minute = 60 + self.assertEqual(common.fuzzy_time(pl=pl, unicode_text=True), 'twelve o’clock') def test_external_ip(self): pl = Pl() @@ -526,15 +530,15 @@ class TestCommon(TestCase): pl = Pl() with replace_attr(common, 'urllib_read', urllib_read): self.assertEqual(common.weather(pl=pl), [ - {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'CLOUDS '}, {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, temp_coldest=0, temp_hottest=100), [ - {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'CLOUDS '}, {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0} ]) self.assertEqual(common.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [ - {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'CLOUDS '}, {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100} ]) self.assertEqual(common.weather(pl=pl, icons={'cloudy': 'o'}), [ @@ -546,15 +550,15 @@ class TestCommon(TestCase): {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, unit='F'), [ - {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'CLOUDS '}, {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, unit='K'), [ - {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'CLOUDS '}, {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0} ]) self.assertEqual(common.weather(pl=pl, temp_format='{temp:.1e}C'), [ - {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '}, + {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'CLOUDS '}, {'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0} ]) @@ -621,8 +625,8 @@ class TestCommon(TestCase): while not common.network_load.interfaces.get('eth0', {}).get('prev', (None, None))[1]: sleep(0.1) self.assertEqual(common.network_load(pl=pl, interface='eth0'), [ - {'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, - {'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 'DL 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, + {'divider_highlight_group': 'background:divider', 'contents': 'UL 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']}, ]) self.assertEqual(common.network_load(pl=pl, interface='eth0', recv_format='r {value}', sent_format='s {value}'), [ {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, @@ -700,13 +704,13 @@ class TestCommon(TestCase): }]) self.assertEqual(common.battery(pl=pl, gamify=True), [ { - 'contents': '♥♥♥♥', + 'contents': 'OOOO', 'draw_inner_divider': False, 'highlight_group': ['battery_full', 'battery_gradient', 'battery'], 'gradient_level': 0 }, { - 'contents': '♥', + 'contents': 'O', 'draw_inner_divider': False, 'highlight_group': ['battery_empty', 'battery_gradient', 'battery'], 'gradient_level': 100 @@ -784,7 +788,7 @@ class TestVim(TestCase): with vim_module._with('mode', 'i') as segment_info: self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'INSERT') with vim_module._with('mode', chr(ord('V') - 0x40)) as segment_info: - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'V·BLCK') + self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'V-BLCK') self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') def test_visual_range(self): @@ -795,17 +799,17 @@ class TestVim(TestCase): with vim_module._with('mode', 'i') as segment_info: self.assertEqual(vr(segment_info=segment_info), '') with vim_module._with('mode', '^V') as segment_info: - self.assertEqual(vr(segment_info=segment_info), '1 × 1') + self.assertEqual(vr(segment_info=segment_info), '1 x 1') with vim_module._with('vpos', line=5, col=5, off=0): - self.assertEqual(vr(segment_info=segment_info), '5 × 5') + self.assertEqual(vr(segment_info=segment_info), '5 x 5') with vim_module._with('vpos', line=5, col=4, off=0): - self.assertEqual(vr(segment_info=segment_info), '5 × 4') + self.assertEqual(vr(segment_info=segment_info), '5 x 4') with vim_module._with('mode', '^S') as segment_info: - self.assertEqual(vr(segment_info=segment_info), '1 × 1') + self.assertEqual(vr(segment_info=segment_info), '1 x 1') with vim_module._with('vpos', line=5, col=5, off=0): - self.assertEqual(vr(segment_info=segment_info), '5 × 5') + self.assertEqual(vr(segment_info=segment_info), '5 x 5') with vim_module._with('vpos', line=5, col=4, off=0): - self.assertEqual(vr(segment_info=segment_info), '5 × 4') + self.assertEqual(vr(segment_info=segment_info), '5 x 4') with vim_module._with('mode', 'V') as segment_info: self.assertEqual(vr(segment_info=segment_info), 'L:1') with vim_module._with('vpos', line=5, col=5, off=0): @@ -857,7 +861,7 @@ class TestVim(TestCase): segment_info = vim_module._get_segment_info() self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), None) with vim_module._with('bufoptions', readonly=1): - self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), '') + self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), 'RO') self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info, text='L'), 'L') def test_file_scheme(self): From d138c947042f4088525a195dee1bf576b11119bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 21:05:54 +0400 Subject: [PATCH 1405/1472] Fix non-unicode error messages Ref #645 --- powerline/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index d94329d2..529826ab 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -67,9 +67,15 @@ class PowerlineLogger(object): def _log(self, attr, msg, *args, **kwargs): prefix = kwargs.get('prefix') or self.prefix prefix = self.ext + ((':' + prefix) if prefix else '') + msg = safe_unicode(msg) if args or kwargs: + args = [safe_unicode(s) if isinstance(s, bytes) else s for s in args] + kwargs = dict(( + (k, safe_unicode(v) if isinstance(v, bytes) else v) + for k, v in kwargs.items() + )) msg = msg.format(*args, **kwargs) - msg = prefix + ':' + safe_unicode(msg) + msg = prefix + ':' + msg key = attr + ':' + prefix if msg != self.last_msgs.get(key): getattr(self.logger, attr)(msg) From 4c95928c962d12bdf6038a0b745384304e272045 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 22:22:39 +0400 Subject: [PATCH 1406/1472] Split shell tests into files I am mostly interested in running all python and vim tests separately from other tests. --- tests/run_lint_tests.sh | 7 +++++++ tests/run_python_tests.sh | 9 +++++++++ tests/run_shell_tests.sh | 9 +++++++++ tests/run_vim_tests.sh | 11 +++++++++++ tests/test.sh | 26 ++++---------------------- 5 files changed, 40 insertions(+), 22 deletions(-) create mode 100755 tests/run_lint_tests.sh create mode 100755 tests/run_python_tests.sh create mode 100755 tests/run_shell_tests.sh create mode 100755 tests/run_vim_tests.sh diff --git a/tests/run_lint_tests.sh b/tests/run_lint_tests.sh new file mode 100755 index 00000000..09157910 --- /dev/null +++ b/tests/run_lint_tests.sh @@ -0,0 +1,7 @@ +#!/bin/sh +FAILED=0 +if ! ${PYTHON} scripts/powerline-lint -p powerline/config_files ; then + echo "Failed powerline-lint" + FAILED=1 +fi +exit $FAILED diff --git a/tests/run_python_tests.sh b/tests/run_python_tests.sh new file mode 100755 index 00000000..62eac30a --- /dev/null +++ b/tests/run_python_tests.sh @@ -0,0 +1,9 @@ +#!/bin/sh +FAILED=0 +for file in tests/test_*.py ; do + if ! ${PYTHON} $file --verbose --catch ; then + echo "Failed test(s) from $file" + FAILED=1 + fi +done +exit $FAILED diff --git a/tests/run_shell_tests.sh b/tests/run_shell_tests.sh new file mode 100755 index 00000000..08655312 --- /dev/null +++ b/tests/run_shell_tests.sh @@ -0,0 +1,9 @@ +#!/bin/sh +FAILED=0 +if ! sh tests/test_shells/test.sh --fast ; then + echo "Failed shells" + if ${PYTHON} -c 'import platform, sys; sys.exit(1 * (platform.python_implementation() == "PyPy"))' ; then + FAILED=1 + fi +fi +exit $FAILED diff --git a/tests/run_vim_tests.sh b/tests/run_vim_tests.sh new file mode 100755 index 00000000..ef82ab35 --- /dev/null +++ b/tests/run_vim_tests.sh @@ -0,0 +1,11 @@ +#!/bin/sh +FAILED=0 +for script in tests/*.vim ; do + if ! vim -u NONE -S $script || test -f message.fail ; then + echo "Failed script $script" >&2 + cat message.fail >&2 + rm message.fail + FAILED=1 + fi +done +exit $FAILED diff --git a/tests/test.sh b/tests/test.sh index 129e0b5e..c1195f3f 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,29 +1,11 @@ #!/bin/sh -: ${PYTHON:=python} FAILED=0 +export PYTHON="${PYTHON:=python}" export PYTHONPATH="${PYTHONPATH}:`realpath .`" -for file in tests/test_*.py ; do - if ! ${PYTHON} $file --verbose --catch ; then - echo "Failed test(s) from $file" +for script in tests/run_*_tests.sh ; do + if ! sh $script ; then + echo "Failed $script" FAILED=1 fi done -if ! ${PYTHON} scripts/powerline-lint -p powerline/config_files ; then - echo "Failed powerline-lint" - FAILED=1 -fi -for script in tests/*.vim ; do - if ! vim -u NONE -S $script || test -f message.fail ; then - echo "Failed script $script" >&2 - cat message.fail >&2 - rm message.fail - FAILED=1 - fi -done -if ! bash tests/test_shells/test.sh --fast ; then - echo "Failed shells" - if ${PYTHON} -c 'import platform, sys; sys.exit(1 * (platform.python_implementation() == "PyPy"))' ; then - FAILED=1 - fi -fi exit $FAILED From 9a0e100ca061107f4a1d190a356af74805e34fa1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 22:32:42 +0400 Subject: [PATCH 1407/1472] Specify -p argument in run function when running shell tests --- tests/test_shells/input.bash | 1 - tests/test_shells/input.busybox | 1 - tests/test_shells/input.dash | 1 - tests/test_shells/input.fish | 1 - tests/test_shells/input.mksh | 1 - tests/test_shells/input.tcsh | 2 +- tests/test_shells/input.zsh | 13 ++++++------- tests/test_shells/test.sh | 2 +- tests/test_shells/zsh.daemon.ok | 8 ++++---- tests/test_shells/zsh.nodaemon.ok | 8 ++++---- 10 files changed, 16 insertions(+), 22 deletions(-) diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 7e503679..d224e6e7 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -1,6 +1,5 @@ export VIRTUAL_ENV= source powerline/bindings/bash/powerline.sh -POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" cd tests/shell/3rd diff --git a/tests/test_shells/input.busybox b/tests/test_shells/input.busybox index 803bd5c1..220d3b59 100644 --- a/tests/test_shells/input.busybox +++ b/tests/test_shells/input.busybox @@ -1,5 +1,4 @@ . powerline/bindings/shell/powerline.sh -POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= diff --git a/tests/test_shells/input.dash b/tests/test_shells/input.dash index 803bd5c1..220d3b59 100644 --- a/tests/test_shells/input.dash +++ b/tests/test_shells/input.dash @@ -1,5 +1,4 @@ . powerline/bindings/shell/powerline.sh -POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 9d819cfc..d1d9d62e 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -3,7 +3,6 @@ while jobs | grep fish_update_completions sleep 1 end powerline-setup -set POWERLINE_COMMAND "$POWERLINE_COMMAND -p $PWD/powerline/config_files" set POWERLINE_COMMAND "$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" set POWERLINE_COMMAND "$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" setenv VIRTUAL_ENV diff --git a/tests/test_shells/input.mksh b/tests/test_shells/input.mksh index ae6bc326..2da58cee 100644 --- a/tests/test_shells/input.mksh +++ b/tests/test_shells/input.mksh @@ -1,5 +1,4 @@ . powerline/bindings/shell/powerline.sh -POWERLINE_COMMAND="$POWERLINE_COMMAND -p $PWD/powerline/config_files" POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= diff --git a/tests/test_shells/input.tcsh b/tests/test_shells/input.tcsh index 297cff9c..a9d2cf30 100644 --- a/tests/test_shells/input.tcsh +++ b/tests/test_shells/input.tcsh @@ -1,5 +1,5 @@ source powerline/bindings/tcsh/powerline.tcsh -set POWERLINE_COMMAND=$POWERLINE_COMMAND:q" -p "$PWD:q/powerline/config_files" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" +set POWERLINE_COMMAND=$POWERLINE_COMMAND:q" -t default_leftonly.segment_data.hostname.args.only_if_ssh=false -c ext.shell.theme=default_leftonly" unsetenv VIRTUAL_ENV cd tests/shell/3rd cd .git diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 56bbfc49..b4fa6487 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -5,9 +5,8 @@ setopt interactivecomments # POWERLINE_CONFIG=( ext.shell.theme=default_leftonly ) POWERLINE_NO_ZSH_ZPYTHON=1 # TODO: make tests work with zsh/zpython source powerline/bindings/zsh/powerline.zsh -POWERLINE_COMMAND=( $POWERLINE_COMMAND -p $PWD/powerline/config_files ) -POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false ) -POWERLINE_COMMAND=( $POWERLINE_COMMAND -c ext.shell.theme=default_leftonly ) +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default_leftonly.segment_data.hostname.args.only_if_ssh=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -c ext.shell.theme=default_leftonly" export VIRTUAL_ENV= cd tests/shell/3rd cd .git @@ -26,13 +25,13 @@ cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' cd .. -POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,4] ${${POWERLINE_COMMAND[5]}/_leftonly} ) ; bindkey -v +POWERLINE_COMMAND="${POWERLINE_COMMAND//_leftonly}" ; bindkey -v  echo abc false -POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.hostname.display=false ) -POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.user.display=false ) +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.segment_data.hostname.display=false" +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.segment_data.user.display=false" select abc in def ghi jkl do echo $abc @@ -40,7 +39,7 @@ do done 1 hash -d foo=$PWD:h ; cd . -POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC ) +POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC" true true is the last line exit diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 00479dcf..7511691b 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -74,7 +74,7 @@ run() { IPYTHONDIR="$PWD/tests/shell/ipython_home" \ POWERLINE_SHELL_CONTINUATION=$additional_prompts \ POWERLINE_SHELL_SELECT=$additional_prompts \ - POWERLINE_COMMAND="${POWERLINE_COMMAND}" \ + POWERLINE_COMMAND="${POWERLINE_COMMAND} -p $PWD/powerline/config_files" \ "$@" } diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 2e5d05d9..d0259c4a 100644 --- a/tests/test_shells/zsh.daemon.ok +++ b/tests/test_shells/zsh.daemon.ok @@ -17,14 +17,14 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,4] ${${POWERLINE_COMMAND[5]}/_leftonly} ) ; bindkey -v +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="${POWERLINE_COMMAND//_leftonly}" ; bindkey -v  INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd   COMMND   HOSTNAME  USER  ⋯  tests  shell  3rd    INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd    INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  echo abc abc  INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  false - INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.hostname.display=false ) - INSERT  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.user.display=false ) + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.segment_data.hostname.display=false" + INSERT  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.segment_data.user.display=false"  INSERT  ⋯  tests  shell  3rd  select abc in def ghi jkl  select                            do  select                             echo $abc @@ -34,5 +34,5 @@ abc                    Select variant  1 def  INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd . - INSERT  ~foo  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC ) + INSERT  ~foo  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC"  INSERT $ABC~foo  3rd $ABCtrue diff --git a/tests/test_shells/zsh.nodaemon.ok b/tests/test_shells/zsh.nodaemon.ok index 27a734a3..f3edbcdc 100644 --- a/tests/test_shells/zsh.nodaemon.ok +++ b/tests/test_shells/zsh.nodaemon.ok @@ -17,14 +17,14 @@   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`'   HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`  cd .. -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND[1,4] ${${POWERLINE_COMMAND[5]}/_leftonly} ) ; bindkey -v +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  POWERLINE_COMMAND="${POWERLINE_COMMAND//_leftonly}" ; bindkey -v  INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd   COMMND   HOSTNAME  USER  ⋯  tests  shell  3rd    INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd    INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  echo abc abc  INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  false - INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.hostname.display=false ) - INSERT  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.segment_data.user.display=false ) + INSERT   HOSTNAME  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.segment_data.hostname.display=false" + INSERT  USER  ⋯  tests  shell  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.segment_data.user.display=false"  INSERT  ⋯  tests  shell  3rd  select abc in def ghi jkl  select  do  select   echo $abc @@ -34,5 +34,5 @@ abc  Select variant  1 def  INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd . - INSERT  ~foo  3rd  POWERLINE_COMMAND=( $POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC ) + INSERT  ~foo  3rd  POWERLINE_COMMAND="$POWERLINE_COMMAND -t default.dividers.left.hard=\$ABC"  INSERT $ABC~foo  3rd $ABCtrue From 5bd543ae8932f865d1e3b0ee7aaf9123654fa203 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 22:36:32 +0400 Subject: [PATCH 1408/1472] Do not run daemon when running with ONLY_TEST_TYPE=nodaemon --- tests/test_shells/test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 7511691b..25b3d268 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -232,6 +232,9 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te scripts/powerline-config shell command for TEST_TYPE in "daemon" "nodaemon" ; do + if test "x$ONLY_TEST_TYPE" != "x" && test "x$ONLY_TEST_TYPE" != "x$TEST_TYPE" ; then + continue + fi if test x$FAST = x1 ; then if test $TEST_TYPE = daemon ; then VARIANTS=3 @@ -248,9 +251,6 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te $PYTHON ./scripts/powerline-daemon -s$ADDRESS -f &>tests/shell/daemon_log ' & fi - if test "x$ONLY_TEST_TYPE" != "x" && test "x$ONLY_TEST_TYPE" != "x$TEST_TYPE" ; then - continue - fi echo "> Testing $TEST_TYPE" I=-1 for POWERLINE_COMMAND in \ From 41476544ca1f6176a99e164e53a7c5c3b98ee455 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 22:38:26 +0400 Subject: [PATCH 1409/1472] Replace `name` and `module` keys with `function` key Fixes #1031 --- docs/source/configuration/reference.rst | 49 ++++--- powerline/config_files/themes/ipython/in.json | 5 +- .../config_files/themes/ipython/out.json | 5 +- .../config_files/themes/ipython/rewrite.json | 4 +- .../themes/shell/continuation.json | 2 +- .../config_files/themes/shell/default.json | 20 ++- .../themes/shell/default_leftonly.json | 17 +-- .../config_files/themes/tmux/default.json | 10 +- .../config_files/themes/vim/default.json | 40 +++--- powerline/config_files/themes/vim/help.json | 6 +- .../config_files/themes/vim/plugin_ctrlp.json | 4 +- .../themes/vim/plugin_nerdtree.json | 2 +- .../config_files/themes/vim/quickfix.json | 4 +- .../config_files/themes/vim/tabline.json | 17 ++- powerline/config_files/themes/wm/default.json | 8 +- powerline/lint/__init__.py | 125 ++++++++++-------- powerline/segment.py | 76 ++++++----- tests/test_configuration.py | 52 +++++++- tests/test_plugin_file.vim | 1 + tests/test_provided_config_files.py | 9 ++ tests/test_tabline.vim | 1 + 21 files changed, 267 insertions(+), 190 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index d4e8e432..ac4b2171 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -64,8 +64,9 @@ Common configuration is a subdictionary that is a value of ``common`` key in ``paths`` Defines additional paths which will be searched for modules when using - :ref:`module segment option `. Paths defined here - have priority when searching for modules. + :ref:`function segment option ` or :ref:`Vim + local_themes option `. Paths defined here have + priority when searching for modules. .. _config-common-log: @@ -283,7 +284,8 @@ ascii Theme without any unicode characters at all .. _config-themes-default_module: ``default_module`` - Python module where segments will be looked by default. + Python module where segments will be looked by default. Defaults to + ``powerline.segments.{ext}``. ``spaces`` Defines number of spaces just before the divider (on the right side) or just @@ -325,8 +327,8 @@ ascii Theme without any unicode characters at all .. _config-themes-segment_data: ``segment_data`` - A dict where keys are segment names or strings ``{module}.{name}``. Used to - specify default values for various keys: + A dict where keys are segment names or strings ``{module}.{function}``. Used + to specify default values for various keys: :ref:`after `, :ref:`before `, :ref:`contents ` (only for string segments @@ -335,8 +337,8 @@ ascii Theme without any unicode characters at all Key :ref:`args ` (only for function and segments_list segments) is handled specially: unlike other values it is - merged with all other values, except that a single ``{module}.{name}`` key - if found prevents merging all ``{name}`` values. + merged with all other values, except that a single ``{module}.{function}`` + key if found prevents merging all ``{function}`` values. When using :ref:`local themes ` values of these keys are first searched in the segment description, then in ``segment_data`` @@ -372,8 +374,8 @@ ascii Theme without any unicode characters at all ``filler`` or ``segments_list``: ``function`` - The segment contents is the return value of the function defined - in the :ref:`name option `. + The segment contents is the return value of the function defined in + the :ref:`function option `. ``string`` A static string segment where the contents is defined in the @@ -382,22 +384,31 @@ ascii Theme without any unicode characters at all option `. ``segments_list`` - Sub-list of segments. This list only allows :ref:`name - `, :ref:`segments + Sub-list of segments. This list only allows :ref:`function + `, :ref:`segments ` and :ref:`args ` options. - .. _config-themes-seg-module: - - ``module`` - Function module, only required for function segments. Defaults to - ``powerline.segments.{extension}``. Default is overriden by - :ref:`default_module theme option `. - .. _config-themes-seg-name: ``name`` - Function name, only required for function and list segments. + Segment name. If present allows referring to this segment in + :ref:`segment_data ` dictionary by this + name. If not ``string`` segments may not be referred there at all and + ``function`` and ``segments_list`` segments may be referred there using + either ``{module}.{function_name}`` or ``{function_name}``, whichever + will be found first. Function name is taken from :ref:`function key + `. + + .. note:: + If present prevents ``function`` key from acting as a segment name. + + .. _config-themes-seg-function: + + ``function`` + Function used to get segment contents, in format ``{module}.{function}`` + or ``{function}``. If ``{module}`` is omitted :ref:`default_module + option ` is used. .. _config-themes-seg-highlight_group: diff --git a/powerline/config_files/themes/ipython/in.json b/powerline/config_files/themes/ipython/in.json index c0086509..6218b3ab 100644 --- a/powerline/config_files/themes/ipython/in.json +++ b/powerline/config_files/themes/ipython/in.json @@ -3,7 +3,7 @@ "segments": { "left": [ { - "name": "virtualenv", + "function": "virtualenv", "priority": 10 }, { @@ -13,8 +13,7 @@ "highlight_group": ["prompt"] }, { - "name": "prompt_count", - "module": "powerline.segments.ipython", + "function": "powerline.segments.ipython.prompt_count", "draw_soft_divider": false }, { diff --git a/powerline/config_files/themes/ipython/out.json b/powerline/config_files/themes/ipython/out.json index 11a63234..f7c27adf 100644 --- a/powerline/config_files/themes/ipython/out.json +++ b/powerline/config_files/themes/ipython/out.json @@ -1,5 +1,5 @@ { - "default_module": "powerline.segments.common", + "default_module": "powerline.segments.ipython", "segments": { "left": [ { @@ -11,8 +11,7 @@ "highlight_group": ["prompt"] }, { - "name": "prompt_count", - "module": "powerline.segments.ipython", + "function": "prompt_count", "draw_soft_divider": false }, { diff --git a/powerline/config_files/themes/ipython/rewrite.json b/powerline/config_files/themes/ipython/rewrite.json index 47d8de0d..35b39024 100644 --- a/powerline/config_files/themes/ipython/rewrite.json +++ b/powerline/config_files/themes/ipython/rewrite.json @@ -1,4 +1,5 @@ { + "default_module": "powerline.segments.ipython", "segments": { "left": [ { @@ -9,8 +10,7 @@ "highlight_group": ["prompt"] }, { - "name": "prompt_count", - "module": "powerline.segments.ipython", + "function": "prompt_count", "draw_soft_divider": false }, { diff --git a/powerline/config_files/themes/shell/continuation.json b/powerline/config_files/themes/shell/continuation.json index e377f5b4..9307fc0e 100644 --- a/powerline/config_files/themes/shell/continuation.json +++ b/powerline/config_files/themes/shell/continuation.json @@ -3,7 +3,7 @@ "segments": { "left": [ { - "name": "continuation" + "function": "continuation" } ], "right": [ diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index e4f486a3..6ba1ba6d 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -3,40 +3,36 @@ "segments": { "left": [ { - "module": "powerline.segments.shell", - "name": "mode" + "function": "powerline.segments.shell.mode" }, { - "name": "hostname", + "function": "hostname", "priority": 10 }, { - "name": "user", + "function": "user", "priority": 30 }, { - "name": "virtualenv", + "function": "virtualenv", "priority": 50 }, { - "module": "powerline.segments.shell", - "name": "cwd", + "function": "powerline.segments.shell.cwd", "priority": 10 }, { - "module": "powerline.segments.shell", - "name": "jobnum", + "function": "powerline.segments.shell.jobnum", "priority": 20 } ], "right": [ { - "module": "powerline.segments.shell", - "name": "last_pipe_status", + "function": "powerline.segments.shell.last_pipe_status", "priority": 10 }, { - "name": "branch", + "function": "branch", "priority": 40 } ] diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index 350fb026..018847ba 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -3,34 +3,31 @@ "segments": { "left": [ { - "name": "hostname", + "function": "hostname", "priority": 10 }, { - "name": "user", + "function": "user", "priority": 30 }, { - "name": "virtualenv", + "function": "virtualenv", "priority": 50 }, { - "name": "branch", + "function": "branch", "priority": 40 }, { - "module": "powerline.segments.shell", - "name": "cwd", + "function": "powerline.segments.shell.cwd", "priority": 10 }, { - "module": "powerline.segments.shell", - "name": "jobnum", + "function": "powerline.segments.shell.jobnum", "priority": 20 }, { - "module": "powerline.segments.shell", - "name": "last_status", + "function": "powerline.segments.shell.last_status", "priority": 10 } ] diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 479506ae..336093b1 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -3,26 +3,26 @@ "segments": { "right": [ { - "name": "uptime", + "function": "uptime", "priority": 50 }, { - "name": "system_load", + "function": "system_load", "priority": 50 }, { - "name": "date", + "function": "date", "before": "" }, { - "name": "date", + "function": "date", "args": { "format": "%H:%M", "istime": true } }, { - "name": "hostname" + "function": "hostname" } ] } diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 1f9348bb..a71c2136 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -2,67 +2,65 @@ "segments": { "left": [ { - "name": "mode", + "function": "mode", "exclude_modes": ["nc"] }, { - "name": "visual_range", + "function": "visual_range", "include_modes": ["v", "V", "^V", "s", "S", "^S"], "priority": 10 }, { - "name": "paste_indicator", + "function": "paste_indicator", "exclude_modes": ["nc"], "priority": 10 }, { - "name": "branch", + "function": "branch", "exclude_modes": ["nc"], "priority": 30 }, { - "name": "readonly_indicator", + "function": "readonly_indicator", "draw_soft_divider": false, "after": " " }, { - "name": "file_scheme", + "function": "file_scheme", "priority": 20 }, { - "name": "file_directory", + "function": "file_directory", "priority": 40, "draw_soft_divider": false }, { - "name": "file_name", + "function": "file_name", "draw_soft_divider": false }, { - "name": "file_vcs_status", + "function": "file_vcs_status", "before": " ", "draw_soft_divider": false }, { - "name": "modified_indicator", + "function": "modified_indicator", "before": " " }, { "exclude_modes": ["i", "R", "Rv"], - "name": "trailing_whitespace", + "function": "trailing_whitespace", "display": false, "priority": 60 }, { "exclude_modes": ["nc"], - "module": "powerline.segments.vim.plugin.syntastic", - "name": "syntastic", + "function": "powerline.segments.vim.plugin.syntastic.syntastic", "priority": 50 }, { "exclude_modes": ["nc"], - "module": "powerline.segments.vim.plugin.tagbar", - "name": "current_tag", + "function": "powerline.segments.vim.plugin.tagbar.current_tag", "draw_soft_divider": false, "priority": 50 }, @@ -76,23 +74,23 @@ ], "right": [ { - "name": "file_format", + "function": "file_format", "draw_soft_divider": false, "exclude_modes": ["nc"], "priority": 60 }, { - "name": "file_encoding", + "function": "file_encoding", "exclude_modes": ["nc"], "priority": 60 }, { - "name": "file_type", + "function": "file_type", "exclude_modes": ["nc"], "priority": 60 }, { - "name": "line_percent", + "function": "line_percent", "priority": 50, "width": 4, "align": "r" @@ -103,13 +101,13 @@ "highlight_group": ["line_current_symbol", "line_current"] }, { - "name": "line_current", + "function": "line_current", "draw_soft_divider": false, "width": 3, "align": "r" }, { - "name": "virtcol_current", + "function": "virtcol_current", "draw_soft_divider": false, "priority": 20, "before": ":", diff --git a/powerline/config_files/themes/vim/help.json b/powerline/config_files/themes/vim/help.json index 74071057..aef0c23e 100644 --- a/powerline/config_files/themes/vim/help.json +++ b/powerline/config_files/themes/vim/help.json @@ -2,7 +2,7 @@ "segments": { "left": [ { - "name": "file_name", + "function": "file_name", "draw_soft_divider": false }, { @@ -15,7 +15,7 @@ ], "right": [ { - "name": "line_percent", + "function": "line_percent", "priority": 30, "width": 4, "align": "r" @@ -26,7 +26,7 @@ "highlight_group": ["line_current_symbol", "line_current"] }, { - "name": "line_current", + "function": "line_current", "draw_soft_divider": false, "width": 3, "align": "r" diff --git a/powerline/config_files/themes/vim/plugin_ctrlp.json b/powerline/config_files/themes/vim/plugin_ctrlp.json index ae814c30..25e16e79 100644 --- a/powerline/config_files/themes/vim/plugin_ctrlp.json +++ b/powerline/config_files/themes/vim/plugin_ctrlp.json @@ -3,7 +3,7 @@ "segments": { "left": [ { - "name": "ctrlp", + "function": "ctrlp", "args": { "side": "left" } @@ -18,7 +18,7 @@ ], "right": [ { - "name": "ctrlp", + "function": "ctrlp", "args": { "side": "right" } diff --git a/powerline/config_files/themes/vim/plugin_nerdtree.json b/powerline/config_files/themes/vim/plugin_nerdtree.json index ee142cde..784523af 100644 --- a/powerline/config_files/themes/vim/plugin_nerdtree.json +++ b/powerline/config_files/themes/vim/plugin_nerdtree.json @@ -3,7 +3,7 @@ "segments": { "left": [ { - "name": "nerdtree" + "function": "nerdtree" }, { "type": "string", diff --git a/powerline/config_files/themes/vim/quickfix.json b/powerline/config_files/themes/vim/quickfix.json index e1e8f253..2aa1c0f1 100644 --- a/powerline/config_files/themes/vim/quickfix.json +++ b/powerline/config_files/themes/vim/quickfix.json @@ -12,7 +12,7 @@ "highlight_group": ["file_name"] }, { - "name": "window_title", + "function": "window_title", "draw_soft_divider": false }, { @@ -30,7 +30,7 @@ "highlight_group": ["line_current_symbol", "line_current"] }, { - "name": "line_current", + "function": "line_current", "draw_soft_divider": false, "width": 3, "align": "r" diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 79b145fa..9be639eb 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -4,39 +4,38 @@ "left": [ { "type": "segment_list", - "module": "powerline.listers.vim", - "name": "tabbuflister", + "function": "powerline.listers.vim.tabbuflister", "segments": [ { - "name": "tabnr", + "function": "tabnr", "after": " ", "exclude_modes": ["tab", "buf", "buf_nc"], "priority": 5 }, { - "name": "bufnr", + "function": "bufnr", "after": " ", "exclude_modes": ["tab", "buf", "tab_nc"], "priority": 5 }, { - "name": "file_directory", + "function": "file_directory", "priority": 40 }, { - "name": "file_name", + "function": "file_name", "args": { "display_no_file": true }, "priority": 10 }, { - "name": "tab_modified_indicator", + "function": "tab_modified_indicator", "exclude_modes": ["buf", "buf_nc"], "priority": 5 }, { - "name": "modified_indicator", + "function": "modified_indicator", "exclude_modes": ["tab", "tab_nc"], "priority": 5 } @@ -52,7 +51,7 @@ ], "right": [ { - "name": "single_tab" + "function": "single_tab" } ] } diff --git a/powerline/config_files/themes/wm/default.json b/powerline/config_files/themes/wm/default.json index b00699a5..0d3cb4e3 100644 --- a/powerline/config_files/themes/wm/default.json +++ b/powerline/config_files/themes/wm/default.json @@ -3,22 +3,22 @@ "segments": { "right": [ { - "name": "weather", + "function": "weather", "priority": 50 }, { - "name": "date", + "function": "date", "before": "" }, { - "name": "date", + "function": "date", "args": { "format": "%H:%M", "istime": true } }, { - "name": "email_imap_alert", + "function": "email_imap_alert", "priority": 10, "args": { "username": "", diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 61ecca38..22f037b2 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -808,16 +808,16 @@ generic_keys = set(( 'display' )) type_keys = { - 'function': set(('args', 'module', 'draw_inner_divider')), + 'function': set(('function', 'args', 'draw_inner_divider')), 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), - 'segment_list': set(('segments', 'module', 'args', 'type')), + 'segment_list': set(('function', 'segments', 'args', 'type')), } required_keys = { - 'function': set(('name',)), + 'function': set(('function',)), 'string': set(()), 'filler': set(), - 'segment_list': set(('name', 'segments',)), + 'segment_list': set(('function', 'segments',)), } highlight_keys = set(('highlight_group', 'name')) @@ -887,8 +887,17 @@ def check_segment_module(module, data, context, echoerr): return True, False, False +def get_function_strings(function_name, context, ext): + if '.' in function_name: + module, function_name = function_name.rpartition('.')[::2] + else: + module = context[0][1].get( + 'default_module', MarkedUnicode('powerline.segments.' + ext, None)) + return module, function_name + + def check_full_segment_data(segment, data, context, echoerr): - if 'name' not in segment: + if 'name' not in segment and 'function' not in segment: return True, False, False ext = data['ext'] @@ -899,11 +908,17 @@ def check_full_segment_data(segment, data, context, echoerr): else: top_segment_data = data['ext_theme_configs'].get(main_theme_name, {}).get('segment_data', {}) - names = [segment['name']] if segment.get('type', 'function') == 'function': - module = segment.get('module', context[0][1].get('default_module', MarkedUnicode( - 'powerline.segments.' + ext, None))) - names.insert(0, unicode(module) + '.' + unicode(names[0])) + function_name = segment.get('function') + if function_name: + module, function_name = get_function_strings(function_name, context, ext) + names = [module + '.' + function_name, function_name] + else: + names = [] + elif segment.get('name'): + names = [segment['name']] + else: + return True, False, False segment_copy = segment.copy() @@ -921,15 +936,9 @@ def check_full_segment_data(segment, data, context, echoerr): return check_key_compatibility(segment_copy, data, context, echoerr) -def import_segment(name, data, context, echoerr, module=None): +def import_segment(name, data, context, echoerr, module): context_has_marks(context) - havemarks(name) - if not module: - module = context[-2][1].get( - 'module', context[0][1].get( - 'default_module', MarkedUnicode( - 'powerline.segments.' + data['ext'], None))) - havemarks(module) + havemarks(name, module) with WithPath(data['import_paths']): try: @@ -956,11 +965,12 @@ def import_segment(name, data, context, echoerr, module=None): return func -def check_segment_name(name, data, context, echoerr): - havemarks(name) +def check_segment_function(function_name, data, context, echoerr): + havemarks(function_name) ext = data['ext'] + module, function_name = get_function_strings(function_name, context, ext) if context[-2][1].get('type', 'function') == 'function': - func = import_segment(name, data, context, echoerr) + func = import_segment(function_name, data, context, echoerr, module=module) if not func: return True, False, True @@ -974,7 +984,7 @@ def check_segment_name(name, data, context, echoerr): D_H_G_USED_STR = 'Divider highlight group used: ' LDHGUS = len(D_H_G_USED_STR) pointer = 0 - mark_name = '<{0} docstring>'.format(name) + mark_name = '<{0} docstring>'.format(function_name) for i, line in enumerate(func.__doc__.split('\n')): if H_G_USED_STR in line: idx = line.index(H_G_USED_STR) + LHGUS @@ -1000,7 +1010,7 @@ def check_segment_name(name, data, context, echoerr): 'found highlight group {0} not defined in the following colorschemes: {1}\n' '(Group name was obtained from function documentation.)' ).format(divider_hl_group, list_sep.join(r)), - problem_mark=name.mark + problem_mark=function_name.mark ) hadproblem = True @@ -1039,7 +1049,7 @@ def check_segment_name(name, data, context, echoerr): 'found highlight groups list ({0}) with all groups not defined in some colorschemes\n' '(Group names were taken from function documentation.)' ).format(list_sep.join((h[0] for h in required_pack))), - problem_mark=name.mark + problem_mark=function_name.mark ) for r, h in zip(rs, required_pack): echoerr( @@ -1049,7 +1059,7 @@ def check_segment_name(name, data, context, echoerr): ) hadproblem = True else: - r = hl_exists(name, data, context, echoerr, allow_gradients=True) + r = hl_exists(function_name, data, context, echoerr, allow_gradients=True) if r: echoerr( context='Error while checking theme (key {key})'.format(key=context_key(context)), @@ -1058,27 +1068,27 @@ def check_segment_name(name, data, context, echoerr): '(If not specified otherwise in documentation, ' 'highlight group for function segments\n' 'is the same as the function name.)' - ).format(name, list_sep.join(r)), - problem_mark=name.mark + ).format(function_name, list_sep.join(r)), + problem_mark=function_name.mark ) hadproblem = True return True, False, hadproblem elif context[-2][1].get('type') != 'segment_list': - if name not in context[0][1].get('segment_data', {}): + if function_name not in context[0][1].get('segment_data', {}): main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) if data['theme'] == main_theme_name: main_theme = {} else: main_theme = data['ext_theme_configs'].get(main_theme_name, {}) if ( - name not in main_theme.get('segment_data', {}) - and name not in data['ext_theme_configs'].get('__main__', {}).get('segment_data', {}) - and not any(((name in theme.get('segment_data', {})) for theme in data['top_themes'].values())) + function_name not in main_theme.get('segment_data', {}) + and function_name not in data['ext_theme_configs'].get('__main__', {}).get('segment_data', {}) + and not any(((function_name in theme.get('segment_data', {})) for theme in data['top_themes'].values())) ): echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)), problem='found useless use of name key (such name is not present in theme/segment_data)', - problem_mark=name.mark) + problem_mark=function_name.mark) return True, False, False @@ -1192,16 +1202,22 @@ def check_segment_data_key(key, data, context, echoerr): for segments in theme.get('segments', {}).values(): for segment in segments: if 'name' in segment: - if has_module_name: - module = segment.get('module', theme.get('default_module', 'powerline.segments.' + ext)) - full_name = unicode(module) + '.' + unicode(segment['name']) - if key == full_name: - found = True - break - else: - if key == segment['name']: - found = True - break + if key == segment['name']: + found = True + break + else: + function_name = segment.get('function') + if function_name: + module, function_name = get_function_strings(function_name, ((None, theme),), ext) + if has_module_name: + full_name = module + '.' + function_name + if key == full_name: + found = True + break + else: + if key == function_name: + found = True + break if found: break if found: @@ -1288,9 +1304,11 @@ def check_args(get_functions, args, data, context, echoerr): def get_one_segment_function(data, context, echoerr): - name = context[-2][1].get('name') - if name: - func = import_segment(name, data, context, echoerr) + ext = data['ext'] + function_name = context[-2][1].get('function') + if function_name: + module, function_name = get_function_strings(function_name, context, ext) + func = import_segment(function_name, data, context, echoerr, module=module) if func: yield func @@ -1307,14 +1325,14 @@ def get_all_possible_functions(data, context, echoerr): for segments in theme_config.get('segments', {}).values(): for segment in segments: if segment.get('type', 'function') == 'function': - module = segment.get( - 'module', - theme_config.get('default_module', MarkedUnicode( - 'powerline.segments.' + data['ext'], None)) - ) - func = import_segment(name, data, context, echoerr, module=module) - if func: - yield func + function_name = segment.get('function') + current_name = segment.get('name') + if function_name: + module, function_name = get_function_strings(function_name, ((None, theme_config),), ext) + if current_name == name or function_name == name: + func = import_segment(function_name, data, context, echoerr, module=module) + if func: + yield func args_spec = Spec( @@ -1326,7 +1344,8 @@ segment_module_spec = Spec().type(unicode).func(check_segment_module).optional() sub_segments_spec = Spec() segment_spec = Spec( type=Spec().oneof(type_keys).optional(), - name=Spec().re('^[a-zA-Z_]\w*$').func(check_segment_name).optional(), + name=Spec().re('^[a-zA-Z_]\w*$').optional(), + function=Spec().re('^(\w+\.)*[a-zA-Z_]\w*$').func(check_segment_function).optional(), exclude_modes=Spec().list(vim_mode_spec()).optional(), include_modes=Spec().list(vim_mode_spec()).optional(), draw_hard_divider=Spec().type(bool).optional(), diff --git a/powerline/segment.py b/powerline/segment.py index 7513a92b..d282a765 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -4,34 +4,35 @@ from __future__ import absolute_import, unicode_literals, division, print_functi from powerline.lib.watcher import create_file_watcher -def list_segment_key_values(segment, theme_configs, segment_data, key, module=None, default=None): +def list_segment_key_values(segment, theme_configs, segment_data, key, function_name=None, name=None, module=None, default=None): try: yield segment[key] except KeyError: pass - try: - name = segment['name'] - except KeyError: - pass - else: - found_module_key = False - for theme_config in theme_configs: - try: - segment_data = theme_config['segment_data'] - except KeyError: - pass - else: + found_module_key = False + for theme_config in theme_configs: + try: + segment_data = theme_config['segment_data'] + except KeyError: + pass + else: + if function_name and not name: if module: try: - yield segment_data[module + '.' + name][key] + yield segment_data[module + '.' + function_name][key] found_module_key = True except KeyError: pass if not found_module_key: try: - yield segment_data[name][key] + yield segment_data[function_name][key] except KeyError: pass + if name: + try: + yield segment_data[name][key] + except KeyError: + pass if segment_data is not None: try: yield segment_data[key] @@ -58,26 +59,31 @@ def get_segment_key(merge, *args, **kwargs): def get_function(data, segment): - segment_module = str(segment.get('module', data['default_module'])) - function = data['get_module_attr'](segment_module, segment['name'], prefix='segment_generator') + function_name = segment['function'] + if '.' in function_name: + module, function_name = function_name.rpartition('.')[::2] + else: + module = data['default_module'] + function = data['get_module_attr'](module, function_name, prefix='segment_generator') if not function: raise ImportError('Failed to obtain segment function') - return None, function, segment_module + return None, function, module, function_name, segment.get('name') def get_string(data, segment): - return data['get_key'](False, segment, None, 'contents'), None, None + name = segment.get('name') + return data['get_key'](False, segment, None, None, name, 'contents'), None, None, None, name def get_filler(data, segment): - return None, None, None + return None, None, None, None, None segment_getters = { - "function": get_function, - "string": get_string, - "filler": get_filler, - "segment_list": get_function, + 'function': get_function, + 'string': get_string, + 'filler': get_filler, + 'segment_list': get_function, } @@ -185,8 +191,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'segment_data': None, } - def get_key(merge, segment, module, key, default=None): - return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, module, default) + def get_key(merge, segment, module, function_name, name, key, default=None): + return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, function_name, name, module, default) data['get_key'] = get_key def get(segment, side): @@ -197,12 +203,12 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge raise TypeError('Unknown segment type: {0}'.format(segment_type)) try: - contents, _contents_func, module = get_segment_info(data, segment) + contents, _contents_func, module, function_name, name = get_segment_info(data, segment) except Exception as e: pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') return None - if not get_key(False, segment, module, 'display', True): + if not get_key(False, segment, module, function_name, name, 'display', True): return None segment_datas = getattr(_contents_func, 'powerline_segment_datas', None) @@ -213,12 +219,12 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge pass if segment_type == 'function': - highlight_group = [module + '.' + segment['name'], segment['name']] + highlight_group = [module + '.' + function_name, function_name] else: - highlight_group = segment.get('highlight_group') or segment.get('name') + highlight_group = segment.get('highlight_group') or name if segment_type in ('function', 'segment_list'): - args = dict(((str(k), v) for k, v in get_key(True, segment, module, 'args', {}).items())) + args = dict(((str(k), v) for k, v in get_key(True, segment, module, function_name, name, 'args', {}).items())) if segment_type == 'segment_list': # Handle startup and shutdown of _contents_func? @@ -227,7 +233,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge for subsegment in segment['segments'] ] return { - 'name': segment.get('name'), + 'name': name or function_name, 'type': segment_type, 'highlight_group': None, 'divider_highlight_group': None, @@ -278,12 +284,12 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge contents_func = None return { - 'name': segment.get('name'), + 'name': name or function_name, 'type': segment_type, 'highlight_group': highlight_group, 'divider_highlight_group': None, - 'before': get_key(False, segment, module, 'before', ''), - 'after': get_key(False, segment, module, 'after', ''), + 'before': get_key(False, segment, module, function_name, name, 'before', ''), + 'after': get_key(False, segment, module, function_name, name, 'after', ''), 'contents_func': contents_func, 'contents': contents, 'priority': segment.get('priority', None), diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 87817ed6..34e402c8 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -123,7 +123,7 @@ config = { 'segments': { 'left': [ { - 'name': 'environment', + 'function': 'environment', 'args': { 'variable': 'TEST', }, @@ -442,8 +442,7 @@ class TestSegmentAttributes(TestRender): config['themes/test/default']['segments'] = { 'left': [ { - 'name': 'm1', - 'module': 'bar' + 'function': 'bar.m1' } ] } @@ -469,14 +468,57 @@ class TestSegmentAttributes(TestRender): config['themes/test/default']['segments'] = { 'left': [ { - 'name': 'm1', - 'module': 'bar' + 'function': 'bar.m1' } ] } self.assertRenderEqual(p, '{56} pl;{6-}>>{--}') +class TestSegmentData(TestRender): + @add_args + def test_segment_data(self, p, config): + def m1(**kwargs): + return 'S' + + def m2(**kwargs): + return 'S' + sys.modules['bar'] = Args(m1=m1, m2=m2) + config['themes/powerline']['segment_data'] = { + 'm1': { + 'before': '1' + }, + 'bar.m2': { + 'before': '2' + }, + 'n': { + 'before': '3' + }, + 'm2': { + 'before': '4' + }, + } + config['themes/test/default']['segments'] = { + 'left': [ + { + 'function': 'bar.m1' + }, + { + 'function': 'bar.m1', + 'name': 'n' + }, + { + 'function': 'bar.m2', + 'name': 'n' + }, + { + 'function': 'bar.m2' + } + ] + } + self.assertRenderEqual(p, '{56} 1S{56}>{56}3S{610}>>{910}3S{910}>{910}2S{10-}>>{--}') + + class TestVim(TestCase): def test_environ_update(self): # Regression test: test that segment obtains environment from vim, not diff --git a/tests/test_plugin_file.vim b/tests/test_plugin_file.vim index 6c99c5d0..27a85411 100755 --- a/tests/test_plugin_file.vim +++ b/tests/test_plugin_file.vim @@ -1,5 +1,6 @@ #!/usr/bin/vim -S set encoding=utf-8 +let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' tabedit abc tabedit def try diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 27698cde..e6329d1f 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -138,10 +138,16 @@ class TestConfig(TestCase): old_cwd = None +saved_get_config_paths = None def setUpModule(): global old_cwd + global saved_get_config_paths + import powerline + saved_get_config_paths = powerline.get_config_paths + path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') + powerline.get_config_paths = lambda: [path] sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) old_cwd = os.getcwd() from powerline.segments import vim @@ -150,6 +156,9 @@ def setUpModule(): def tearDownModule(): global old_cwd + global saved_get_config_paths + import powerline + powerline.get_config_paths = saved_get_config_paths os.chdir(old_cwd) old_cwd = None sys.path.pop(0) diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index b4bc4112..00c807a7 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -1,5 +1,6 @@ #!/usr/bin/vim -S set encoding=utf-8 +let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' source powerline/bindings/vim/plugin/powerline.vim edit abc tabedit def From 4f5a606ce2700c877e0ece0c9f98840ddb70fb64 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 22:57:04 +0400 Subject: [PATCH 1410/1472] =?UTF-8?q?Use=20=E2=80=9Ctime=E2=80=9D=20for=20?= =?UTF-8?q?time=20segment=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerline/config_files/themes/ascii.json | 7 ++++--- powerline/config_files/themes/powerline.json | 7 ++++--- powerline/config_files/themes/tmux/default.json | 4 ++-- powerline/config_files/themes/unicode.json | 7 ++++--- powerline/config_files/themes/unicode_terminus.json | 7 ++++--- .../config_files/themes/unicode_terminus_condensed.json | 7 ++++--- powerline/config_files/themes/wm/default.json | 4 ++-- 7 files changed, 24 insertions(+), 19 deletions(-) diff --git a/powerline/config_files/themes/ascii.json b/powerline/config_files/themes/ascii.json index fa1927ba..7875f219 100644 --- a/powerline/config_files/themes/ascii.json +++ b/powerline/config_files/themes/ascii.json @@ -25,6 +25,10 @@ "contents": "LN " }, + "time": { + "before": "" + }, + "powerline.segments.common.network_load": { "args": { "recv_format": "DL {value:>8}", @@ -50,9 +54,6 @@ "powerline.segments.common.uptime": { "before": "UP " }, - "powerline.segments.common.date": { - "before": "" - }, "powerline.segments.common.email_imap_alert": { "before": "MAIL " }, diff --git a/powerline/config_files/themes/powerline.json b/powerline/config_files/themes/powerline.json index 7f83ec3b..859d8341 100644 --- a/powerline/config_files/themes/powerline.json +++ b/powerline/config_files/themes/powerline.json @@ -24,6 +24,10 @@ "contents": " " }, + "time": { + "before": "⌚ " + }, + "powerline.segments.common.network_load": { "args": { "recv_format": "⬇ {value:>8}", @@ -49,9 +53,6 @@ "powerline.segments.common.uptime": { "before": "⇑ " }, - "powerline.segments.common.date": { - "before": "⌚ " - }, "powerline.segments.common.email_imap_alert": { "before": "✉ " }, diff --git a/powerline/config_files/themes/tmux/default.json b/powerline/config_files/themes/tmux/default.json index 336093b1..780f34f2 100644 --- a/powerline/config_files/themes/tmux/default.json +++ b/powerline/config_files/themes/tmux/default.json @@ -11,11 +11,11 @@ "priority": 50 }, { - "function": "date", - "before": "" + "function": "date" }, { "function": "date", + "name": "time", "args": { "format": "%H:%M", "istime": true diff --git a/powerline/config_files/themes/unicode.json b/powerline/config_files/themes/unicode.json index 78f84e0b..a3b5a365 100644 --- a/powerline/config_files/themes/unicode.json +++ b/powerline/config_files/themes/unicode.json @@ -24,6 +24,10 @@ "contents": "␤ " }, + "time": { + "before": "⌚ " + }, + "powerline.segments.common.network_load": { "args": { "recv_format": "⬇ {value:>8}", @@ -49,9 +53,6 @@ "powerline.segments.common.uptime": { "before": "⇑ " }, - "powerline.segments.common.date": { - "before": "⌚ " - }, "powerline.segments.common.email_imap_alert": { "before": "✉ " }, diff --git a/powerline/config_files/themes/unicode_terminus.json b/powerline/config_files/themes/unicode_terminus.json index e1077d2e..e435ea93 100644 --- a/powerline/config_files/themes/unicode_terminus.json +++ b/powerline/config_files/themes/unicode_terminus.json @@ -24,6 +24,10 @@ "contents": "␤ " }, + "time": { + "before": "" + }, + "powerline.segments.common.network_load": { "args": { "recv_format": "⇓ {value:>8}", @@ -49,9 +53,6 @@ "powerline.segments.common.uptime": { "before": "↑ " }, - "powerline.segments.common.date": { - "before": "" - }, "powerline.segments.common.email_imap_alert": { "before": "MAIL " }, diff --git a/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline/config_files/themes/unicode_terminus_condensed.json index 4b32d0ca..c4266ee6 100644 --- a/powerline/config_files/themes/unicode_terminus_condensed.json +++ b/powerline/config_files/themes/unicode_terminus_condensed.json @@ -25,6 +25,10 @@ "contents": "␤" }, + "time": { + "before": "" + }, + "powerline.segments.common.network_load": { "args": { "recv_format": "⇓{value:>8}", @@ -50,9 +54,6 @@ "powerline.segments.common.uptime": { "before": "↑" }, - "powerline.segments.common.date": { - "before": "" - }, "powerline.segments.common.email_imap_alert": { "before": "M " }, diff --git a/powerline/config_files/themes/wm/default.json b/powerline/config_files/themes/wm/default.json index 0d3cb4e3..009c4924 100644 --- a/powerline/config_files/themes/wm/default.json +++ b/powerline/config_files/themes/wm/default.json @@ -7,11 +7,11 @@ "priority": 50 }, { - "function": "date", - "before": "" + "function": "date" }, { "function": "date", + "name": "time", "args": { "format": "%H:%M", "istime": true From 10e8d9bb15272fd198ebf79937e6749c17d0d84b Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 29 Aug 2014 23:21:19 +0400 Subject: [PATCH 1411/1472] Do not use &> in #!/bin/sh scripts --- tests/test_shells/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 25b3d268..216ed4fc 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -248,7 +248,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te if test $TEST_TYPE = daemon ; then sh -c ' echo $$ > tests/shell/daemon_pid - $PYTHON ./scripts/powerline-daemon -s$ADDRESS -f &>tests/shell/daemon_log + $PYTHON ./scripts/powerline-daemon -s$ADDRESS -f >tests/shell/daemon_log 2>&1 ' & fi echo "> Testing $TEST_TYPE" @@ -332,7 +332,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te done fi -if ! $PYTHON scripts/powerline-daemon -s$ADDRESS &> tests/shell/daemon_log_2 ; then +if ! $PYTHON scripts/powerline-daemon -s$ADDRESS > tests/shell/daemon_log_2 2>&1 ; then echo "Daemon exited with status $?" FAILED=1 else From acd55bbd87817a4e5e00e3bbdb13109a8787e79a Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Aug 2014 19:25:32 +0400 Subject: [PATCH 1412/1472] Handle errors from Renderer._set_highlighting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is done by moving appropriate get_highlighting calls into segment.py: here errors from contents_func are handled as well. This is a “fix” for #480 that will make such failures cause segment to disappear with better header which should show segment which caused the error. Closes #480 --- powerline/__init__.py | 7 ++--- powerline/renderer.py | 26 +----------------- powerline/segment.py | 61 ++++++++++++++++++++++++++++++++++--------- powerline/theme.py | 12 ++++++++- 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 529826ab..4f5f32bc 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -467,7 +467,7 @@ class Powerline(object): self.get_module_attr = gen_module_attr_getter(self.pl, self.import_paths, self.imported_modules) - self.renderer_options.update( + mergedicts(self.renderer_options, dict( pl=self.pl, term_truecolor=self.common_config['term_truecolor'], ambiwidth=self.common_config['ambiwidth'], @@ -480,7 +480,7 @@ class Powerline(object): 'shutdown_event': self.shutdown_event, 'get_module_attr': self.get_module_attr, }, - ) + )) if not self.run_once and self.common_config['reload_config']: interval = self.common_config['interval'] @@ -535,7 +535,8 @@ class Powerline(object): self._purge_configs('colorscheme') if load_colorscheme: self.colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) - self.renderer_options['colorscheme'] = Colorscheme(self.colorscheme_config, self.colors_config) + self.renderer_options['theme_kwargs']['colorscheme'] = ( + Colorscheme(self.colorscheme_config, self.colors_config)) if load_theme: self._purge_configs('theme') diff --git a/powerline/renderer.py b/powerline/renderer.py index b4a3e9a6..2a80528d 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -36,8 +36,6 @@ class Renderer(object): base class only records this parameter to a ``.local_themes`` attribute. :param dict theme_kwargs: Keyword arguments for ``Theme`` class constructor. - :param Colorscheme colorscheme: - Colorscheme object that holds colors configuration. :param PowerlineLogger pl: Object used for logging. :param int ambiwidth: @@ -93,7 +91,6 @@ class Renderer(object): theme_config, local_themes, theme_kwargs, - colorscheme, pl, ambiwidth=1, **options): @@ -107,7 +104,6 @@ class Renderer(object): self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs - self.colorscheme = colorscheme self.width_data = { 'N': 1, # Neutral 'Na': 1, # Narrow @@ -150,21 +146,6 @@ class Renderer(object): ''' self.theme.shutdown() - def _set_highlighting(self, segment): - segment['highlight'] = self.colorscheme.get_highlighting( - segment['highlight_group'], - segment['mode'], - segment.get('gradient_level') - ) - if segment['divider_highlight_group']: - segment['divider_highlight'] = self.colorscheme.get_highlighting( - segment['divider_highlight_group'], - segment['mode'] - ) - else: - segment['divider_highlight'] = None - return segment - def get_segment_info(self, segment_info, mode): '''Get segment information. @@ -264,12 +245,7 @@ class Renderer(object): def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme): '''Like Renderer.render(), but accept theme in place of matcher_info ''' - segments = [ - self._set_highlighting(segment) - for segment in ( - theme.get_segments(side, line, self.get_segment_info(segment_info, mode), mode) - ) - ] + segments = list(theme.get_segments(side, line, self.get_segment_info(segment_info, mode), mode)) current_width = 0 diff --git a/powerline/segment.py b/powerline/segment.py index d282a765..15659d71 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -99,7 +99,8 @@ def get_attr_func(contents_func, key, args): return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) -def process_segment_lister(pl, segment_info, parsed_segments, side, mode, lister, subsegments, patcher_args): +def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colorscheme, + lister, subsegments, patcher_args): for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) old_pslen = len(parsed_segments) @@ -120,7 +121,15 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, lister ): continue - process_segment(pl, side, subsegment_info, parsed_segments, subsegment, subsegment_mode or mode) + process_segment( + pl, + side, + subsegment_info, + parsed_segments, + subsegment, + subsegment_mode or mode, + colorscheme, + ) new_pslen = len(parsed_segments) if new_pslen > old_pslen + 1 and draw_inner_divider is not None: for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): @@ -128,16 +137,37 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, lister return None -def process_segment(pl, side, segment_info, parsed_segments, segment, mode): +def set_segment_highlighting(pl, colorscheme, segment): + try: + segment['highlight'] = colorscheme.get_highlighting( + segment['highlight_group'], + segment['mode'], + segment.get('gradient_level') + ) + if segment['divider_highlight_group']: + segment['divider_highlight'] = colorscheme.get_highlighting( + segment['divider_highlight_group'], + segment['mode'] + ) + else: + segment['divider_highlight'] = None + except Exception as e: + pl.exception('Failed to set highlight group: {0}', str(e)) + return False + else: + return True + + +def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colorscheme): segment = segment.copy() segment['mode'] = mode + pl.prefix = segment['name'] if segment['type'] in ('function', 'segment_list'): - pl.prefix = segment['name'] try: if segment['type'] == 'function': contents = segment['contents_func'](pl, segment_info) else: - contents = segment['contents_func'](pl, segment_info, parsed_segments, side, mode) + contents = segment['contents_func'](pl, segment_info, parsed_segments, side, mode, colorscheme) except Exception as e: pl.exception('Exception while computing segment: {0}', str(e)) return @@ -176,12 +206,15 @@ def process_segment(pl, side, segment_info, parsed_segments, segment, mode): if draw_inner_divider is not None: segment_copy['draw_soft_divider'] = draw_inner_divider draw_inner_divider = segment_copy.pop('draw_inner_divider', None) - append(segment_copy) + if set_segment_highlighting(pl, colorscheme, segment_copy): + append(segment_copy) else: segment['contents'] = contents - parsed_segments.append(segment) + if set_segment_highlighting(pl, colorscheme, segment): + parsed_segments.append(segment) elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): - parsed_segments.append(segment) + if set_segment_highlighting(pl, colorscheme, segment): + parsed_segments.append(segment) def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr, top_theme): @@ -239,11 +272,13 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'divider_highlight_group': None, 'before': None, 'after': None, - 'contents_func': lambda pl, segment_info, parsed_segments, side, mode: process_segment_lister( - pl, segment_info, parsed_segments, side, mode, - patcher_args=args, - subsegments=subsegments, - lister=_contents_func, + 'contents_func': lambda pl, segment_info, parsed_segments, side, mode, colorscheme: ( + process_segment_lister( + pl, segment_info, parsed_segments, side, mode, colorscheme, + patcher_args=args, + subsegments=subsegments, + lister=_contents_func, + ) ), 'contents': None, 'priority': None, diff --git a/powerline/theme.py b/powerline/theme.py index 2c15886d..3bf8483b 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -31,9 +31,11 @@ class Theme(object): pl, get_module_attr, top_theme, + colorscheme, main_theme_config=None, run_once=False, shutdown_event=None): + self.colorscheme = colorscheme self.dividers = theme_config['dividers'] self.dividers = dict(( (key, dict((k, u(v)) @@ -116,7 +118,15 @@ class Theme(object): if mode not in segment['exclude_modes'] and ( not segment['include_modes'] or mode in segment['include_modes'] ): - process_segment(self.pl, side, segment_info, parsed_segments, segment, mode) + process_segment( + self.pl, + side, + segment_info, + parsed_segments, + segment, + mode, + self.colorscheme, + ) for segment in parsed_segments: segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents From cee13ec0e28a95c3682cfeff105577d3fcee7e8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 15:10:46 +0400 Subject: [PATCH 1413/1472] Add function that prints cyclic references to powerline.lib.debug --- powerline/lib/debug.py | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100755 powerline/lib/debug.py diff --git a/powerline/lib/debug.py b/powerline/lib/debug.py new file mode 100755 index 00000000..d8f1d333 --- /dev/null +++ b/powerline/lib/debug.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet + +import gc +import sys + +from types import FrameType +from itertools import chain + + +# From http://code.activestate.com/recipes/523004-find-cyclical-references/ +def print_cycles(objects, outstream=sys.stdout, show_progress=False): + '''Find reference cycles + + :param list objects: + A list of objects to find cycles in. It is often useful to pass in + gc.garbage to find the cycles that are preventing some objects from + being garbage collected. + :param file outstream: + The stream for output. + :param bool show_progress: + If True, print the number of objects reached as they are found. + ''' + def print_path(path): + for i, step in enumerate(path): + # next "wraps around" + next = path[(i + 1) % len(path)] + + outstream.write(" %s -- " % str(type(step))) + written = False + if isinstance(step, dict): + for key, val in step.items(): + if val is next: + outstream.write("[%s]" % repr(key)) + written = True + break + if key is next: + outstream.write("[key] = %s" % repr(val)) + written = True + break + elif isinstance(step, (list, tuple)): + for i, item in enumerate(step): + if item is next: + outstream.write("[%d]" % i) + written = True + elif getattr(type(step), '__getattribute__', None) in (object.__getattribute__, type.__getattribute__): + for attr in chain(dir(step), getattr(step, '__dict__', ())): + if getattr(step, attr, None) is next: + try: + outstream.write('%r.%s' % (step, attr)) + except TypeError: + outstream.write('.%s' % (step, attr)) + written = True + break + if not written: + outstream.write(repr(step)) + outstream.write(" ->\n") + outstream.write("\n") + + def recurse(obj, start, all, current_path): + if show_progress: + outstream.write("%d\r" % len(all)) + + all[id(obj)] = None + + referents = gc.get_referents(obj) + for referent in referents: + # If we've found our way back to the start, this is + # a cycle, so print it out + if referent is start: + try: + outstream.write('Cyclic reference: %r\n' % referent) + except TypeError: + try: + outstream.write('Cyclic reference: %i (%r)\n' % (id(referent), type(referent))) + except TypeError: + outstream.write('Cyclic reference: %i\n' % id(referent)) + print_path(current_path) + + # Don't go back through the original list of objects, or + # through temporary references to the object, since those + # are just an artifact of the cycle detector itself. + elif referent is objects or isinstance(referent, FrameType): + continue + + # We haven't seen this object before, so recurse + elif id(referent) not in all: + recurse(referent, start, all, current_path + (obj,)) + + for obj in objects: + # We are not interested in non-powerline cyclic references + try: + if not type(obj).__module__.startswith('powerline'): + continue + except AttributeError: + continue + recurse(obj, obj, {}, ()) From ea3cd2c1c7b9ee016f1c5ba489addf9f52216892 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jun 2014 00:52:23 +0400 Subject: [PATCH 1414/1472] Add libuv-based watcher Fixes #821 --- powerline/lib/watcher/__init__.py | 10 ++ powerline/lib/watcher/tree.py | 7 ++ powerline/lib/watcher/uv.py | 167 ++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 powerline/lib/watcher/uv.py diff --git a/powerline/lib/watcher/__init__.py b/powerline/lib/watcher/__init__.py index a36df182..62894695 100644 --- a/powerline/lib/watcher/__init__.py +++ b/powerline/lib/watcher/__init__.py @@ -6,6 +6,7 @@ import sys from powerline.lib.watcher.stat import StatFileWatcher from powerline.lib.watcher.inotify import INotifyFileWatcher from powerline.lib.watcher.tree import TreeWatcher +from powerline.lib.watcher.uv import UvFileWatcher, UvNotFound from powerline.lib.inotify import INotifyError @@ -39,6 +40,9 @@ def create_file_watcher(pl, watcher_type='auto', expire_time=10): # Explicitly selected inotify watcher: do not catch INotifyError then. pl.debug('Using requested inotify watcher', prefix='watcher') return INotifyFileWatcher(expire_time=expire_time) + elif watcher_type == 'uv': + pl.debug('Using requested uv watcher', prefix='watcher') + return UvFileWatcher() if sys.platform.startswith('linux'): try: @@ -47,6 +51,12 @@ def create_file_watcher(pl, watcher_type='auto', expire_time=10): except INotifyError: pl.info('Failed to create inotify watcher', prefix='watcher') + try: + pl.debug('Using libuv-based watcher') + return UvFileWatcher() + except UvNotFound: + pl.debug('Failed to import pyuv') + pl.debug('Using stat-based watcher') return StatFileWatcher() diff --git a/powerline/lib/watcher/tree.py b/powerline/lib/watcher/tree.py index 127261f7..e8efba4e 100644 --- a/powerline/lib/watcher/tree.py +++ b/powerline/lib/watcher/tree.py @@ -7,6 +7,7 @@ from powerline.lib.monotonic import monotonic from powerline.lib.inotify import INotifyError from powerline.lib.path import realpath from powerline.lib.watcher.inotify import INotifyTreeWatcher, DirTooLarge, NoSuchDir, BaseDirChanged +from powerline.lib.watcher.uv import UvTreeWatcher, UvNotFound class DummyTreeWatcher(object): @@ -30,6 +31,8 @@ class TreeWatcher(object): def get_watcher(self, path, ignore_event): if self.watcher_type == 'inotify': return INotifyTreeWatcher(path, ignore_event=ignore_event) + if self.watcher_type == 'uv': + return UvTreeWatcher(path, ignore_event=ignore_event) if self.watcher_type == 'dummy': return DummyTreeWatcher(path) # FIXME @@ -42,6 +45,10 @@ class TreeWatcher(object): except (INotifyError, DirTooLarge) as e: if not isinstance(e, INotifyError): self.pl.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) + try: + return UvTreeWatcher(path, ignore_event=ignore_event) + except UvNotFound: + pass return DummyTreeWatcher(path) else: raise ValueError('Unknown watcher type: {0}'.format(self.watcher_type)) diff --git a/powerline/lib/watcher/uv.py b/powerline/lib/watcher/uv.py new file mode 100644 index 00000000..16e6aeb7 --- /dev/null +++ b/powerline/lib/watcher/uv.py @@ -0,0 +1,167 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, absolute_import, print_function) + +from powerline.lib.path import realpath + +from collections import defaultdict +from threading import RLock +from functools import partial +from threading import Thread + +import os + + +class UvNotFound(NotImplementedError): + pass + + +pyuv = None + + +def import_pyuv(): + global pyuv + if not pyuv: + try: + pyuv = __import__('pyuv') + except ImportError: + raise UvNotFound + + +class UvThread(Thread): + daemon = True + + def __init__(self, loop): + self.uv_loop = loop + super(UvThread, self).__init__() + + def run(self): + while True: + self.uv_loop.run() + + def join(self): + self.uv_loop.stop() + return super(UvThread, self).join() + + +_uv_thread = None + + +def start_uv_thread(): + global _uv_thread + if _uv_thread is None: + loop = pyuv.Loop() + _uv_thread = UvThread(loop) + _uv_thread.start() + return _uv_thread.uv_loop + + +class UvWatcher(object): + def __init__(self): + import_pyuv() + self.watches = {} + self.lock = RLock() + self.loop = start_uv_thread() + + def watch(self, path): + with self.lock: + if path not in self.watches: + try: + self.watches[path] = pyuv.fs.FSEvent( + self.loop, + path, + partial(self._record_event, path), + pyuv.fs.UV_CHANGE | pyuv.fs.UV_RENAME + ) + except pyuv.error.FSEventError as e: + code = e.args[0] + if code == pyuv.errno.UV_ENOENT: + raise OSError('No such file or directory: ' + path) + else: + raise + + def unwatch(self, path): + with self.lock: + try: + watch = self.watches.pop(path) + except KeyError: + return + watch.close(partial(self._stopped_watching, path)) + + def __del__(self): + try: + lock = self.lock + except AttributeError: + pass + else: + with lock: + while self.watches: + path, watch = self.watches.popitem() + watch.close(partial(self._stopped_watching, path)) + + +class UvFileWatcher(UvWatcher): + def __init__(self): + super(UvFileWatcher, self).__init__() + self.events = defaultdict(list) + + def _record_event(self, path, fsevent_handle, filename, events, error): + with self.lock: + self.events[path].append(events) + if events | pyuv.fs.UV_RENAME: + if not os.path.exists(path): + self.watches.pop(path).close() + + def _stopped_watching(self, path, *args): + self.events.pop(path, None) + + def __call__(self, path): + with self.lock: + events = self.events.pop(path, None) + if events: + return True + if path not in self.watches: + self.watch(path) + return True + + +class UvTreeWatcher(UvWatcher): + is_dummy = False + + def __init__(self, basedir, ignore_event=None): + super(UvTreeWatcher, self).__init__() + self.ignore_event = ignore_event or (lambda path, name: False) + self.basedir = realpath(basedir) + self.modified = True + self.watch_directory(self.basedir) + + def watch_directory(self, path): + os.path.walk(path, self.watch_one_directory, None) + + def watch_one_directory(self, arg, dirname, fnames): + try: + self.watch(dirname) + except OSError: + pass + + def _stopped_watching(self, path, *args): + pass + + def _record_event(self, path, fsevent_handle, filename, events, error): + if not self.ignore_event(path, filename): + self.modified = True + if events == pyuv.fs.UV_CHANGE | pyuv.fs.UV_RENAME: + # Stat changes to watched directory are UV_CHANGE|UV_RENAME. It + # is weird. + pass + elif events | pyuv.fs.UV_RENAME: + if not os.path.isdir(path): + self.unwatch(path) + else: + full_name = os.path.join(path, filename) + if os.path.isdir(full_name): + # For some reason mkdir and rmdir both fall into this + # category + self.watch_directory(full_name) + + def __call__(self): + return self.__dict__.pop('modified', False) From 0ee5293e1a37d34cd965380d4c5c8dabbb8dd27f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jun 2014 11:17:23 +0400 Subject: [PATCH 1415/1472] Add tests for libuv-based watcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four possible results of running tests (first three are errors): - “The change to inotify/file2 was not detected” on line 84: most common - “Spurious change detected” at line 82 (uncommon, usually fixed by sleeping before running test in do_test_for_change) - “The change to inotify was not detected” in tree watcher test (e.g. from line 131) (very rare) - All OK. --- tests/test_watcher.py | 177 +++++++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 73 deletions(-) diff --git a/tests/test_watcher.py b/tests/test_watcher.py index 356790b6..64681d96 100644 --- a/tests/test_watcher.py +++ b/tests/test_watcher.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals, print_function, division from powerline.lib.watcher import create_file_watcher, create_tree_watcher, INotifyError +from powerline.lib.watcher.uv import UvNotFound from powerline import get_fallback_logger from powerline.lib.monotonic import monotonic @@ -16,6 +17,14 @@ from tests import TestCase, SkipTest INOTIFY_DIR = 'inotify' + os.environ.get('PYTHON', '') +def clear_dir(dir): + for root, dirs, files in list(os.walk(dir, topdown=False)): + for f in files: + os.remove(os.path.join(root, f)) + for d in dirs: + os.rmdir(os.path.join(root, d)) + + class TestFilesystemWatchers(TestCase): def do_test_for_change(self, watcher, path): st = monotonic() @@ -30,78 +39,104 @@ class TestFilesystemWatchers(TestCase): w = create_file_watcher(pl=get_fallback_logger(), watcher_type='inotify') except INotifyError: raise SkipTest('This test is not suitable for a stat based file watcher') - f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) - with open(f1, 'wb'): - with open(f2, 'wb'): - with open(f3, 'wb'): + return self.do_test_file_watcher(w) + + def do_test_file_watcher(self, w): + try: + f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) + with open(f1, 'wb'): + with open(f2, 'wb'): + with open(f3, 'wb'): + pass + ne = os.path.join(INOTIFY_DIR, 'notexists') + self.assertRaises(OSError, w, ne) + self.assertTrue(w(f1)) + self.assertTrue(w(f2)) + os.utime(f1, None), os.utime(f2, None) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Repeat once + os.utime(f1, None), os.utime(f2, None) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Check that no false changes are reported + self.assertFalse(w(f1), 'Spurious change detected') + self.assertFalse(w(f2), 'Spurious change detected') + # Check that open the file with 'w' triggers a change + with open(f1, 'wb'): + with open(f2, 'wb'): pass - ne = os.path.join(INOTIFY_DIR, 'notexists') - self.assertRaises(OSError, w, ne) - self.assertTrue(w(f1)) - self.assertTrue(w(f2)) - os.utime(f1, None), os.utime(f2, None) - self.do_test_for_change(w, f1) - self.do_test_for_change(w, f2) - # Repeat once - os.utime(f1, None), os.utime(f2, None) - self.do_test_for_change(w, f1) - self.do_test_for_change(w, f2) - # Check that no false changes are reported - self.assertFalse(w(f1), 'Spurious change detected') - self.assertFalse(w(f2), 'Spurious change detected') - # Check that open the file with 'w' triggers a change - with open(f1, 'wb'): - with open(f2, 'wb'): - pass - self.do_test_for_change(w, f1) - self.do_test_for_change(w, f2) - # Check that writing to a file with 'a' triggers a change - with open(f1, 'ab') as f: - f.write(b'1') - self.do_test_for_change(w, f1) - # Check that deleting a file registers as a change - os.unlink(f1) - self.do_test_for_change(w, f1) - # Test that changing the inode of a file does not cause it to stop - # being watched - os.rename(f3, f2) - self.do_test_for_change(w, f2) - self.assertFalse(w(f2), 'Spurious change detected') - os.utime(f2, None) - self.do_test_for_change(w, f2) + self.do_test_for_change(w, f1) + self.do_test_for_change(w, f2) + # Check that writing to a file with 'a' triggers a change + with open(f1, 'ab') as f: + f.write(b'1') + self.do_test_for_change(w, f1) + # Check that deleting a file registers as a change + os.unlink(f1) + self.do_test_for_change(w, f1) + # Test that changing the inode of a file does not cause it to stop + # being watched + os.rename(f3, f2) + self.do_test_for_change(w, f2) + self.assertFalse(w(f2), 'Spurious change detected') + os.utime(f2, None) + self.do_test_for_change(w, f2) + finally: + clear_dir(INOTIFY_DIR) + + def test_uv_file_watcher(self): + try: + w = create_file_watcher(pl=get_fallback_logger(), watcher_type='uv') + except UvNotFound: + raise SkipTest('Pyuv is not available') + return self.do_test_file_watcher(w) def test_tree_watcher(self): tw = create_tree_watcher(get_fallback_logger()) - subdir = os.path.join(INOTIFY_DIR, 'subdir') - os.mkdir(subdir) - if tw.watch(INOTIFY_DIR).is_dummy: - raise SkipTest('No tree watcher available') - self.assertTrue(tw(INOTIFY_DIR)) - self.assertFalse(tw(INOTIFY_DIR)) - changed = partial(self.do_test_for_change, tw, INOTIFY_DIR) - open(os.path.join(INOTIFY_DIR, 'tree1'), 'w').close() - changed() - open(os.path.join(subdir, 'tree1'), 'w').close() - changed() - os.unlink(os.path.join(subdir, 'tree1')) - changed() - os.rmdir(subdir) - changed() - os.mkdir(subdir) - changed() - os.rename(subdir, subdir + '1') - changed() - shutil.rmtree(subdir + '1') - changed() - os.mkdir(subdir) - f = os.path.join(subdir, 'f') - open(f, 'w').close() - changed() - with open(f, 'a') as s: - s.write(' ') - changed() - os.rename(f, f + '1') - changed() + return self.do_test_tree_watcher(tw) + + def do_test_tree_watcher(self, tw): + try: + subdir = os.path.join(INOTIFY_DIR, 'subdir') + os.mkdir(subdir) + try: + if tw.watch(INOTIFY_DIR).is_dummy: + raise SkipTest('No tree watcher available') + except UvNotFound: + raise SkipTest('Pyuv is not available') + self.assertTrue(tw(INOTIFY_DIR)) + self.assertFalse(tw(INOTIFY_DIR)) + changed = partial(self.do_test_for_change, tw, INOTIFY_DIR) + open(os.path.join(INOTIFY_DIR, 'tree1'), 'w').close() + changed() + open(os.path.join(subdir, 'tree1'), 'w').close() + changed() + os.unlink(os.path.join(subdir, 'tree1')) + changed() + os.rmdir(subdir) + changed() + os.mkdir(subdir) + changed() + os.rename(subdir, subdir + '1') + changed() + shutil.rmtree(subdir + '1') + changed() + os.mkdir(subdir) + f = os.path.join(subdir, 'f') + open(f, 'w').close() + changed() + with open(f, 'a') as s: + s.write(' ') + changed() + os.rename(f, f + '1') + changed() + finally: + clear_dir(INOTIFY_DIR) + + def test_uv_tree_watcher(self): + tw = create_tree_watcher(get_fallback_logger(), 'uv') + return self.do_test_tree_watcher(tw) old_cwd = None @@ -116,11 +151,7 @@ def setUpModule(): def tearDownModule(): for d in [INOTIFY_DIR]: - for root, dirs, files in list(os.walk(d, topdown=False)): - for file in files: - os.remove(os.path.join(root, file)) - for dir in dirs: - os.rmdir(os.path.join(root, dir)) + clear_dir(d) os.rmdir(d) os.chdir(old_cwd) From 560600fca9e8c2acb281947effb5e79980aec7db Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Aug 2014 15:26:19 +0400 Subject: [PATCH 1416/1472] Use shutil.rmtree in tests --- tests/test_lib.py | 8 ++------ tests/test_watcher.py | 15 +++++++-------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index ea16e38c..16f4c760 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -4,6 +4,7 @@ from __future__ import division import threading import os import re +import shutil from time import sleep from subprocess import call, PIPE @@ -581,12 +582,7 @@ class TestVCS(TestCase): @classmethod def tearDownClass(cls): for repo_dir in [GIT_REPO] + ([HG_REPO] if use_mercurial else []) + ([BZR_REPO] if use_bzr else []): - for root, dirs, files in list(os.walk(repo_dir, topdown=False)): - for file in files: - os.remove(os.path.join(root, file)) - for dir in dirs: - os.rmdir(os.path.join(root, dir)) - os.rmdir(repo_dir) + shutil.rmtree(repo_dir) if use_mercurial: if cls.powerline_old_HGRCPATH is None: os.environ.pop('HGRCPATH') diff --git a/tests/test_watcher.py b/tests/test_watcher.py index 64681d96..cfb73237 100644 --- a/tests/test_watcher.py +++ b/tests/test_watcher.py @@ -1,16 +1,17 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import, unicode_literals, print_function, division +import shutil +import os + +from time import sleep +from functools import partial + from powerline.lib.watcher import create_file_watcher, create_tree_watcher, INotifyError from powerline.lib.watcher.uv import UvNotFound from powerline import get_fallback_logger from powerline.lib.monotonic import monotonic -import shutil -from time import sleep -from functools import partial -import os - from tests import TestCase, SkipTest @@ -150,9 +151,7 @@ def setUpModule(): def tearDownModule(): - for d in [INOTIFY_DIR]: - clear_dir(d) - os.rmdir(d) + shutil.rmtree(INOTIFY_DIR) os.chdir(old_cwd) From 8d3376ce07a5e4035273d44da1bc77132576a866 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 15:49:50 +0400 Subject: [PATCH 1417/1472] Ultimately disable pyuv watcher tests --- tests/test_watcher.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_watcher.py b/tests/test_watcher.py index cfb73237..323a39ab 100644 --- a/tests/test_watcher.py +++ b/tests/test_watcher.py @@ -87,6 +87,7 @@ class TestFilesystemWatchers(TestCase): clear_dir(INOTIFY_DIR) def test_uv_file_watcher(self): + raise SkipTest('Uv watcher tests are not stable') try: w = create_file_watcher(pl=get_fallback_logger(), watcher_type='uv') except UvNotFound: @@ -136,6 +137,7 @@ class TestFilesystemWatchers(TestCase): clear_dir(INOTIFY_DIR) def test_uv_tree_watcher(self): + raise SkipTest('Uv watcher tests are not stable') tw = create_tree_watcher(get_fallback_logger(), 'uv') return self.do_test_tree_watcher(tw) From f320fb3df35d4912daab781e98324f068cd35c74 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 16:08:30 +0400 Subject: [PATCH 1418/1472] Remove filler segment type It was replaced with `"width": "auto"` and is not used anywhere. I do not even know whether it works. --- docs/source/configuration/reference.rst | 4 ++-- powerline/lint/__init__.py | 2 -- powerline/renderer.py | 10 +++++----- powerline/segment.py | 5 ----- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index ac4b2171..b224eb1e 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -370,8 +370,8 @@ ascii Theme without any unicode characters at all Each segment dictionary has the following options: ``type`` - The segment type. Can be one of ``function`` (default), ``string``, - ``filler`` or ``segments_list``: + The segment type. Can be one of ``function`` (default), ``string`` or + ``segments_list``: ``function`` The segment contents is the return value of the function defined in diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 22f037b2..e11bbc33 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -810,13 +810,11 @@ generic_keys = set(( type_keys = { 'function': set(('function', 'args', 'draw_inner_divider')), 'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')), - 'filler': set(('type', 'highlight_group', 'divider_highlight_group')), 'segment_list': set(('function', 'segments', 'args', 'type')), } required_keys = { 'function': set(('function',)), 'string': set(()), - 'filler': set(), 'segment_list': set(('function', 'segments',)), } highlight_keys = set(('highlight_group', 'name')) diff --git a/powerline/renderer.py b/powerline/renderer.py index 2a80528d..a94a51b4 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -186,11 +186,11 @@ class Renderer(object): def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None): '''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 - 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. + 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 segments with + ``"width": "auto"`` are provided they will fill the remaining space + until the desired width is reached. :param str mode: Mode string. Affects contents (colors and the set of segments) of diff --git a/powerline/segment.py b/powerline/segment.py index 15659d71..6449a43a 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -75,14 +75,9 @@ def get_string(data, segment): return data['get_key'](False, segment, None, None, name, 'contents'), None, None, None, name -def get_filler(data, segment): - return None, None, None, None, None - - segment_getters = { 'function': get_function, 'string': get_string, - 'filler': get_filler, 'segment_list': get_function, } From 875f8e98b1a9da6e4323a14e2b9b6066cc8b9768 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 16:39:49 +0400 Subject: [PATCH 1419/1472] Update documentation --- docs/source/configuration/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index b224eb1e..326de4ba 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -441,7 +441,7 @@ ascii Theme without any unicode characters at all ``align`` Aligns the segments contents to the left (``l``), center (``c``) or - right (``r``). + right (``r``). Has no sense if ``width`` key was not specified. .. _config-themes-seg-width: From 0da40f08a59d5e40c0968c5515df84e038a98cb4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 16:50:23 +0400 Subject: [PATCH 1420/1472] Fix syntastic error --- tests/lib/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index bd271103..5399df1a 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -44,6 +44,7 @@ def urllib_read(query_url): else: raise NotImplementedError + class Process(object): def __init__(self, output, err): self.output = output From bfa335d96a12c53e84ce80d85a68b8610d1e8138 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 17:08:32 +0400 Subject: [PATCH 1421/1472] Fix typo in test_configuration.py --- tests/test_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 34e402c8..de15ab88 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -94,7 +94,7 @@ config = { highlighted_string('g', 'str2'), ], 'right': [ - highlighted_string('f', 'str2', width='auto', align='right'), + highlighted_string('f', 'str2', width='auto', align='r'), ], }, }, From 5b038dba8264085c4144de8bc4e673e39096c13d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 17:10:30 +0400 Subject: [PATCH 1422/1472] Use `expand` key to fill segments --- powerline/renderer.py | 43 ++++++++++++------------------------------- powerline/segment.py | 6 ++---- powerline/theme.py | 27 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index a94a51b4..ca62e387 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -269,7 +269,7 @@ class Renderer(object): segments.remove(segment) # Distribute the remaining space on spacer segments - segments_spacers = [segment for segment in segments if segment['width'] == 'auto'] + segments_spacers = [segment for segment in segments if segment['expand'] is not None] if segments_spacers: if not segments_priority: # Update segment['_len'] and current_width if not already done @@ -278,15 +278,12 @@ class Renderer(object): current_width = self._render_length(theme, segments, divider_widths) distribute_len, distribute_len_remainder = divmod(width - current_width, len(segments_spacers)) for segment in segments_spacers: - if segment['align'] == 'l': - segment['_space_right'] += distribute_len - elif segment['align'] == 'r': - segment['_space_left'] += distribute_len - elif segment['align'] == 'c': - space_side, space_side_remainder = divmod(distribute_len, 2) - segment['_space_left'] += space_side + space_side_remainder - segment['_space_right'] += space_side - segments_spacers[0]['_space_right'] += distribute_len_remainder + segment['contents'] = ( + segment['expand']( + self.pl, + distribute_len + (1 if distribute_len_remainder > 0 else 0), + segment)) + distribute_len_remainder -= 1 # `_len` key is not needed anymore, but current_width should have an # actual value for various bindings. current_width = width @@ -321,7 +318,7 @@ class Renderer(object): )) draw_divider = segment['draw_' + divider_type + '_divider'] - segment_len += segment['_space_left'] + segment['_space_right'] + outer_padding + segment_len += outer_padding if draw_divider: segment_len += divider_widths[side][divider_type] + divider_spaces @@ -363,30 +360,14 @@ class Renderer(object): if draw_divider: divider_raw = self.escape(theme.get_divider(side, divider_type)) if side == 'left': - contents_raw = ( - outer_padding + (segment['_space_left'] * ' ') - + contents_raw - + ((divider_spaces + segment['_space_right']) * ' ') - ) + contents_raw = outer_padding + contents_raw + (divider_spaces * ' ') else: - contents_raw = ( - ((divider_spaces + segment['_space_left']) * ' ') - + contents_raw - + (segment['_space_right'] * ' ') + outer_padding - ) + contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding else: if side == 'left': - contents_raw = ( - outer_padding + (segment['_space_left'] * ' ') - + contents_raw - + (segment['_space_right'] * ' ') - ) + contents_raw = outer_padding + contents_raw else: - contents_raw = ( - (segment['_space_left'] * ' ') - + contents_raw - + (segment['_space_right'] * ' ') + outer_padding - ) + contents_raw = contents_raw + outer_padding # Replace spaces with no-break spaces contents_raw = contents_raw.translate(self.np_character_translations) diff --git a/powerline/segment.py b/powerline/segment.py index 6449a43a..00a48a30 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -285,6 +285,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'include_modes': segment.get('include_modes', []), 'width': None, 'align': None, + 'expand': None, 'startup': None, 'shutdown': None, 'mode': None, @@ -292,8 +293,6 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge '_rendered_hl': '', '_len': None, '_contents_len': None, - '_space_left': 0, - '_space_right': 0, } if segment_type == 'function': @@ -331,6 +330,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'include_modes': segment.get('include_modes', []), 'width': segment.get('width'), 'align': segment.get('align', 'l'), + 'expand': None, 'startup': startup_func, 'shutdown': shutdown_func, 'mode': None, @@ -338,8 +338,6 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge '_rendered_hl': '', '_len': None, '_contents_len': None, - '_space_left': 0, - '_space_right': 0, } return get diff --git a/powerline/theme.py b/powerline/theme.py index 3bf8483b..2fbdd79e 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -23,6 +23,26 @@ def new_empty_segment_line(): } +def add_spaces_left(pl, amount, segment): + return (' ' * amount) + segment['contents'] + + +def add_spaces_right(pl, amount, segment): + return segment['contents'] + (' ' * amount) + + +def add_spaces_center(pl, amount, segment): + amount, remainder = divmod(amount, 2) + return (' ' * (amount + remainder)) + segment['contents'] + (' ' * amount) + + +expand_functions = { + 'l': add_spaces_right, + 'r': add_spaces_left, + 'c': add_spaces_center, +} + + class Theme(object): def __init__(self, ext, @@ -128,6 +148,13 @@ class Theme(object): self.colorscheme, ) for segment in parsed_segments: + width = segment['width'] + align = segment['align'] + if width == 'auto': + segment['expand'] = expand_functions.get(align) + if segment['expand'] is None: + self.pl.error('Align argument must be “r”, “l” or “c”, not “{0}”', align) + segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents if segment['width'] and segment['width'] != 'auto': From d6c603daf0a6b74b390d5a4ae909a5beff318961 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 17:54:46 +0400 Subject: [PATCH 1423/1472] Add support for `segment.expand` Closes #154 --- docs/source/configuration/reference.rst | 12 ++- docs/source/develop/listers.rst | 7 +- docs/source/develop/segments.rst | 109 ++++++++++++++++++++++++ powerline/segment.py | 18 ++-- powerline/theme.py | 2 +- tests/test_configuration.py | 20 +++++ 6 files changed, 159 insertions(+), 9 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 326de4ba..c3096b9c 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -369,6 +369,8 @@ ascii Theme without any unicode characters at all Each segment dictionary has the following options: + .. _config-themes-seg-type: + ``type`` The segment type. Can be one of ``function`` (default), ``string`` or ``segments_list``: @@ -439,9 +441,13 @@ ascii Theme without any unicode characters at all ``args`` A dict of arguments to be passed to a ``function`` segment. + .. _config-themes-seg-align: + ``align`` Aligns the segments contents to the left (``l``), center (``c``) or - right (``r``). Has no sense if ``width`` key was not specified. + right (``r``). Has no sense if ``width`` key was not specified or if + segment provides its own function for ``auto`` ``width`` handling and + does not care about this option. .. _config-themes-seg-width: @@ -454,6 +460,8 @@ ascii Theme without any unicode characters at all either returned by a function or a static string, and the contents can be aligned with the ``align`` property. + .. _config-themes-seg-priority: + ``priority`` Optional segment priority. Segments with priority ``None`` (the default priority, represented by ``null`` in json) will always be included, @@ -482,6 +490,8 @@ ascii Theme without any unicode characters at all segments. Only applicable for functions returning multiple segments. Defaults to ``False``. + .. _config-themes-seg-exclude_modes: + ``exclude_modes`` A list of modes where this segment will be excluded: The segment is included in all modes, *except* for the modes in this list. diff --git a/docs/source/develop/listers.rst b/docs/source/develop/listers.rst index 8c7b439e..85513718 100644 --- a/docs/source/develop/listers.rst +++ b/docs/source/develop/listers.rst @@ -36,8 +36,11 @@ Listers must return a sequence of pairs. First item in the pair must contain a ``segment_info`` dictionary specific to one of the listed entities. Second item must contain another dictionary: it will be used to modify the -resulting segment. In addition to usual keys that describe segment the following -keys may be present (it is advised that *only* the following keys will be used): +resulting segment. In addition to :ref:`usual keys that describe segment +` the following keys may be present (it is advised that +*only* the following keys will be used): + +.. _dev-listers-mode: ``mode`` Segment-specific mode. Used to alter segment highlighting. diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index fe802818..22f88206 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -51,6 +51,8 @@ powerline: theme-specific values go directly to :ref:`top-level themes `. +.. _dev-segments-startup: + ``startup`` This attribute must be a callable which accepts the following keyword arguments: @@ -68,6 +70,8 @@ powerline: used (more specific: when :py:class:`powerline.Powerline` constructor received true ``run_once`` argument). +.. _dev-segments-shutdown: + ``shutdown`` This attribute must be a callable that accepts no arguments and shuts down threads and frees any other resources allocated in ``startup`` method of the @@ -75,6 +79,29 @@ powerline: This function is not called when ``startup`` method is not called. +.. _dev-segments-expand: + +``expand`` + This attribute must be a callable that accepts the following keyword + arguments: + + * ``pl``: :py:class:`powerline.PowerlineLogger` instance which is to be used + for logging. + * ``amount``: integer number representing amount of display cells result + must occupy. + + .. warning:: + “Amount of display cells” is *not* number of Unicode codepoints, string + length, or byte count. It is suggested that your function should look + something like ``return (' ' * amount) + segment['contents']`` where + ``' '`` may be replaced with anything that is known to occupy exactly + one display cell. + * ``segment``: :ref:`segment dictionary `. + * Any arguments found in user configuration for the given segment (i.e. + :ref:`args key `). + + It must return new value of :ref:`contents ` key. + This callable object should may return either a string (``unicode`` in Python2 or ``str`` in Python3, *not* ``str`` in Python2 or ``bytes`` in Python3) object or a list of dictionaries. String object is a short form of the following return @@ -87,6 +114,8 @@ value: 'highlight_group': [segment_name], }] +.. _dev-segments-return: + Returned list is a list of segments treated independently, except for :ref:`draw_inner_divider key `. @@ -158,6 +187,86 @@ Detailed description of used dictionary keys: No error occurs if segment has this key, but no used highlight groups use gradient color. +``_*`` + Keys starting with underscore are reserved for powerline and must not be + returned. + +``__*`` + Keys starting with two underscores are reserved for the segment functions, + specifically for :ref:`expand function `. + +.. _dev-segments-segment: + +Segment dictionary +================== + +Segment dictionary contains the following keys: + +* All keys returned by segment function (if it was used). + +* All of the following keys: + + ``name`` + Segment name: value of the :ref:`name key ` or + function name (last component of the :ref:`function key + `). May be ``None``. + + ``type`` + :ref:`Segment type `. Always represents actual type + and is never ``None``. + + ``highlight_group``, ``divider_highlight_group`` + Used highlight groups. May be ``None``. + + .. _dev-segments-seg-around: + + ``before``, ``after`` + Value of :ref:`before ` or :ref:`after + ` configuration options. May be ``None`` as well as + an empty string. + + ``contents_func`` + Function used to get segment contents. May be ``None``. + + .. _dev-segments-seg-contents: + + ``contents`` + Actual segment contents, excluding dividers and :ref:`before/after + `. May be ``None``. + + ``priority`` + :ref:`Segment priority `. May be ``None`` for no + priority (such segments are always shown). + + ``draw_soft_divider``, ``draw_hard_divider``, ``draw_inner_divider`` + :ref:`Divider control flags `. + + ``side`` + Segment side: ``right`` or ``left``. + + ``exclude_modes``, ``include_modes`` + :ref:`Mode display control lists `. May be + empty, but may not be ``None``. + + ``width``, ``align`` + :ref:`Width and align options `. May be ``None``. + + ``expand`` + Partially applied :ref:`expand function `. Accepts + ``pl``, ``amount`` and ``segment`` positional parameters, keyword parameters + from :ref:`args ` key were applied. + + ``startup`` + Partially applied :ref:`startup function `. Accepts + ``pl`` and ``shutdown_event`` positional parameters, keyword parameters from + :ref:`args ` key were applied. + + ``shutdown`` + :ref:`Shutdown function `. Accepts no argument. + + ``mode`` + :ref:`Segment-specific mode `. May be ``None``. + Segments layout =============== diff --git a/powerline/segment.py b/powerline/segment.py index 00a48a30..5cbe31dc 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -82,14 +82,20 @@ segment_getters = { } -def get_attr_func(contents_func, key, args): +def get_attr_func(contents_func, key, args, is_space_func=False): try: func = getattr(contents_func, key) except AttributeError: return None else: - if args is None: - return lambda: func() + if is_space_func: + def expand_func(pl, amount, segment): + try: + return func(pl=pl, amount=amount, segment=segment, **args) + except Exception as e: + pl.exception('Exception while computing segment expansion: {0}', str(e)) + return segment['contents'] + (' ' * amount) + return expand_func else: return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) @@ -297,7 +303,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge if segment_type == 'function': startup_func = get_attr_func(_contents_func, 'startup', args) - shutdown_func = get_attr_func(_contents_func, 'shutdown', None) + shutdown_func = getattr(_contents_func, 'shutdown', None) + expand_func = get_attr_func(_contents_func, 'expand', args, True) if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) @@ -311,6 +318,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge startup_func = None shutdown_func = None contents_func = None + expand_func = None return { 'name': name or function_name, @@ -330,7 +338,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'include_modes': segment.get('include_modes', []), 'width': segment.get('width'), 'align': segment.get('align', 'l'), - 'expand': None, + 'expand': expand_func, 'startup': startup_func, 'shutdown': shutdown_func, 'mode': None, diff --git a/powerline/theme.py b/powerline/theme.py index 2fbdd79e..2558ca0e 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -150,7 +150,7 @@ class Theme(object): for segment in parsed_segments: width = segment['width'] align = segment['align'] - if width == 'auto': + if width == 'auto' and segment['expand'] is None: segment['expand'] = expand_functions.get(align) if segment['expand'] is None: self.pl.error('Align argument must be “r”, “l” or “c”, not “{0}”', align) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index de15ab88..e4fc7f5f 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -474,6 +474,26 @@ class TestSegmentAttributes(TestRender): } self.assertRenderEqual(p, '{56} pl;{6-}>>{--}') + @add_args + def test_expand(self, p, config): + def m1(divider=',', **kwargs): + return divider.join(kwargs.keys()) + divider + + def expand(pl, amount, segment, **kwargs): + return ('-' * amount) + segment['contents'] + + m1.expand = expand + sys.modules['bar'] = Args(m1=m1) + config['themes/test/default']['segments'] = { + 'left': [ + { + 'function': 'bar.m1', + 'width': 'auto' + } + ] + } + self.assertRenderEqual(p, '{56} ----pl,{6-}>>{--}', width=10) + class TestSegmentData(TestRender): @add_args From 2505d0b827fb165ff3e39d5e7038289aba4eb411 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Aug 2014 18:19:56 +0400 Subject: [PATCH 1424/1472] Implement segment truncation Closes #161 (requires implementation for specific segments though) --- docs/source/develop/segments.rst | 19 +++++++++++++++---- powerline/renderer.py | 23 ++++++++++++++++------- powerline/segment.py | 6 +++++- tests/test_configuration.py | 19 +++++++++++++++++++ 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 22f88206..e1dd1b64 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -102,6 +102,16 @@ powerline: It must return new value of :ref:`contents ` key. +.. _dev-segments-truncate: + +``truncate`` + Like :ref:`expand function `, but for truncating + segments. Here ``amount`` means the number of display cells which must be + freed. + + This function is called for all segments before powerline starts purging + them to free space. + This callable object should may return either a string (``unicode`` in Python2 or ``str`` in Python3, *not* ``str`` in Python2 or ``bytes`` in Python3) object or a list of dictionaries. String object is a short form of the following return @@ -251,10 +261,11 @@ Segment dictionary contains the following keys: ``width``, ``align`` :ref:`Width and align options `. May be ``None``. - ``expand`` - Partially applied :ref:`expand function `. Accepts - ``pl``, ``amount`` and ``segment`` positional parameters, keyword parameters - from :ref:`args ` key were applied. + ``expand``, ``truncate`` + Partially applied :ref:`expand ` or :ref:`truncate + ` function. Accepts ``pl``, ``amount`` and + ``segment`` positional parameters, keyword parameters from :ref:`args + ` key were applied. ``startup`` Partially applied :ref:`startup function `. Accepts diff --git a/powerline/renderer.py b/powerline/renderer.py index ca62e387..a1408b5e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,9 +1,12 @@ # vim:fileencoding=utf-8:noet -from powerline.theme import Theme -from unicodedata import east_asian_width, combining import os +from unicodedata import east_asian_width, combining +from itertools import chain + +from powerline.theme import Theme + try: NBSP = unicode(' ', 'utf-8') except NameError: @@ -262,11 +265,17 @@ class Renderer(object): # Create an ordered list of segments that can be dropped segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) - for segment in segments_priority: - current_width = self._render_length(theme, segments, divider_widths) - if current_width <= width: - break - segments.remove(segment) + no_priority_segments = filter(lambda segment: segment['priority'] is None, segments) + current_width = self._render_length(theme, segments, divider_widths) + if current_width > width: + for segment in chain(segments_priority, no_priority_segments): + if segment['truncate'] is not None: + segment['contents'] = segment['truncate'](self.pl, current_width - width, segment) + for segment in segments_priority: + if current_width <= width: + break + segments.remove(segment) + current_width = self._render_length(theme, segments, divider_widths) # Distribute the remaining space on spacer segments segments_spacers = [segment for segment in segments if segment['expand'] is not None] diff --git a/powerline/segment.py b/powerline/segment.py index 5cbe31dc..4dd283e4 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -93,7 +93,7 @@ def get_attr_func(contents_func, key, args, is_space_func=False): try: return func(pl=pl, amount=amount, segment=segment, **args) except Exception as e: - pl.exception('Exception while computing segment expansion: {0}', str(e)) + pl.exception('Exception while computing {0} function: {1}', key, str(e)) return segment['contents'] + (' ' * amount) return expand_func else: @@ -292,6 +292,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'width': None, 'align': None, 'expand': None, + 'truncate': None, 'startup': None, 'shutdown': None, 'mode': None, @@ -305,6 +306,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge startup_func = get_attr_func(_contents_func, 'startup', args) shutdown_func = getattr(_contents_func, 'shutdown', None) expand_func = get_attr_func(_contents_func, 'expand', args, True) + truncate_func = get_attr_func(_contents_func, 'truncate', args, True) if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) @@ -319,6 +321,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge shutdown_func = None contents_func = None expand_func = None + truncate_func = None return { 'name': name or function_name, @@ -339,6 +342,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'width': segment.get('width'), 'align': segment.get('align', 'l'), 'expand': expand_func, + 'truncate': truncate_func, 'startup': startup_func, 'shutdown': shutdown_func, 'mode': None, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index e4fc7f5f..f6cbc975 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -494,6 +494,25 @@ class TestSegmentAttributes(TestRender): } self.assertRenderEqual(p, '{56} ----pl,{6-}>>{--}', width=10) + @add_args + def test_truncate(self, p, config): + def m1(divider=',', **kwargs): + return divider.join(kwargs.keys()) + divider + + def truncate(pl, amount, segment, **kwargs): + return segment['contents'][:-amount] + + m1.truncate = truncate + sys.modules['bar'] = Args(m1=m1) + config['themes/test/default']['segments'] = { + 'left': [ + { + 'function': 'bar.m1' + } + ] + } + self.assertRenderEqual(p, '{56} p{6-}>>{--}', width=4) + class TestSegmentData(TestRender): @add_args From 0e98bc2d4f3def33be72cc7da77a144eab9fd124 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Aug 2014 21:29:03 +0400 Subject: [PATCH 1425/1472] Fix style in a number of places MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixes lines where line with N tab indent is followed by a line with N+2 tab indent or greater (most of such lines were already fixed in ae92d83eae5142322ff20b9aa81eb53b0b363575, but regex used there has one flow: it does not catches lines where N=0 for which case first `\+` needs to be replaced with `*`). - Replace print(…, file=sys.stderr) with sys.stderr.write in powerline-daemon. --- powerline/lib/watcher/inotify.py | 16 +++++++++------ powerline/lint/__init__.py | 4 ++-- powerline/lint/markedjson/constructor.py | 25 +++++++++--------------- powerline/lint/markedjson/resolver.py | 24 +++++++++++------------ scripts/powerline-daemon | 14 +++++++------ 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/powerline/lib/watcher/inotify.py b/powerline/lib/watcher/inotify.py index 48c730c0..8891875e 100644 --- a/powerline/lib/watcher/inotify.py +++ b/powerline/lib/watcher/inotify.py @@ -213,13 +213,17 @@ class INotifyTreeWatcher(INotify): def add_watch(self, path): bpath = path if isinstance(path, bytes) else path.encode(self.fenc) - wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath), - # Ignore symlinks and watch only directories - self.DONT_FOLLOW | self.ONLYDIR | + wd = self._add_watch( + self._inotify_fd, + ctypes.c_char_p(bpath), - self.MODIFY | self.CREATE | self.DELETE | - self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | - self.ATTRIB | self.DELETE_SELF) + # Ignore symlinks and watch only directories + self.DONT_FOLLOW | self.ONLYDIR | + + self.MODIFY | self.CREATE | self.DELETE | + self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | + self.ATTRIB | self.DELETE_SELF + ) if wd == -1: eno = ctypes.get_errno() if eno == errno.ENOTDIR: diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index e11bbc33..f7c74f88 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -560,8 +560,8 @@ def check_top_theme(theme, data, context, echoerr): return True, False, False -divider_spec = Spec().type(unicode).len('le', 3, - lambda value: 'Divider {0!r} is too large!'.format(value)).copy +divider_spec = Spec().type(unicode).len( + 'le', 3, (lambda value: 'Divider {0!r} is too large!'.format(value))).copy ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy top_theme_spec = Spec().type(unicode).func(check_top_theme).copy ext_spec = Spec( diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index f887d9e2..f99608f2 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -264,32 +264,25 @@ class Constructor(BaseConstructor): Constructor.add_constructor( - 'tag:yaml.org,2002:null', - Constructor.construct_yaml_null) + 'tag:yaml.org,2002:null', Constructor.construct_yaml_null) Constructor.add_constructor( - 'tag:yaml.org,2002:bool', - Constructor.construct_yaml_bool) + 'tag:yaml.org,2002:bool', Constructor.construct_yaml_bool) Constructor.add_constructor( - 'tag:yaml.org,2002:int', - Constructor.construct_yaml_int) + 'tag:yaml.org,2002:int', Constructor.construct_yaml_int) Constructor.add_constructor( - 'tag:yaml.org,2002:float', - Constructor.construct_yaml_float) + 'tag:yaml.org,2002:float', Constructor.construct_yaml_float) Constructor.add_constructor( - 'tag:yaml.org,2002:str', - Constructor.construct_yaml_str) + 'tag:yaml.org,2002:str', Constructor.construct_yaml_str) Constructor.add_constructor( - 'tag:yaml.org,2002:seq', - Constructor.construct_yaml_seq) + 'tag:yaml.org,2002:seq', Constructor.construct_yaml_seq) Constructor.add_constructor( - 'tag:yaml.org,2002:map', - Constructor.construct_yaml_map) + 'tag:yaml.org,2002:map', Constructor.construct_yaml_map) -Constructor.add_constructor(None, - Constructor.construct_undefined) +Constructor.add_constructor( + None, Constructor.construct_undefined) diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py index 0196c70b..40b5130d 100644 --- a/powerline/lint/markedjson/resolver.py +++ b/powerline/lint/markedjson/resolver.py @@ -110,21 +110,21 @@ class Resolver(BaseResolver): Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:bool', - re.compile(r'''^(?:true|false)$''', re.X), - list('yYnNtTfFoO')) + 'tag:yaml.org,2002:bool', + re.compile(r'''^(?:true|false)$''', re.X), + list('yYnNtTfFoO')) Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:float', - re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X), - list('-0123456789')) + 'tag:yaml.org,2002:float', + re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X), + list('-0123456789')) Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:int', - re.compile(r'^(?:0|-?[1-9]\d*)$', re.X), - list('-0123456789')) + 'tag:yaml.org,2002:int', + re.compile(r'^(?:0|-?[1-9]\d*)$', re.X), + list('-0123456789')) Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:null', - re.compile(r'^null$', re.X), - ['n']) + 'tag:yaml.org,2002:null', + re.compile(r'^null$', re.X), + ['n']) diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index d14eac04..956710d9 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -246,7 +246,7 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull): # exit first parent sys.exit(0) except OSError as e: - print ("fork #1 failed: %d (%s)" % (e.errno, e.strerror), file=sys.stderr) + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) # decouple from parent environment @@ -261,7 +261,7 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull): # exit from second parent sys.exit(0) except OSError as e: - print ("fork #2 failed: %d (%s)" % (e.errno, e.strerror), file=sys.stderr) + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) # Redirect standard file descriptors. @@ -399,16 +399,18 @@ def main(): # Create a locked pid file containing the daemon's PID if lockpidfile() is None: if not args.quiet: - print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), - file=sys.stderr) + sys.stderr.write( + 'The daemon is already running. Use %s -k to kill it.\n' % ( + os.path.basename(sys.argv[0]))) raise SystemExit(1) # Bind to address or bail if we cannot bind sock = check_existing() if sock is None: if not args.quiet: - print ('The daemon is already running. Use %s -k to kill it.' % os.path.basename(sys.argv[0]), - file=sys.stderr) + sys.stderr.write( + 'The daemon is already running. Use %s -k to kill it.\n' % ( + os.path.basename(sys.argv[0]))) raise SystemExit(1) if args.foreground: From aaed00e078eda972f957cc5f3b3bcf62e64e56df Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Aug 2014 21:33:56 +0400 Subject: [PATCH 1426/1472] Fix installation notes location. These notes are parts of the list entries. --- docs/source/installation/osx.rst | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index 70523b78..fc2f6f8f 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -17,26 +17,29 @@ Python package . -.. note:: - In case you want or have to use ``powerline.sh`` socat-based client you - should also install GNU env named ``genv``. This may be achieved by running - ``brew install coreutils``. + .. note:: + In case you want or have to use ``powerline.sh`` socat-based client you + should also install GNU env named ``genv``. This may be achieved by + running ``brew install coreutils``. 2. Install Powerline using the following command:: pip install --user git+git://github.com/Lokaltog/powerline -.. warning:: When using ``brew install`` to install Python one must not supply - ``--user`` flag to ``pip``. + .. warning:: + When using ``brew install`` to install Python one must not supply + ``--user`` flag to ``pip``. -.. note:: You need to use the GitHub URI when installing Powerline! This - project is currently unavailable on the PyPI due to a naming conflict - with an unrelated project. + .. note:: + You need to use the GitHub URI when installing Powerline! This project is + currently unavailable on the PyPI due to a naming conflict with an + unrelated project. -.. note:: If you are powerline developer you should be aware that ``pip install - --editable`` does not currently fully work. If you install powerline this way - you will be missing ``powerline`` executable and need to symlink it. It will - be located in ``scripts/powerline``. + .. note:: + If you are powerline developer you should be aware that ``pip install + --editable`` does not currently fully work. If you install powerline this + way you will be missing ``powerline`` executable and need to symlink it. It + will be located in ``scripts/powerline``. Vim installation ================ From d952b469d63eca6c142a70834e5aae63f61f688a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Aug 2014 21:44:18 +0400 Subject: [PATCH 1427/1472] Fix syntastic error in powerline.lib.memoize --- powerline/lib/memoize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index e180f300..1fb96323 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -35,6 +35,6 @@ class memoize(object): cached = self.cache[key] = { 'result': func(**kwargs), 'time': monotonic(), - } + } return cached['result'] return decorated_function From 545bd6b52f0147a4341ce844f81ba19bb2e3005d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Aug 2014 22:09:09 +0400 Subject: [PATCH 1428/1472] Fix some style errors reported by syntastic in .segments.plugin.ctrlp --- powerline/segments/vim/plugin/ctrlp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim/plugin/ctrlp.py b/powerline/segments/vim/plugin/ctrlp.py index 3c6af70d..f54f8bac 100644 --- a/powerline/segments/vim/plugin/ctrlp.py +++ b/powerline/segments/vim/plugin/ctrlp.py @@ -33,7 +33,7 @@ def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): segments.append({ 'contents': 'regex', 'highlight_group': ['ctrlp.regex', 'background'], - }) + }) segments += [ { @@ -62,7 +62,7 @@ def ctrlp_stl_left_main(pl, focus, byfname, regex, prev, item, next, marked): 'contents': marked, 'highlight_group': ['ctrlp.marked', 'background'], 'draw_inner_divider': True, - }) + }) return segments From 06211cbe63e8edd1eafcefcce430595a51e5dbee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Aug 2014 22:55:26 +0400 Subject: [PATCH 1429/1472] Unify imports Now imports follow the following structure: 1. __future__ line: exactly one line allowed: from __future__ import (unicode_literals, division, absolute_import, print_function) (powerline.shell is the only exception due to problems with argparse). 2. Standard python library imports in a form `import X`. 3. Standard python library imports in a form `from X import Y`. 4. and 5. 2. and 3. for third-party (non-python and non-powerline imports). 6. 3. for powerline non-test imports. 7. and 8. 2. and 3. for powerline testing module imports. Each list entry is separated by exactly one newline from another import. If there is module docstring it goes between `# vim:` comment and `__future__` import. So the structure containing all items is the following: #!/usr/bin/env python # vim:fileencoding=utf-8:noet '''Powerline super module''' import sys from argparse import ArgumentParser import psutil from colormath.color_diff import delta_e_cie2000 from powerline.lib.unicode import u import tests.vim as vim_module from tests import TestCase . --- CONTRIBUTING.rst | 59 ++++++++++++++++ client/powerline.py | 1 - docs/source/conf.py | 2 + docs/source/powerline_autodoc.py | 12 ++-- powerline/__init__.py | 2 +- .../bindings/awesome/powerline-awesome.py | 9 ++- powerline/bindings/config.py | 3 +- powerline/bindings/i3/powerline-i3.py | 12 ++-- powerline/bindings/ipython/post_0_11.py | 5 +- powerline/bindings/ipython/pre_0_11.py | 7 +- powerline/bindings/qtile/widget.py | 1 + powerline/bindings/tmux/__init__.py | 3 +- powerline/bindings/vim/__init__.py | 7 +- powerline/bindings/zsh/__init__.py | 6 +- powerline/colorscheme.py | 7 +- powerline/config.py | 3 +- powerline/ipython.py | 1 + powerline/lib/__init__.py | 5 +- powerline/lib/config.py | 15 ++-- powerline/lib/debug.py | 1 + powerline/lib/humanize_bytes.py | 3 + powerline/lib/inotify.py | 9 +-- powerline/lib/memoize.py | 2 + powerline/lib/monotonic.py | 5 +- powerline/lib/path.py | 2 +- powerline/lib/shell.py | 6 +- powerline/lib/threaded.py | 3 +- powerline/lib/unicode.py | 8 ++- powerline/lib/url.py | 1 + powerline/lib/vcs/__init__.py | 5 +- powerline/lib/vcs/bzr.py | 3 +- powerline/lib/vcs/git.py | 3 +- powerline/lib/vcs/mercurial.py | 2 +- powerline/lib/watcher/__init__.py | 5 +- powerline/lib/watcher/inotify.py | 2 +- powerline/lib/watcher/stat.py | 2 +- powerline/lib/watcher/tree.py | 2 +- powerline/lib/watcher/uv.py | 6 +- powerline/lint/__init__.py | 1 + powerline/lint/inspect.py | 2 +- powerline/lint/markedjson/__init__.py | 6 +- powerline/lint/markedjson/composer.py | 44 ++++++------ powerline/lint/markedjson/constructor.py | 31 ++++----- powerline/lint/markedjson/error.py | 9 +-- powerline/lint/markedjson/events.py | 8 +-- powerline/lint/markedjson/loader.py | 17 ++--- powerline/lint/markedjson/markedvalue.py | 9 +-- powerline/lint/markedjson/nodes.py | 4 ++ powerline/lint/markedjson/parser.py | 69 ++++++++++--------- powerline/lint/markedjson/reader.py | 18 +++-- powerline/lint/markedjson/resolver.py | 19 ++--- powerline/lint/markedjson/scanner.py | 45 +++++------- powerline/lint/markedjson/tokens.py | 4 ++ powerline/listers/vim.py | 7 +- powerline/matchers/__init__.py | 4 ++ powerline/matchers/vim/__init__.py | 4 +- powerline/matchers/vim/plugin/__init__.py | 4 ++ powerline/matchers/vim/plugin/ctrlp.py | 18 +++-- powerline/matchers/vim/plugin/gundo.py | 1 + powerline/matchers/vim/plugin/nerdtree.py | 1 + powerline/renderer.py | 13 ++-- powerline/renderers/i3bar.py | 4 +- powerline/renderers/ipython/__init__.py | 1 + powerline/renderers/pango_markup.py | 5 +- powerline/renderers/shell/__init__.py | 3 +- powerline/renderers/shell/bash.py | 3 +- powerline/renderers/shell/ksh.py | 3 +- powerline/renderers/shell/tcsh.py | 3 +- powerline/renderers/shell/zsh.py | 3 +- powerline/renderers/tmux.py | 3 +- powerline/renderers/vim.py | 19 ++--- powerline/segment.py | 2 +- powerline/segments/__init__.py | 2 +- powerline/segments/common.py | 5 +- powerline/segments/i3wm.py | 1 + powerline/segments/ipython.py | 1 + powerline/segments/shell.py | 1 + powerline/segments/tmux.py | 2 +- powerline/segments/vim/__init__.py | 7 +- powerline/segments/vim/plugin/__init__.py | 4 ++ powerline/segments/vim/plugin/ctrlp.py | 1 + powerline/segments/vim/plugin/nerdtree.py | 1 + powerline/segments/vim/plugin/syntastic.py | 1 + powerline/segments/vim/plugin/tagbar.py | 1 + powerline/shell.py | 2 + powerline/theme.py | 5 +- powerline/vim.py | 3 +- scripts/powerline-config | 3 + scripts/powerline-daemon | 1 + scripts/powerline-lint | 7 +- scripts/powerline-render | 4 ++ setup.py | 4 +- tests/__init__.py | 3 + tests/lib/__init__.py | 2 + tests/lib/config_mock.py | 15 ++-- tests/lib/fsconfig.py | 2 +- tests/matchers.py | 1 - tests/path/vim.py | 2 + tests/test_cmdline.py | 12 ++-- tests/test_config_merging.py | 15 ++-- tests/test_config_reload.py | 2 +- tests/test_configuration.py | 2 +- tests/test_lib.py | 4 +- tests/test_lib_config.py | 3 +- tests/test_provided_config_files.py | 5 +- tests/test_segments.py | 3 +- tests/test_shells/postproc.py | 4 +- tests/test_watcher.py | 2 +- tools/colors_find.py | 5 +- tools/generate_gradients.py | 15 ++-- 110 files changed, 453 insertions(+), 317 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index e31718f8..2ae9facd 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -67,6 +67,65 @@ Programming style documentation and commit messages. * It is allowed to have too long lines. It is advised though to avoid lines wider then a hundred of characters. +* Imports have the following structure: + + 1. Shebang and modeline in a form + + .. code-block:: python + + #!/usr/bin/env python + # vim:fileencoding=utf-8:noet + + . Modeline is required, shebang is not. If shebang is present file must end + with + + .. code-block:: python + + if __name__ == '__main__': + # Actual script here + + 2. Module docstring. + 3. ``__future__`` import exactly in a form + + .. code-block:: python + + from __future__ import (unicode_literals, division, absolute_import, print_function) + + (powerline.shell is the only exception due to problems with argparse). It + is not separated by newline with shebang and modeline, but is with + docstring. + 4. Standard python library imports in a form ``import X``. + 5. Standard python library imports in a form ``from X import Y``. + 6. Third-party (non-python and non-powerline) library imports in a form + ``import X``. + 7. Third-party library imports in a form ``from X import Y``. + 8. Powerline non-test imports in a form ``from powerline.X import Y``. + 9. Powerline test imports in a form ``import tests.vim as vim_module``. + 10. Powerline test imports in a form ``from tests.X import Y``. + + Each entry is separated by newline from another entry. Any entry except for + the first and third ones is optional. Example with all entries: + + .. code-block:: python + + #!/usr/bin/env python + # vim:fileencoding=utf-8:noet + + '''Powerline super module''' + + import sys + + from argparse import ArgumentParser + + import psutil + + from colormath.color_diff import delta_e_cie2000 + + from powerline.lib.unicode import u + + import tests.vim as vim_module + + from tests import TestCase Submitting changes ================== diff --git a/client/powerline.py b/client/powerline.py index ac3d6356..07a01cb6 100755 --- a/client/powerline.py +++ b/client/powerline.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet - from __future__ import (unicode_literals, division, absolute_import, print_function) import sys diff --git a/docs/source/conf.py b/docs/source/conf.py index 5d0f2161..f451f462 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,8 +1,10 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import sys + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(os.getcwd())))) sys.path.insert(0, os.path.abspath(os.getcwd())) diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 55e0af35..971717df 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -1,13 +1,13 @@ # vim:fileencoding=utf-8:noet -from sphinx.ext import autodoc +from __future__ import (unicode_literals, division, absolute_import, print_function) + from inspect import formatargspec + +from sphinx.ext import autodoc + from powerline.lint.inspect import getconfigargspec from powerline.segments import Segment - -try: - from __builtin__ import unicode -except ImportError: - unicode = lambda s, enc: s # NOQA +from powerline.lib.unicode import unicode def formatvalue(val): diff --git a/powerline/__init__.py b/powerline/__init__.py index 4f5f32bc..78aa9bb0 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from __future__ import absolute_import import os import sys import logging diff --git a/powerline/bindings/awesome/powerline-awesome.py b/powerline/bindings/awesome/powerline-awesome.py index 3e4125dc..11d27f57 100755 --- a/powerline/bindings/awesome/powerline-awesome.py +++ b/powerline/bindings/awesome/powerline-awesome.py @@ -1,11 +1,14 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from time import sleep +from subprocess import Popen, PIPE from powerline import Powerline -import sys -from time import sleep from powerline.lib.monotonic import monotonic -from subprocess import Popen, PIPE powerline = Powerline('wm', renderer_module='pango_markup') powerline.update_renderer() diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 042a6e9c..71afca84 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import re diff --git a/powerline/bindings/i3/powerline-i3.py b/powerline/bindings/i3/powerline-i3.py index d6d84f59..c5e01c8a 100755 --- a/powerline/bindings/i3/powerline-i3.py +++ b/powerline/bindings/i3/powerline-i3.py @@ -1,15 +1,17 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet -from __future__ import print_function - -from powerline import Powerline -from powerline.lib.monotonic import monotonic +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import time -import i3 + from threading import Lock +import i3 + +from powerline import Powerline +from powerline.lib.monotonic import monotonic + if __name__ == '__main__': name = 'wm' diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 5586fee4..bf2358ac 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -1,12 +1,13 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from weakref import ref -from powerline.ipython import IPythonPowerline, RewriteResult - from IPython.core.prompts import PromptManager from IPython.core.magic import Magics, magics_class, line_magic +from powerline.ipython import IPythonPowerline, RewriteResult + @magics_class class PowerlineMagics(Magics): diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index d5389efe..6e5e356d 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -1,16 +1,17 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import re from weakref import ref -from powerline.ipython import IPythonPowerline, RewriteResult -from powerline.lib.unicode import string - from IPython.Prompts import BasePrompt from IPython.ipapi import get as get_ipython from IPython.ipapi import TryNext +from powerline.ipython import IPythonPowerline, RewriteResult +from powerline.lib.unicode import string + class IPythonInfo(object): def __init__(self, cache): diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index 6c1e6602..83acd9fa 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from libqtile import bar from libqtile.widget import base diff --git a/powerline/bindings/tmux/__init__.py b/powerline/bindings/tmux/__init__.py index 2adf08f9..d56abde4 100644 --- a/powerline/bindings/tmux/__init__.py +++ b/powerline/bindings/tmux/__init__.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals, division, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) import re import os diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index eec4da3c..277c37ca 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import codecs @@ -8,6 +9,10 @@ try: except ImportError: vim = {} +if not hasattr(vim, 'bindeval'): + import json + + if hasattr(vim, 'bindeval'): def vim_get_func(f, rettype=None): '''Return a vim function binding.''' @@ -19,8 +24,6 @@ if hasattr(vim, 'bindeval'): except vim.error: return None else: - import json - class VimFunc(object): '''Evaluate a vim function using vim.eval(). diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 9fa82794..46940c80 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,12 +1,12 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals, division, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) import atexit -import zsh - from weakref import WeakValueDictionary, ref +import zsh + from powerline.shell import ShellPowerline from powerline.lib import parsedotval from powerline.lib.unicode import unicode diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index a49c5c09..4ae8d74f 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,10 +1,9 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from copy import copy -try: - from __builtin__ import unicode -except ImportError: - unicode = str + +from powerline.lib.unicode import unicode DEFAULT_MODE_KEY = None diff --git a/powerline/config.py b/powerline/config.py index c895da5e..edcf921f 100644 --- a/powerline/config.py +++ b/powerline/config.py @@ -1,8 +1,9 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from __future__ import absolute_import, unicode_literals, print_function import os + POWERLINE_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BINDINGS_DIRECTORY = os.path.join(POWERLINE_ROOT, 'powerline', 'bindings') TMUX_CONFIG_DIRECTORY = os.path.join(BINDINGS_DIRECTORY, 'tmux') diff --git a/powerline/ipython.py b/powerline/ipython.py index f658f111..331f6927 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline import Powerline from powerline.lib import mergedicts diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 7a0d7add..ee82d216 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -1,7 +1,10 @@ # vim:fileencoding=utf-8:noet -from functools import wraps +from __future__ import (unicode_literals, division, absolute_import, print_function) + import json +from functools import wraps + REMOVE_THIS_KEY = object() diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 8742667b..0c95e476 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -1,15 +1,16 @@ # vim:fileencoding=utf-8:noet - -from powerline.lib.threaded import MultiRunnedThread -from powerline.lib.watcher import create_file_watcher -from copy import deepcopy - -from threading import Event, Lock -from collections import defaultdict +from __future__ import (unicode_literals, division, absolute_import, print_function) import json import codecs +from copy import deepcopy +from threading import Event, Lock +from collections import defaultdict + +from powerline.lib.threaded import MultiRunnedThread +from powerline.lib.watcher import create_file_watcher + def open_file(path): return codecs.open(path, encoding='utf-8') diff --git a/powerline/lib/debug.py b/powerline/lib/debug.py index d8f1d333..478b9ed8 100755 --- a/powerline/lib/debug.py +++ b/powerline/lib/debug.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import gc import sys diff --git a/powerline/lib/humanize_bytes.py b/powerline/lib/humanize_bytes.py index 570d803e..c98a1170 100644 --- a/powerline/lib/humanize_bytes.py +++ b/powerline/lib/humanize_bytes.py @@ -1,6 +1,9 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from math import log + + unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2])) diff --git a/powerline/lib/inotify.py b/powerline/lib/inotify.py index ec40bcd7..510008ed 100644 --- a/powerline/lib/inotify.py +++ b/powerline/lib/inotify.py @@ -1,8 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import - -__copyright__ = '2013, Kovid Goyal ' -__docformat__ = 'restructuredtext en' +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import os @@ -13,6 +10,10 @@ import struct from ctypes.util import find_library +__copyright__ = '2013, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + class INotifyError(Exception): pass diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 1fb96323..cedbe45e 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -1,6 +1,8 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from functools import wraps + from powerline.lib.monotonic import monotonic diff --git a/powerline/lib/monotonic.py b/powerline/lib/monotonic.py index 93f147f1..5f9a80f9 100644 --- a/powerline/lib/monotonic.py +++ b/powerline/lib/monotonic.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import division, absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) try: try: @@ -13,11 +12,9 @@ try: from time import CLOCK_MONOTONIC as CLOCK_ID # NOQA monotonic = lambda: clock_gettime(CLOCK_ID) - except ImportError: # >=python-3.3 from time import monotonic # NOQA - except ImportError: import ctypes import sys diff --git a/powerline/lib/path.py b/powerline/lib/path.py index be6872e7..a55d3e0a 100644 --- a/powerline/lib/path.py +++ b/powerline/lib/path.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import os diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index 19b25c8b..96b87128 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -1,12 +1,12 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from __future__ import absolute_import, unicode_literals, division, print_function +import sys +import os from subprocess import Popen, PIPE from locale import getlocale, getdefaultlocale, LC_MESSAGES from functools import partial -import sys -import os if sys.platform.startswith('win32'): diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index e4f2f0a4..5e30988a 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) from threading import Thread, Lock, Event from types import MethodType diff --git a/powerline/lib/unicode.py b/powerline/lib/unicode.py index 34505e9a..cdaab65f 100644 --- a/powerline/lib/unicode.py +++ b/powerline/lib/unicode.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet - +from __future__ import (unicode_literals, division, absolute_import, print_function) from locale import getpreferredencoding @@ -10,6 +10,12 @@ except ImportError: unicode = str # NOQA +try: + from __builtin__ import unichr +except ImportError: + unichr = chr + + def u(s): '''Return unicode instance assuming UTF-8 encoded string. ''' diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 6e599349..4f1cfd8c 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) try: from urllib.error import HTTPError diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index ba00eca2..9d8f60a0 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -1,8 +1,9 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import errno + from threading import Lock from collections import defaultdict @@ -226,7 +227,7 @@ def guess(path, create_watcher): continue try: if vcs not in globals(): - globals()[vcs] = getattr(__import__('powerline.lib.vcs', fromlist=[vcs]), vcs) + globals()[vcs] = getattr(__import__(str('powerline.lib.vcs'), fromlist=[str(vcs)]), str(vcs)) return globals()[vcs].Repository(directory, create_watcher) except: pass diff --git a/powerline/lib/vcs/bzr.py b/powerline/lib/vcs/bzr.py index f8818bdc..012f612e 100644 --- a/powerline/lib/vcs/bzr.py +++ b/powerline/lib/vcs/bzr.py @@ -1,9 +1,10 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals, division, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import os import re + from io import StringIO from bzrlib import (workingtree, status, library_state, trace, ui) diff --git a/powerline/lib/vcs/git.py b/powerline/lib/vcs/git.py index 42bdd321..21b5e434 100644 --- a/powerline/lib/vcs/git.py +++ b/powerline/lib/vcs/git.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import (unicode_literals, absolute_import, print_function) +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import sys diff --git a/powerline/lib/vcs/mercurial.py b/powerline/lib/vcs/mercurial.py index cebd7960..71963dd3 100644 --- a/powerline/lib/vcs/mercurial.py +++ b/powerline/lib/vcs/mercurial.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import os diff --git a/powerline/lib/watcher/__init__.py b/powerline/lib/watcher/__init__.py index 62894695..f1cb5d79 100644 --- a/powerline/lib/watcher/__init__.py +++ b/powerline/lib/watcher/__init__.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys @@ -11,8 +11,7 @@ from powerline.lib.inotify import INotifyError def create_file_watcher(pl, watcher_type='auto', expire_time=10): - ''' - Create an object that can watch for changes to specified files + '''Create an object that can watch for changes to specified files Use ``.__call__()`` method of the returned object to start watching the file or check whether file has changed since last call. diff --git a/powerline/lib/watcher/inotify.py b/powerline/lib/watcher/inotify.py index 8891875e..ea7edea4 100644 --- a/powerline/lib/watcher/inotify.py +++ b/powerline/lib/watcher/inotify.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import errno import os diff --git a/powerline/lib/watcher/stat.py b/powerline/lib/watcher/stat.py index ff2bf154..3ac70c74 100644 --- a/powerline/lib/watcher/stat.py +++ b/powerline/lib/watcher/stat.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import os diff --git a/powerline/lib/watcher/tree.py b/powerline/lib/watcher/tree.py index e8efba4e..7d2b83f6 100644 --- a/powerline/lib/watcher/tree.py +++ b/powerline/lib/watcher/tree.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import (unicode_literals, absolute_import, print_function) +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys diff --git a/powerline/lib/watcher/uv.py b/powerline/lib/watcher/uv.py index 16e6aeb7..26e9dbfe 100644 --- a/powerline/lib/watcher/uv.py +++ b/powerline/lib/watcher/uv.py @@ -1,14 +1,14 @@ # vim:fileencoding=utf-8:noet -from __future__ import (unicode_literals, absolute_import, print_function) +from __future__ import (unicode_literals, division, absolute_import, print_function) -from powerline.lib.path import realpath +import os from collections import defaultdict from threading import RLock from functools import partial from threading import Thread -import os +from powerline.lib.path import realpath class UvNotFound(NotImplementedError): diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index f7c74f88..bf88d2c6 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import itertools import sys diff --git a/powerline/lint/inspect.py b/powerline/lint/inspect.py index 4e7f1a10..b6e0380b 100644 --- a/powerline/lint/inspect.py +++ b/powerline/lint/inspect.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) from inspect import ArgSpec, getargspec diff --git a/powerline/lint/markedjson/__init__.py b/powerline/lint/markedjson/__init__.py index f8ef7483..aa084eaf 100644 --- a/powerline/lint/markedjson/__init__.py +++ b/powerline/lint/markedjson/__init__.py @@ -1,7 +1,7 @@ -__version__ = '3.10' +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) - -from .loader import Loader +from powerline.lint.markedjson.loader import Loader def load(stream, Loader=Loader): diff --git a/powerline/lint/markedjson/composer.py b/powerline/lint/markedjson/composer.py index 25e60109..3067af04 100644 --- a/powerline/lint/markedjson/composer.py +++ b/powerline/lint/markedjson/composer.py @@ -1,8 +1,12 @@ -__all__ = ['Composer', 'ComposerError'] +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from .error import MarkedError -from .events import * # NOQA -from .nodes import * # NOQA +from powerline.lint.markedjson import nodes +from powerline.lint.markedjson import events +from powerline.lint.markedjson.error import MarkedError + + +__all__ = ['Composer', 'ComposerError'] class ComposerError(MarkedError): @@ -15,15 +19,15 @@ class Composer: def check_node(self): # Drop the STREAM-START event. - if self.check_event(StreamStartEvent): + if self.check_event(events.StreamStartEvent): self.get_event() # If there are more documents available? - return not self.check_event(StreamEndEvent) + return not self.check_event(events.StreamEndEvent) def get_node(self): # Get the root node of the next document. - if not self.check_event(StreamEndEvent): + if not self.check_event(events.StreamEndEvent): return self.compose_document() def get_single_node(self): @@ -32,11 +36,11 @@ class Composer: # Compose a document if the stream is not empty. document = None - if not self.check_event(StreamEndEvent): + if not self.check_event(events.StreamEndEvent): document = self.compose_document() # Ensure that the stream contains no more documents. - if not self.check_event(StreamEndEvent): + if not self.check_event(events.StreamEndEvent): event = self.get_event() raise ComposerError( "expected a single document in the stream", @@ -64,11 +68,11 @@ class Composer: def compose_node(self, parent, index): self.descend_resolver(parent, index) - if self.check_event(ScalarEvent): + if self.check_event(events.ScalarEvent): node = self.compose_scalar_node() - elif self.check_event(SequenceStartEvent): + elif self.check_event(events.SequenceStartEvent): node = self.compose_sequence_node() - elif self.check_event(MappingStartEvent): + elif self.check_event(events.MappingStartEvent): node = self.compose_mapping_node() self.ascend_resolver() return node @@ -77,18 +81,18 @@ class Composer: event = self.get_event() tag = event.tag if tag is None or tag == '!': - tag = self.resolve(ScalarNode, event.value, event.implicit, event.start_mark) - node = ScalarNode(tag, event.value, event.start_mark, event.end_mark, style=event.style) + tag = self.resolve(nodes.ScalarNode, event.value, event.implicit, event.start_mark) + node = nodes.ScalarNode(tag, event.value, event.start_mark, event.end_mark, style=event.style) return node def compose_sequence_node(self): start_event = self.get_event() tag = start_event.tag if tag is None or tag == '!': - tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) + tag = self.resolve(nodes.SequenceNode, None, start_event.implicit) + node = nodes.SequenceNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) index = 0 - while not self.check_event(SequenceEndEvent): + while not self.check_event(events.SequenceEndEvent): node.value.append(self.compose_node(node, index)) index += 1 end_event = self.get_event() @@ -99,9 +103,9 @@ class Composer: start_event = self.get_event() tag = start_event.tag if tag is None or tag == '!': - tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) - while not self.check_event(MappingEndEvent): + tag = self.resolve(nodes.MappingNode, None, start_event.implicit) + node = nodes.MappingNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) + while not self.check_event(events.MappingEndEvent): # key_event = self.peek_event() item_key = self.compose_node(node, None) # if item_key in node.value: diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index f99608f2..f51f25ac 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -1,19 +1,16 @@ -__all__ = ['BaseConstructor', 'Constructor', 'ConstructorError'] - -from .error import MarkedError -from .nodes import * # NOQA -from .markedvalue import gen_marked_value +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import collections import types from functools import wraps +from powerline.lint.markedjson.error import MarkedError -try: - from __builtin__ import unicode -except ImportError: - unicode = str # NOQA +from powerline.lint.markedjson import nodes +from powerline.lint.markedjson.markedvalue import gen_marked_value +from powerline.lib.unicode import unicode def marked(func): @@ -94,7 +91,7 @@ class BaseConstructor: @marked def construct_scalar(self, node): - if not isinstance(node, ScalarNode): + if not isinstance(node, nodes.ScalarNode): raise ConstructorError( None, None, "expected a scalar node, but found %s" % node.id, @@ -103,7 +100,7 @@ class BaseConstructor: return node.value def construct_sequence(self, node, deep=False): - if not isinstance(node, SequenceNode): + if not isinstance(node, nodes.SequenceNode): raise ConstructorError( None, None, "expected a sequence node, but found %s" % node.id, @@ -116,7 +113,7 @@ class BaseConstructor: @marked def construct_mapping(self, node, deep=False): - if not isinstance(node, MappingNode): + if not isinstance(node, nodes.MappingNode): raise ConstructorError( None, None, "expected a mapping node, but found %s" % node.id, @@ -156,7 +153,7 @@ class BaseConstructor: class Constructor(BaseConstructor): def construct_scalar(self, node): - if isinstance(node, MappingNode): + if isinstance(node, nodes.MappingNode): for key_node, value_node in node.value: if key_node.tag == 'tag:yaml.org,2002:value': return self.construct_scalar(value_node) @@ -169,13 +166,13 @@ class Constructor(BaseConstructor): key_node, value_node = node.value[index] if key_node.tag == 'tag:yaml.org,2002:merge': del node.value[index] - if isinstance(value_node, MappingNode): + if isinstance(value_node, nodes.MappingNode): self.flatten_mapping(value_node) merge.extend(value_node.value) - elif isinstance(value_node, SequenceNode): + elif isinstance(value_node, nodes.SequenceNode): submerge = [] for subnode in value_node.value: - if not isinstance(subnode, MappingNode): + if not isinstance(subnode, nodes.MappingNode): raise ConstructorError( "while constructing a mapping", node.start_mark, @@ -203,7 +200,7 @@ class Constructor(BaseConstructor): node.value = merge + node.value def construct_mapping(self, node, deep=False): - if isinstance(node, MappingNode): + if isinstance(node, nodes.MappingNode): self.flatten_mapping(node) return BaseConstructor.construct_mapping(self, node, deep=deep) diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index 1e1c7214..643c86b3 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -1,13 +1,10 @@ -__all__ = ['Mark', 'MarkedError', 'echoerr', 'NON_PRINTABLE'] - +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import re -try: - from __builtin__ import unichr -except ImportError: - unichr = chr # NOQA +from powerline.lib.unicode import unichr NON_PRINTABLE = re.compile('[^\t\n\x20-\x7E' + unichr(0x85) + (unichr(0xA0) + '-' + unichr(0xD7FF)) + (unichr(0xE000) + '-' + unichr(0xFFFD)) + ']') diff --git a/powerline/lint/markedjson/events.py b/powerline/lint/markedjson/events.py index 587c5ae4..ef8a70e5 100644 --- a/powerline/lint/markedjson/events.py +++ b/powerline/lint/markedjson/events.py @@ -1,6 +1,8 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + # Abstract classes. - - class Event(object): def __init__(self, start_mark=None, end_mark=None): self.start_mark = start_mark @@ -38,8 +40,6 @@ class CollectionEndEvent(Event): # Implementations. - - class StreamStartEvent(Event): def __init__(self, start_mark=None, end_mark=None, encoding=None): self.start_mark = start_mark diff --git a/powerline/lint/markedjson/loader.py b/powerline/lint/markedjson/loader.py index 50ae6d05..3ee56866 100644 --- a/powerline/lint/markedjson/loader.py +++ b/powerline/lint/markedjson/loader.py @@ -1,12 +1,13 @@ -__all__ = ['Loader'] +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from .reader import Reader -from .scanner import Scanner -from .parser import Parser -from .composer import Composer -from .constructor import Constructor -from .resolver import Resolver -from .error import echoerr +from powerline.lint.markedjson.reader import Reader +from powerline.lint.markedjson.scanner import Scanner +from powerline.lint.markedjson.parser import Parser +from powerline.lint.markedjson.composer import Composer +from powerline.lint.markedjson.constructor import Constructor +from powerline.lint.markedjson.resolver import Resolver +from powerline.lint.markedjson.error import echoerr class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 6a9600dc..74a62b64 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -1,10 +1,7 @@ -__all__ = ['gen_marked_value', 'MarkedValue'] +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) - -try: - from __builtin__ import unicode -except ImportError: - unicode = str +from powerline.lib.unicode import unicode def gen_new(cls): diff --git a/powerline/lint/markedjson/nodes.py b/powerline/lint/markedjson/nodes.py index 9325a64c..66ad8433 100644 --- a/powerline/lint/markedjson/nodes.py +++ b/powerline/lint/markedjson/nodes.py @@ -1,3 +1,7 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + class Node(object): def __init__(self, tag, value, start_mark, end_mark): self.tag = tag diff --git a/powerline/lint/markedjson/parser.py b/powerline/lint/markedjson/parser.py index aa5f7ade..960b74a3 100644 --- a/powerline/lint/markedjson/parser.py +++ b/powerline/lint/markedjson/parser.py @@ -1,8 +1,9 @@ -__all__ = ['Parser', 'ParserError'] +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from .error import MarkedError -from .tokens import * # NOQA -from .events import * # NOQA +from powerline.lint.markedjson.error import MarkedError +from powerline.lint.markedjson import tokens +from powerline.lint.markedjson import events class ParserError(MarkedError): @@ -58,7 +59,7 @@ class Parser: def parse_stream_start(self): # Parse the stream start. token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, encoding=token.encoding) + event = events.StreamStartEvent(token.start_mark, token.end_mark, encoding=token.encoding) # Prepare the next state. self.state = self.parse_implicit_document_start @@ -67,10 +68,10 @@ class Parser: def parse_implicit_document_start(self): # Parse an implicit document. - if not self.check_token(StreamEndToken): + if not self.check_token(tokens.StreamEndToken): token = self.peek_token() start_mark = end_mark = token.start_mark - event = DocumentStartEvent(start_mark, end_mark, explicit=False) + event = events.DocumentStartEvent(start_mark, end_mark, explicit=False) # Prepare the next state. self.states.append(self.parse_document_end) @@ -83,17 +84,17 @@ class Parser: def parse_document_start(self): # Parse an explicit document. - if not self.check_token(StreamEndToken): + if not self.check_token(tokens.StreamEndToken): token = self.peek_token() self.echoerr( None, None, ("expected '', but found %r" % token.id), token.start_mark ) - return StreamEndEvent(token.start_mark, token.end_mark) + return events.StreamEndEvent(token.start_mark, token.end_mark) else: # Parse the end of the stream. token = self.get_token() - event = StreamEndEvent(token.start_mark, token.end_mark) + event = events.StreamEndEvent(token.start_mark, token.end_mark) assert not self.states assert not self.marks self.state = None @@ -104,7 +105,7 @@ class Parser: token = self.peek_token() start_mark = end_mark = token.start_mark explicit = False - event = DocumentEndEvent(start_mark, end_mark, explicit=explicit) + event = events.DocumentEndEvent(start_mark, end_mark, explicit=explicit) # Prepare the next state. self.state = self.parse_document_start @@ -120,22 +121,22 @@ class Parser: start_mark = end_mark = self.peek_token().start_mark event = None implicit = True - if self.check_token(ScalarToken): + if self.check_token(tokens.ScalarToken): token = self.get_token() end_mark = token.end_mark if token.plain: implicit = (True, False) else: implicit = (False, True) - event = ScalarEvent(implicit, token.value, start_mark, end_mark, style=token.style) + event = events.ScalarEvent(implicit, token.value, start_mark, end_mark, style=token.style) self.state = self.states.pop() - elif self.check_token(FlowSequenceStartToken): + elif self.check_token(tokens.FlowSequenceStartToken): end_mark = self.peek_token().end_mark - event = SequenceStartEvent(implicit, start_mark, end_mark, flow_style=True) + event = events.SequenceStartEvent(implicit, start_mark, end_mark, flow_style=True) self.state = self.parse_flow_sequence_first_entry - elif self.check_token(FlowMappingStartToken): + elif self.check_token(tokens.FlowMappingStartToken): end_mark = self.peek_token().end_mark - event = MappingStartEvent(implicit, start_mark, end_mark, flow_style=True) + event = events.MappingStartEvent(implicit, start_mark, end_mark, flow_style=True) self.state = self.parse_flow_mapping_first_key else: token = self.peek_token() @@ -152,11 +153,11 @@ class Parser: return self.parse_flow_sequence_entry(first=True) def parse_flow_sequence_entry(self, first=False): - if not self.check_token(FlowSequenceEndToken): + if not self.check_token(tokens.FlowSequenceEndToken): if not first: - if self.check_token(FlowEntryToken): + if self.check_token(tokens.FlowEntryToken): self.get_token() - if self.check_token(FlowSequenceEndToken): + if self.check_token(tokens.FlowSequenceEndToken): token = self.peek_token() self.echoerr( "While parsing a flow sequence", self.marks[-1], @@ -169,11 +170,11 @@ class Parser: ("expected ',' or ']', but got %r" % token.id), token.start_mark ) - if not self.check_token(FlowSequenceEndToken): + if not self.check_token(tokens.FlowSequenceEndToken): self.states.append(self.parse_flow_sequence_entry) return self.parse_node() token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) + event = events.SequenceEndEvent(token.start_mark, token.end_mark) self.state = self.states.pop() self.marks.pop() return event @@ -181,7 +182,7 @@ class Parser: def parse_flow_sequence_entry_mapping_end(self): self.state = self.parse_flow_sequence_entry token = self.peek_token() - return MappingEndEvent(token.start_mark, token.start_mark) + return events.MappingEndEvent(token.start_mark, token.start_mark) def parse_flow_mapping_first_key(self): token = self.get_token() @@ -189,11 +190,11 @@ class Parser: return self.parse_flow_mapping_key(first=True) def parse_flow_mapping_key(self, first=False): - if not self.check_token(FlowMappingEndToken): + if not self.check_token(tokens.FlowMappingEndToken): if not first: - if self.check_token(FlowEntryToken): + if self.check_token(tokens.FlowEntryToken): self.get_token() - if self.check_token(FlowMappingEndToken): + if self.check_token(tokens.FlowMappingEndToken): token = self.peek_token() self.echoerr( "While parsing a flow mapping", self.marks[-1], @@ -205,9 +206,9 @@ class Parser: "while parsing a flow mapping", self.marks[-1], ("expected ',' or '}', but got %r" % token.id), token.start_mark ) - if self.check_token(KeyToken): + if self.check_token(tokens.KeyToken): token = self.get_token() - if not self.check_token(ValueToken, FlowEntryToken, FlowMappingEndToken): + if not self.check_token(tokens.ValueToken, tokens.FlowEntryToken, tokens.FlowMappingEndToken): self.states.append(self.parse_flow_mapping_value) return self.parse_node() else: @@ -216,12 +217,12 @@ class Parser: "while parsing a flow mapping", self.marks[-1], ("expected value, but got %r" % token.id), token.start_mark ) - elif not self.check_token(FlowMappingEndToken): + elif not self.check_token(tokens.FlowMappingEndToken): token = self.peek_token() - expect_key = self.check_token(ValueToken, FlowEntryToken) + expect_key = self.check_token(tokens.ValueToken, tokens.FlowEntryToken) if not expect_key: self.get_token() - expect_key = self.check_token(ValueToken) + expect_key = self.check_token(tokens.ValueToken) if expect_key: raise ParserError( @@ -235,15 +236,15 @@ class Parser: ("expected ':', but got %r" % token.id), token.start_mark ) token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) + event = events.MappingEndEvent(token.start_mark, token.end_mark) self.state = self.states.pop() self.marks.pop() return event def parse_flow_mapping_value(self): - if self.check_token(ValueToken): + if self.check_token(tokens.ValueToken): token = self.get_token() - if not self.check_token(FlowEntryToken, FlowMappingEndToken): + if not self.check_token(tokens.FlowEntryToken, tokens.FlowMappingEndToken): self.states.append(self.parse_flow_mapping_key) return self.parse_node() diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index 32f5d7b6..e212a2bd 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -1,16 +1,14 @@ -# This module contains abstractions for the input stream. You don't have to -# looks further, there are no pretty code. - -__all__ = ['Reader', 'ReaderError'] - -from .error import MarkedError, Mark, NON_PRINTABLE +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import codecs -try: - from __builtin__ import unicode -except ImportError: - unicode = str # NOQA +from powerline.lint.markedjson.error import MarkedError, Mark, NON_PRINTABLE +from powerline.lib.unicode import unicode + + +# This module contains abstractions for the input stream. You don't have to +# looks further, there are no pretty code. class ReaderError(MarkedError): diff --git a/powerline/lint/markedjson/resolver.py b/powerline/lint/markedjson/resolver.py index 40b5130d..fa8ceaa4 100644 --- a/powerline/lint/markedjson/resolver.py +++ b/powerline/lint/markedjson/resolver.py @@ -1,10 +1,11 @@ -__all__ = ['BaseResolver', 'Resolver'] - -from .error import MarkedError -from .nodes import * # NOQA +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import re +from powerline.lint.markedjson.error import MarkedError +from powerline.lint.markedjson import nodes + class ResolverError(MarkedError): pass @@ -73,7 +74,7 @@ class BaseResolver: and current_index is None): return if isinstance(index_check, str): - if not (isinstance(current_index, ScalarNode) and index_check == current_index.value): + if not (isinstance(current_index, nodes.ScalarNode) and index_check == current_index.value): return elif isinstance(index_check, int) and not isinstance(index_check, bool): if index_check != current_index: @@ -81,7 +82,7 @@ class BaseResolver: return True def resolve(self, kind, value, implicit, mark=None): - if kind is ScalarNode and implicit[0]: + if kind is nodes.ScalarNode and implicit[0]: if value == '': resolvers = self.yaml_implicit_resolvers.get('', []) else: @@ -97,11 +98,11 @@ class BaseResolver: mark ) return self.DEFAULT_SCALAR_TAG - if kind is ScalarNode: + if kind is nodes.ScalarNode: return self.DEFAULT_SCALAR_TAG - elif kind is SequenceNode: + elif kind is nodes.SequenceNode: return self.DEFAULT_SEQUENCE_TAG - elif kind is MappingNode: + elif kind is nodes.MappingNode: return self.DEFAULT_MAPPING_TAG diff --git a/powerline/lint/markedjson/scanner.py b/powerline/lint/markedjson/scanner.py index e4defc26..b4776f8a 100644 --- a/powerline/lint/markedjson/scanner.py +++ b/powerline/lint/markedjson/scanner.py @@ -1,3 +1,11 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lint.markedjson.error import MarkedError +from powerline.lint.markedjson import tokens +from powerline.lib.unicode import unicode + + # Scanner produces tokens of the following types: # STREAM-START # STREAM-END @@ -14,22 +22,11 @@ # # Read comments in the Scanner code for more details. -__all__ = ['Scanner', 'ScannerError'] - -from .error import MarkedError -from .tokens import * # NOQA - class ScannerError(MarkedError): pass -try: - from __builtin__ import unicode -except ImportError: - unicode = str # NOQA - - class SimpleKey: # See below simple keys treatment. def __init__(self, token_number, index, line, column, mark): @@ -241,8 +238,7 @@ class Scanner: mark = self.get_mark() # Add STREAM-START. - self.tokens.append(StreamStartToken(mark, mark, - encoding=self.encoding)) + self.tokens.append(tokens.StreamStartToken(mark, mark, encoding=self.encoding)) def fetch_stream_end(self): # Reset simple keys. @@ -254,19 +250,18 @@ class Scanner: mark = self.get_mark() # Add STREAM-END. - self.tokens.append(StreamEndToken(mark, mark)) + self.tokens.append(tokens.StreamEndToken(mark, mark)) # The steam is finished. self.done = True def fetch_flow_sequence_start(self): - self.fetch_flow_collection_start(FlowSequenceStartToken) + self.fetch_flow_collection_start(tokens.FlowSequenceStartToken) def fetch_flow_mapping_start(self): - self.fetch_flow_collection_start(FlowMappingStartToken) + self.fetch_flow_collection_start(tokens.FlowMappingStartToken) def fetch_flow_collection_start(self, TokenClass): - # '[' and '{' may start a simple key. self.save_possible_simple_key() @@ -283,13 +278,12 @@ class Scanner: self.tokens.append(TokenClass(start_mark, end_mark)) def fetch_flow_sequence_end(self): - self.fetch_flow_collection_end(FlowSequenceEndToken) + self.fetch_flow_collection_end(tokens.FlowSequenceEndToken) def fetch_flow_mapping_end(self): - self.fetch_flow_collection_end(FlowMappingEndToken) + self.fetch_flow_collection_end(tokens.FlowMappingEndToken) def fetch_flow_collection_end(self, TokenClass): - # Reset possible simple key on the current level. self.remove_possible_simple_key() @@ -312,7 +306,7 @@ class Scanner: # Add KEY. key = self.possible_simple_keys[self.flow_level] del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number - self.tokens_taken, KeyToken(key.mark, key.mark)) + self.tokens.insert(key.token_number - self.tokens_taken, tokens.KeyToken(key.mark, key.mark)) # There cannot be two simple keys one after another. self.allow_simple_key = False @@ -321,10 +315,9 @@ class Scanner: start_mark = self.get_mark() self.forward() end_mark = self.get_mark() - self.tokens.append(ValueToken(start_mark, end_mark)) + self.tokens.append(tokens.ValueToken(start_mark, end_mark)) def fetch_flow_entry(self): - # Simple keys are allowed after ','. self.allow_simple_key = True @@ -335,7 +328,7 @@ class Scanner: start_mark = self.get_mark() self.forward() end_mark = self.get_mark() - self.tokens.append(FlowEntryToken(start_mark, end_mark)) + self.tokens.append(tokens.FlowEntryToken(start_mark, end_mark)) def fetch_double(self): # A flow scalar could be a simple key. @@ -385,7 +378,7 @@ class Scanner: chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) self.forward() end_mark = self.get_mark() - return ScalarToken(unicode().join(chunks), False, start_mark, end_mark, '"') + return tokens.ScalarToken(unicode().join(chunks), False, start_mark, end_mark, '"') ESCAPE_REPLACEMENTS = { 'b': '\x08', @@ -480,4 +473,4 @@ class Scanner: chunks.append(self.prefix(length)) self.forward(length) end_mark = self.get_mark() - return ScalarToken(''.join(chunks), True, start_mark, end_mark) + return tokens.ScalarToken(''.join(chunks), True, start_mark, end_mark) diff --git a/powerline/lint/markedjson/tokens.py b/powerline/lint/markedjson/tokens.py index 15b1836c..6fa8bf18 100644 --- a/powerline/lint/markedjson/tokens.py +++ b/powerline/lint/markedjson/tokens.py @@ -1,3 +1,7 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + class Token(object): def __init__(self, start_mark, end_mark): self.start_mark = start_mark diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index b8d38ecb..c40743d1 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -1,15 +1,14 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from __future__ import unicode_literals, absolute_import, division +from powerline.theme import requires_segment_info +from powerline.bindings.vim import (current_tabpage, list_tabpages, vim_getbufoption) try: import vim except ImportError: vim = {} # NOQA -from powerline.theme import requires_segment_info -from powerline.bindings.vim import (current_tabpage, list_tabpages, vim_getbufoption) - def tabpage_updated_segment_info(segment_info, tabpage, mode): segment_info = segment_info.copy() diff --git a/powerline/matchers/__init__.py b/powerline/matchers/__init__.py index 3ad9513f..b2b9f102 100644 --- a/powerline/matchers/__init__.py +++ b/powerline/matchers/__init__.py @@ -1,2 +1,6 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from pkgutil import extend_path + + __path__ = extend_path(__path__, __name__) diff --git a/powerline/matchers/vim/__init__.py b/powerline/matchers/vim/__init__.py index 1a16fb49..d7e9b48d 100644 --- a/powerline/matchers/vim/__init__.py +++ b/powerline/matchers/vim/__init__.py @@ -1,8 +1,8 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import os + from powerline.bindings.vim import vim_getbufoption diff --git a/powerline/matchers/vim/plugin/__init__.py b/powerline/matchers/vim/plugin/__init__.py index 3ad9513f..b2b9f102 100644 --- a/powerline/matchers/vim/plugin/__init__.py +++ b/powerline/matchers/vim/plugin/__init__.py @@ -1,2 +1,6 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from pkgutil import extend_path + + __path__ = extend_path(__path__, __name__) diff --git a/powerline/matchers/vim/plugin/ctrlp.py b/powerline/matchers/vim/plugin/ctrlp.py index d6754798..6ba1c76d 100644 --- a/powerline/matchers/vim/plugin/ctrlp.py +++ b/powerline/matchers/vim/plugin/ctrlp.py @@ -1,22 +1,28 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import os + try: import vim - - vim.command('''function! Powerline_plugin_ctrlp_main(...) +except ImportError: + vim = object() # NOQA +else: + vim.command(''' + function! Powerline_plugin_ctrlp_main(...) let b:powerline_ctrlp_type = 'main' let b:powerline_ctrlp_args = a:000 endfunction''') - vim.command('''function! Powerline_plugin_ctrlp_prog(...) + vim.command(''' + function! Powerline_plugin_ctrlp_prog(...) let b:powerline_ctrlp_type = 'prog' let b:powerline_ctrlp_args = a:000 endfunction''') - vim.command('''let g:ctrlp_status_func = { 'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog' }''') -except ImportError: - vim = object() # NOQA + vim.command(''' + let g:ctrlp_status_func = {'main': 'Powerline_plugin_ctrlp_main', 'prog': 'Powerline_plugin_ctrlp_prog'} + ''') def ctrlp(matcher_info): diff --git a/powerline/matchers/vim/plugin/gundo.py b/powerline/matchers/vim/plugin/gundo.py index 1992f711..356ffd65 100644 --- a/powerline/matchers/vim/plugin/gundo.py +++ b/powerline/matchers/vim/plugin/gundo.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import os diff --git a/powerline/matchers/vim/plugin/nerdtree.py b/powerline/matchers/vim/plugin/nerdtree.py index a9f8f0bc..aeb2c24c 100644 --- a/powerline/matchers/vim/plugin/nerdtree.py +++ b/powerline/matchers/vim/plugin/nerdtree.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import re diff --git a/powerline/renderer.py b/powerline/renderer.py index a1408b5e..2e97e16f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import os @@ -6,16 +7,10 @@ from unicodedata import east_asian_width, combining from itertools import chain from powerline.theme import Theme +from powerline.lib.unicode import unichr -try: - NBSP = unicode(' ', 'utf-8') -except NameError: - NBSP = ' ' -try: - from __builtin__ import unichr as chr -except ImportError: - pass +NBSP = ' ' def construct_returned_value(rendered_highlighted, segments, width, output_raw, output_width): @@ -80,7 +75,7 @@ class Renderer(object): See documentation of ``unicode.translate`` for details. ''' - np_character_translations = dict(((i, '^' + chr(i + 0x40)) for i in range(0x20))) + np_character_translations = dict(((i, '^' + unichr(i + 0x40)) for i in range(0x20))) '''Non-printable character translations These are used to transform characters in range 0x00—0x1F into ``^@``, diff --git a/powerline/renderers/i3bar.py b/powerline/renderers/i3bar.py index 2e0ef9b3..223458c2 100644 --- a/powerline/renderers/i3bar.py +++ b/powerline/renderers/i3bar.py @@ -1,7 +1,9 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import json from powerline.renderer import Renderer -import json class I3barRenderer(Renderer): diff --git a/powerline/renderers/ipython/__init__.py b/powerline/renderers/ipython/__init__.py index 1fce961e..985e6c34 100644 --- a/powerline/renderers/ipython/__init__.py +++ b/powerline/renderers/ipython/__init__.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderers.shell import ShellRenderer from powerline.theme import Theme diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index ea1fe15e..02511ab5 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -1,10 +1,11 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from xml.sax.saxutils import escape as _escape from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE -from xml.sax.saxutils import escape as _escape - class PangoMarkupRenderer(Renderer): '''Powerline Pango markup segment renderer.''' diff --git a/powerline/renderers/shell/__init__.py b/powerline/renderers/shell/__init__.py index 70d9ac6a..b767fc26 100644 --- a/powerline/renderers/shell/__init__.py +++ b/powerline/renderers/shell/__init__.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals, division, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderer import Renderer from powerline.theme import Theme diff --git a/powerline/renderers/shell/bash.py b/powerline/renderers/shell/bash.py index c37a2cf9..783bd501 100644 --- a/powerline/renderers/shell/bash.py +++ b/powerline/renderers/shell/bash.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/shell/ksh.py b/powerline/renderers/shell/ksh.py index 3b4387a4..0828e573 100644 --- a/powerline/renderers/shell/ksh.py +++ b/powerline/renderers/shell/ksh.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/shell/tcsh.py b/powerline/renderers/shell/tcsh.py index 261b081a..5d138b93 100644 --- a/powerline/renderers/shell/tcsh.py +++ b/powerline/renderers/shell/tcsh.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderers.shell.zsh import ZshPromptRenderer diff --git a/powerline/renderers/shell/zsh.py b/powerline/renderers/shell/zsh.py index 47e76cc3..a2315124 100644 --- a/powerline/renderers/shell/zsh.py +++ b/powerline/renderers/shell/zsh.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderers.shell import ShellRenderer diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 5daccbbb..5c02f29b 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import, unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 89f54630..6ea2a30b 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -1,20 +1,15 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) -from __future__ import absolute_import, unicode_literals +import sys + +import vim from powerline.bindings.vim import vim_get_func, vim_getoption, environ, current_tabpage from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme - -import vim -import sys - - -try: - from __builtin__ import unichr as chr -except ImportError: - pass +from powerline.lib.unicode import unichr vim_mode = vim_get_func('mode', rettype=str) @@ -23,8 +18,8 @@ if int(vim.eval('v:version')) >= 702: vim_mode = lambda: _vim_mode(1) mode_translations = { - chr(ord('V') - 0x40): '^V', - chr(ord('S') - 0x40): '^S', + unichr(ord('V') - 0x40): '^V', + unichr(ord('S') - 0x40): '^S', } diff --git a/powerline/segment.py b/powerline/segment.py index 4dd283e4..c098756a 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals, division, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.lib.watcher import create_file_watcher diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py index 0a33bdbc..681a7289 100644 --- a/powerline/segments/__init__.py +++ b/powerline/segments/__init__.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys diff --git a/powerline/segments/common.py b/powerline/segments/common.py index ee6e0b75..047bada1 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import unicode_literals, absolute_import, division +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import sys @@ -9,6 +8,7 @@ import socket from datetime import datetime from multiprocessing import cpu_count as _cpu_count +from collections import namedtuple from powerline.lib import add_divider_highlight_group from powerline.lib.shell import asrun, run_cmd @@ -20,7 +20,6 @@ from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.unicode import u from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.segments import Segment, with_docstring -from collections import namedtuple cpu_count = None diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index 69752dff..1a290c78 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import i3 diff --git a/powerline/segments/ipython.py b/powerline/segments/ipython.py index 9a29ea8e..622e0a50 100644 --- a/powerline/segments/ipython.py +++ b/powerline/segments/ipython.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.theme import requires_segment_info diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index bfd7c9a3..e7e71f19 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.theme import requires_segment_info from powerline.segments import with_docstring diff --git a/powerline/segments/tmux.py b/powerline/segments/tmux.py index 3a027aa4..b26824dc 100644 --- a/powerline/segments/tmux.py +++ b/powerline/segments/tmux.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals, division, print_function +from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.bindings.tmux import get_tmux_output diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index c973af1d..f4246a14 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -1,17 +1,16 @@ # vim:fileencoding=utf-8:noet - -from __future__ import unicode_literals, absolute_import, division +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import re +from collections import defaultdict + try: import vim except ImportError: vim = {} # NOQA -from collections import defaultdict - from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar, register_buffer_cache, current_tabpage, diff --git a/powerline/segments/vim/plugin/__init__.py b/powerline/segments/vim/plugin/__init__.py index 3ad9513f..b2b9f102 100644 --- a/powerline/segments/vim/plugin/__init__.py +++ b/powerline/segments/vim/plugin/__init__.py @@ -1,2 +1,6 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) from pkgutil import extend_path + + __path__ = extend_path(__path__, __name__) diff --git a/powerline/segments/vim/plugin/ctrlp.py b/powerline/segments/vim/plugin/ctrlp.py index f54f8bac..eb658b56 100644 --- a/powerline/segments/vim/plugin/ctrlp.py +++ b/powerline/segments/vim/plugin/ctrlp.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) try: import vim diff --git a/powerline/segments/vim/plugin/nerdtree.py b/powerline/segments/vim/plugin/nerdtree.py index a8e6ad85..6ea9a04b 100644 --- a/powerline/segments/vim/plugin/nerdtree.py +++ b/powerline/segments/vim/plugin/nerdtree.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) try: import vim diff --git a/powerline/segments/vim/plugin/syntastic.py b/powerline/segments/vim/plugin/syntastic.py index 71a411fa..f3da22c3 100644 --- a/powerline/segments/vim/plugin/syntastic.py +++ b/powerline/segments/vim/plugin/syntastic.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) try: import vim diff --git a/powerline/segments/vim/plugin/tagbar.py b/powerline/segments/vim/plugin/tagbar.py index 24e8db52..dc92d88d 100644 --- a/powerline/segments/vim/plugin/tagbar.py +++ b/powerline/segments/vim/plugin/tagbar.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) try: import vim diff --git a/powerline/shell.py b/powerline/shell.py index d1f717e6..047f21af 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -1,4 +1,6 @@ # vim:fileencoding=utf-8:noet +# WARNING: using unicode_literals causes errors in argparse +from __future__ import (division, absolute_import, print_function) from powerline import Powerline from powerline.lib import mergedicts, parsedotval diff --git a/powerline/theme.py b/powerline/theme.py index 2558ca0e..9fa90e54 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,9 +1,10 @@ # vim:fileencoding=utf-8:noet -from __future__ import division +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import itertools from powerline.segment import gen_segment_getter, process_segment from powerline.lib.unicode import u -import itertools def requires_segment_info(func): diff --git a/powerline/vim.py b/powerline/vim.py index 56159f00..5f83e54e 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import absolute_import +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys diff --git a/scripts/powerline-config b/scripts/powerline-config index 0d82eba5..b89e4e39 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -1,7 +1,10 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet + '''Script used to obtain powerline configuration''' +from __future__ import (unicode_literals, division, absolute_import, print_function) + import argparse try: diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 956710d9..5ac069d8 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -6,6 +6,7 @@ import socket import os import errno import sys + from argparse import ArgumentParser from select import select from signal import signal, SIGTERM diff --git a/scripts/powerline-lint b/scripts/powerline-lint index 5a9b1285..cfebad3e 100755 --- a/scripts/powerline-lint +++ b/scripts/powerline-lint @@ -1,10 +1,15 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet + '''Powerline configuration checker.''' + +from __future__ import (unicode_literals, division, absolute_import, print_function) + import argparse -from powerline.lint import check import sys +from powerline.lint import check + parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-p', '--config_path', action='append', metavar='PATH') diff --git a/scripts/powerline-render b/scripts/powerline-render index 52f85a13..cf422b04 100755 --- a/scripts/powerline-render +++ b/scripts/powerline-render @@ -1,6 +1,10 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet + '''Powerline prompt and statusline script.''' + +from __future__ import (unicode_literals, division, absolute_import, print_function) + import sys import os diff --git a/setup.py b/setup.py index d397e03e..c34d8424 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) + import os import sys import subprocess @@ -8,6 +9,7 @@ import logging from setuptools import setup, find_packages + CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) try: README = open(os.path.join(CURRENT_DIR, 'README.rst'), 'rb').read().decode('utf-8') diff --git a/tests/__init__.py b/tests/__init__.py index 2492b045..9a961acd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,8 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + import sys + if sys.version_info < (2, 7): from unittest2 import TestCase, main # NOQA from unittest2.case import SkipTest # NOQA diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 5399df1a..80af8097 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,4 +1,6 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + import imp import sys diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index 55f206d1..7161ea28 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -1,13 +1,18 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + from threading import Lock -from powerline.renderer import Renderer -from powerline.lib.config import ConfigLoader -from powerline import Powerline -from tests.lib import Args, replace_attr from copy import deepcopy from time import sleep from functools import wraps -import os + +from powerline.renderer import Renderer +from powerline.lib.config import ConfigLoader +from powerline import Powerline + +from tests.lib import Args, replace_attr class TestHelpers(object): diff --git a/tests/lib/fsconfig.py b/tests/lib/fsconfig.py index db80488b..a1066f65 100644 --- a/tests/lib/fsconfig.py +++ b/tests/lib/fsconfig.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import, division +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import json diff --git a/tests/matchers.py b/tests/matchers.py index 3937f2f1..e905de32 100644 --- a/tests/matchers.py +++ b/tests/matchers.py @@ -1,5 +1,4 @@ # vim:fileencoding=utf-8:noet - from __future__ import (unicode_literals, division, absolute_import, print_function) diff --git a/tests/path/vim.py b/tests/path/vim.py index 899678e5..1de56240 100644 --- a/tests/path/vim.py +++ b/tests/path/vim.py @@ -1,4 +1,6 @@ # vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + from tests import vim diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 5af43eee..f28c3896 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -2,15 +2,19 @@ '''Tests for shell.py parser''' +from __future__ import (unicode_literals, division, absolute_import, print_function) -from powerline.shell import get_argparser, finish_args -from tests import TestCase -from tests.lib import replace_attr import sys + if sys.version_info < (3,): from io import BytesIO as StrIO else: - from io import StringIO as StrIO # NOQA + from io import StringIO as StrIO + +from powerline.shell import get_argparser, finish_args + +from tests import TestCase +from tests.lib import replace_attr class TestParser(TestCase): diff --git a/tests/test_config_merging.py b/tests/test_config_merging.py index 5249a0a3..ffdc4b51 100644 --- a/tests/test_config_merging.py +++ b/tests/test_config_merging.py @@ -1,15 +1,18 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) -from powerline import Powerline -from tests import TestCase -from tests.lib.config_mock import select_renderer -from shutil import rmtree import os import json -from powerline.lib import mergedicts_copy as mdc + from subprocess import check_call from operator import add +from shutil import rmtree + +from powerline.lib import mergedicts_copy as mdc +from powerline import Powerline + +from tests import TestCase +from tests.lib.config_mock import select_renderer CONFIG_DIR = 'tests/config' diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index eb7e1cb9..3bcfc9e2 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) from time import sleep from copy import deepcopy diff --git a/tests/test_configuration.py b/tests/test_configuration.py index f6cbc975..c0dbf8e5 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import, division +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import os diff --git a/tests/test_lib.py b/tests/test_lib.py index 16f4c760..ef8dc737 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import division +from __future__ import (unicode_literals, division, absolute_import, print_function) import threading import os @@ -385,7 +385,7 @@ class TestLib(TestCase): return str(kwargs) func = add_divider_highlight_group('hl_group')(decorated_function_name) self.assertEqual(func.__name__, 'decorated_function_name') - self.assertEqual(func(kw={}), [{'contents': repr({'kw': {}}), 'divider_highlight_group': 'hl_group'}]) + self.assertEqual(func(kw={}), [{'contents': repr({str('kw'): {}}), 'divider_highlight_group': 'hl_group'}]) def test_humanize_bytes(self): self.assertEqual(humanize_bytes(0), '0 B') diff --git a/tests/test_lib_config.py b/tests/test_lib_config.py index 5c941d72..2b3db707 100644 --- a/tests/test_lib_config.py +++ b/tests/test_lib_config.py @@ -1,9 +1,10 @@ # vim:fileencoding=utf-8:noet -from __future__ import unicode_literals, absolute_import, division +from __future__ import (unicode_literals, division, absolute_import, print_function) import os from powerline.lib.config import ConfigLoader + from tests import TestCase from tests.lib.fsconfig import FSTree diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index e6329d1f..214748ea 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -2,11 +2,14 @@ '''Dynamic configuration files tests.''' +from __future__ import (unicode_literals, division, absolute_import, print_function) -import tests.vim as vim_module import sys import os import json + +import tests.vim as vim_module + from tests.lib import Args, urllib_read, replace_attr from tests import TestCase diff --git a/tests/test_segments.py b/tests/test_segments.py index 0f143bda..641d5f81 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -1,6 +1,5 @@ # vim:fileencoding=utf-8:noet - -from __future__ import unicode_literals +from __future__ import (unicode_literals, division, absolute_import, print_function) import sys import os diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index ddacb8b1..71e79f9e 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -1,6 +1,6 @@ #!/usr/bin/env python - -from __future__ import unicode_literals +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) import os import socket diff --git a/tests/test_watcher.py b/tests/test_watcher.py index 323a39ab..13aecbea 100644 --- a/tests/test_watcher.py +++ b/tests/test_watcher.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals, print_function, division +from __future__ import (unicode_literals, division, absolute_import, print_function) import shutil import os diff --git a/tools/colors_find.py b/tools/colors_find.py index cdc01e10..cf66ef91 100755 --- a/tools/colors_find.py +++ b/tools/colors_find.py @@ -1,7 +1,10 @@ #!/usr/bin/env python -from __future__ import division, print_function +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + import sys import os + from colormath.color_objects import sRGBColor, LabColor from colormath.color_conversions import convert_color from colormath.color_diff import delta_e_cie2000 diff --git a/tools/generate_gradients.py b/tools/generate_gradients.py index b3c94615..290e75e4 100755 --- a/tools/generate_gradients.py +++ b/tools/generate_gradients.py @@ -1,21 +1,22 @@ #!/usr/bin/env python # vim:fileencoding=utf-8:noet + '''Gradients generator ''' -from __future__ import division, unicode_literals + +from __future__ import (unicode_literals, division, absolute_import, print_function) + import sys import json -from powerline.colorscheme import cterm_to_hex -from itertools import groupby import argparse + +from itertools import groupby + from colormath.color_objects import sRGBColor, LabColor from colormath.color_conversions import convert_color from colormath.color_diff import delta_e_cie2000 -try: - from __builtin__ import unicode -except ImportError: - unicode = str # NOQA +from powerline.colorscheme import cterm_to_hex def num2(s): From 8d00ba781cb258a4f5fae249915f8b2417f9fea4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 31 Aug 2014 23:01:40 +0400 Subject: [PATCH 1430/1472] Remove unneeded # NOQA comments It appears that something has changed and it no longer complains about double declaration in else or except blocks. --- powerline/bindings/vim/__init__.py | 18 +++++++++--------- powerline/lib/monotonic.py | 10 +++++----- powerline/lib/shell.py | 2 +- powerline/lib/unicode.py | 2 +- powerline/lib/url.py | 4 ++-- powerline/listers/vim.py | 2 +- powerline/matchers/vim/plugin/ctrlp.py | 2 +- powerline/renderers/vim.py | 2 +- powerline/segments/__init__.py | 2 +- powerline/segments/common.py | 18 +++++++++--------- powerline/segments/vim/__init__.py | 2 +- powerline/segments/vim/plugin/ctrlp.py | 2 +- powerline/segments/vim/plugin/nerdtree.py | 2 +- powerline/segments/vim/plugin/syntastic.py | 2 +- powerline/segments/vim/plugin/tagbar.py | 2 +- powerline/vim.py | 2 +- scripts/powerline-config | 2 +- scripts/powerline-render | 2 +- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 277c37ca..4b38897a 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -76,14 +76,14 @@ else: _vim_exists = vim_get_func('exists', rettype=int) - def vim_getvar(varname): # NOQA + def vim_getvar(varname): varname = 'g:' + varname if _vim_exists(varname): return vim.eval(varname) else: raise KeyError(varname) - def bufvar_exists(buffer, varname): # NOQA + def bufvar_exists(buffer, varname): if not buffer or buffer.number == vim.current.buffer.number: return int(vim.eval('exists("b:{0}")'.format(varname))) else: @@ -91,7 +91,7 @@ else: 'has_key(getbufvar({0}, ""), {1})'.format(buffer.number, varname) )) - def vim_getwinvar(segment_info, varname): # NOQA + def vim_getwinvar(segment_info, varname): result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname)) if result == '': if not int(vim.eval('has_key(getwinvar({0}, ""), "{1}")'.format(segment_info['winnr'], varname))): @@ -125,13 +125,13 @@ if hasattr(vim, 'options'): def vim_setoption(option, value): vim.options[str(option)] = value else: - def vim_getbufoption(info, option): # NOQA + def vim_getbufoption(info, option): return getbufvar(info['bufnr'], '&' + option) - def vim_getoption(option): # NOQA + def vim_getoption(option): return vim.eval('&g:' + option) - def vim_setoption(option, value): # NOQA + def vim_setoption(option, value): vim.command('let &g:{option} = {value}'.format( option=option, value=json.encode(value))) @@ -208,10 +208,10 @@ else: def _last_tab_nr(): return int(vim.eval('tabpagenr("$")')) - def current_tabpage(): # NOQA + def current_tabpage(): return Tabpage(int(vim.eval('tabpagenr()'))) - def list_tabpages(): # NOQA + def list_tabpages(): return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)] class TabBufSegmentInfo(dict): @@ -261,7 +261,7 @@ if sys.version_info < (3,): else: vim_bufname = vim_get_func('bufname') - def buffer_name(buf): # NOQA + def buffer_name(buf): try: name = buf.name except UnicodeDecodeError: diff --git a/powerline/lib/monotonic.py b/powerline/lib/monotonic.py index 5f9a80f9..cd7c4141 100644 --- a/powerline/lib/monotonic.py +++ b/powerline/lib/monotonic.py @@ -9,12 +9,12 @@ try: # >={kernel}-sources-2.6.28 from time import CLOCK_MONOTONIC_RAW as CLOCK_ID except ImportError: - from time import CLOCK_MONOTONIC as CLOCK_ID # NOQA + from time import CLOCK_MONOTONIC as CLOCK_ID monotonic = lambda: clock_gettime(CLOCK_ID) except ImportError: # >=python-3.3 - from time import monotonic # NOQA + from time import monotonic except ImportError: import ctypes import sys @@ -25,7 +25,7 @@ except ImportError: GetTickCount64 = ctypes.windll.kernel32.GetTickCount64 GetTickCount64.restype = ctypes.c_ulonglong - def monotonic(): # NOQA + def monotonic(): return GetTickCount64() / 1000 elif sys.platform == 'darwin': @@ -61,7 +61,7 @@ except ImportError: timebase = mach_timebase_info() factor = timebase[0] / timebase[1] * 1e-9 - def monotonic(): # NOQA + def monotonic(): return mach_absolute_time() * factor else: # linux only (no librt on OS X) @@ -90,7 +90,7 @@ except ImportError: else: raise OSError - def monotonic(): # NOQA + def monotonic(): if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) != 0: errno_ = ctypes.get_errno() raise OSError(errno_, os.strerror(errno_)) diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index 96b87128..3359c843 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -68,7 +68,7 @@ try: except ImportError: # shutil.which was added in python-3.3. Here is what was added: # Lib/shutil.py, commit 5abe28a9c8fe701ba19b1db5190863384e96c798 - def which(cmd, mode=os.F_OK | os.X_OK, path=None): # NOQA + def which(cmd, mode=os.F_OK | os.X_OK, path=None): """Given a command, mode, and a PATH string, return the path which conforms to the given mode on the PATH, or None if there is no such file. diff --git a/powerline/lib/unicode.py b/powerline/lib/unicode.py index cdaab65f..cea8b05f 100644 --- a/powerline/lib/unicode.py +++ b/powerline/lib/unicode.py @@ -7,7 +7,7 @@ from locale import getpreferredencoding try: from __builtin__ import unicode except ImportError: - unicode = str # NOQA + unicode = str try: diff --git a/powerline/lib/url.py b/powerline/lib/url.py index 4f1cfd8c..f25919cd 100644 --- a/powerline/lib/url.py +++ b/powerline/lib/url.py @@ -2,8 +2,8 @@ from __future__ import (unicode_literals, division, absolute_import, print_function) try: - from urllib.error import HTTPError - from urllib.request import urlopen + from urllib.error import HTTPError # NOQA + from urllib.request import urlopen # NOQA from urllib.parse import urlencode as urllib_urlencode # NOQA except ImportError: from urllib2 import urlopen, HTTPError # NOQA diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index c40743d1..bd24e07a 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -7,7 +7,7 @@ from powerline.bindings.vim import (current_tabpage, list_tabpages, vim_getbufop try: import vim except ImportError: - vim = {} # NOQA + vim = {} def tabpage_updated_segment_info(segment_info, tabpage, mode): diff --git a/powerline/matchers/vim/plugin/ctrlp.py b/powerline/matchers/vim/plugin/ctrlp.py index 6ba1c76d..f2b05980 100644 --- a/powerline/matchers/vim/plugin/ctrlp.py +++ b/powerline/matchers/vim/plugin/ctrlp.py @@ -6,7 +6,7 @@ import os try: import vim except ImportError: - vim = object() # NOQA + vim = object() else: vim.command(''' function! Powerline_plugin_ctrlp_main(...) diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 6ea2a30b..4d97fe2f 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -76,7 +76,7 @@ class VimRenderer(Renderer): # renderer return vim.strwidth(string.encode('utf-8')) else: - @staticmethod # NOQA + @staticmethod def strwidth(string): return vim.strwidth(string) diff --git a/powerline/segments/__init__.py b/powerline/segments/__init__.py index 681a7289..fa09e58a 100644 --- a/powerline/segments/__init__.py +++ b/powerline/segments/__init__.py @@ -26,7 +26,7 @@ class Segment(object): def argspecobjs(self): yield '__call__', self.__call__ else: - def argspecobjs(self): # NOQA + def argspecobjs(self): yield '__call__', self argspecobjs.__doc__ = ( diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 047bada1..144e9596 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -675,23 +675,23 @@ try: 'highlight_group': ['cpu_load_percent_gradient', 'cpu_load_percent'], }] except ImportError: - def _get_bytes(interface): # NOQA + def _get_bytes(interface): with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: rx = int(file_obj.read()) with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: tx = int(file_obj.read()) return (rx, tx) - def _get_interfaces(): # NOQA + def _get_interfaces(): for interface in os.listdir('/sys/class/net'): x = _get_bytes(interface) if x is not None: yield interface, x[0], x[1] - def _get_user(segment_info): # NOQA + def _get_user(segment_info): return segment_info['environ'].get('USER', None) - class CPULoadPercentSegment(ThreadedSegment): # NOQA + class CPULoadPercentSegment(ThreadedSegment): interval = 1 @staticmethod @@ -763,12 +763,12 @@ if os.path.exists('/proc/uptime'): elif 'psutil' in globals(): from time import time - def _get_uptime(): # NOQA + def _get_uptime(): # psutil.BOOT_TIME is not subject to clock adjustments, but time() is. # Thus it is a fallback to /proc/uptime reading and not the reverse. return int(time() - psutil.BOOT_TIME) else: - def _get_uptime(): # NOQA + def _get_uptime(): raise NotImplementedError @@ -1198,14 +1198,14 @@ class NowPlayingSegment(Segment): } try: - __import__('dbus') # NOQA + __import__('dbus') except ImportError: if sys.platform.startswith('darwin'): player_spotify = player_spotify_apple_script else: - player_spotify = player_spotify_dbus # NOQA + player_spotify = player_spotify_dbus else: - player_spotify = player_spotify_dbus # NOQA + player_spotify = player_spotify_dbus def player_rhythmbox(self, pl): now_playing = run_cmd(pl, ['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td']) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index f4246a14..4200b12b 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -9,7 +9,7 @@ from collections import defaultdict try: import vim except ImportError: - vim = {} # NOQA + vim = {} from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar, diff --git a/powerline/segments/vim/plugin/ctrlp.py b/powerline/segments/vim/plugin/ctrlp.py index eb658b56..80cabb76 100644 --- a/powerline/segments/vim/plugin/ctrlp.py +++ b/powerline/segments/vim/plugin/ctrlp.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct try: import vim except ImportError: - vim = object() # NOQA + vim = object() from powerline.bindings.vim import getbufvar from powerline.segments.vim import window_cached diff --git a/powerline/segments/vim/plugin/nerdtree.py b/powerline/segments/vim/plugin/nerdtree.py index 6ea9a04b..79aba991 100644 --- a/powerline/segments/vim/plugin/nerdtree.py +++ b/powerline/segments/vim/plugin/nerdtree.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct try: import vim except ImportError: - vim = object() # NOQA + vim = object() from powerline.bindings.vim import bufvar_exists from powerline.segments.vim import window_cached diff --git a/powerline/segments/vim/plugin/syntastic.py b/powerline/segments/vim/plugin/syntastic.py index f3da22c3..61324d45 100644 --- a/powerline/segments/vim/plugin/syntastic.py +++ b/powerline/segments/vim/plugin/syntastic.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct try: import vim except ImportError: - vim = object() # NOQA + vim = object() from powerline.segments.vim import window_cached diff --git a/powerline/segments/vim/plugin/tagbar.py b/powerline/segments/vim/plugin/tagbar.py index dc92d88d..7421208e 100644 --- a/powerline/segments/vim/plugin/tagbar.py +++ b/powerline/segments/vim/plugin/tagbar.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct try: import vim except ImportError: - vim = object() # NOQA + vim = object() from powerline.segments.vim import window_cached diff --git a/powerline/vim.py b/powerline/vim.py index 5f83e54e..a443b74c 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -214,7 +214,7 @@ class VimPowerline(Powerline): _vim_getwinvar = staticmethod(vim_get_func('getwinvar')) _vim_setwinvar = staticmethod(vim_get_func('setwinvar')) - def win_idx(self, window_id): # NOQA + def win_idx(self, window_id): r = None for winnr, window in zip(count(1), vim.windows): curwindow_id = self._vim_getwinvar(winnr, 'powerline_window_id') diff --git a/scripts/powerline-config b/scripts/powerline-config index b89e4e39..d64939e4 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -13,7 +13,7 @@ except ImportError: import sys import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) - import powerline.bindings.config as config # NOQA + import powerline.bindings.config as config TMUX_ACTIONS = { diff --git a/scripts/powerline-render b/scripts/powerline-render index cf422b04..048c34ce 100755 --- a/scripts/powerline-render +++ b/scripts/powerline-render @@ -14,7 +14,7 @@ try: from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output except ImportError: sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) - from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output # NOQA + from powerline.shell import ShellPowerline, get_argparser, finish_args, write_output if sys.version_info < (3,): From 986a69481ff4009b0a4879f2b7a59c7aa932a139 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Sep 2014 21:49:23 +0400 Subject: [PATCH 1431/1472] Update documentation --- docs/source/configuration/reference.rst | 1 + docs/source/installation.rst | 2 ++ powerline/lib/watcher/__init__.py | 17 +++++++++-------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index c3096b9c..915ff020 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -45,6 +45,7 @@ Common configuration is a subdictionary that is a value of ``common`` key in auto Selects most performant watcher. inotify Select inotify watcher. Linux only. stat Select stat-based polling watcher. + uv Select libuv-based watcher. ======= =================================== Default is ``auto``. diff --git a/docs/source/installation.rst b/docs/source/installation.rst index ab4fe6e8..f31e9264 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -19,6 +19,8 @@ Generic requirements repositories. * ``bzr`` python package (note: *not* standalone executable). Required to work with bazaar repositories. +* ``pyuv`` python package. Required for :ref:`libuv-based watcher + ` to work. * ``i3-py``, `available on github `_. Required for i3wm bindings and segments. diff --git a/powerline/lib/watcher/__init__.py b/powerline/lib/watcher/__init__.py index f1cb5d79..4fe98968 100644 --- a/powerline/lib/watcher/__init__.py +++ b/powerline/lib/watcher/__init__.py @@ -18,16 +18,17 @@ def create_file_watcher(pl, watcher_type='auto', expire_time=10): Use ``.unwatch()`` method of the returned object to stop watching the file. - Uses inotify if available, otherwise tracks mtimes. expire_time is the - number of minutes after the last query for a given path for the inotify - watch for that path to be automatically removed. This conserves kernel - resources. + Uses inotify if available, then pyuv, otherwise tracks mtimes. expire_time + is the number of minutes after the last query for a given path for the + inotify watch for that path to be automatically removed. This conserves + kernel resources. :param PowerlineLogger pl: Logger. - :param str watcher_type: - One of ``inotify`` (linux only), ``stat``, ``auto``. Determines what - watcher will be used. ``auto`` will use ``inotify`` if available. + :param str watcher_type + One of ``inotify`` (linux only), ``uv``, ``stat``, ``auto``. Determines + what watcher will be used. ``auto`` will use ``inotify`` if available, + then ``libuv`` and then fall back to ``stat``. :param int expire_time: Number of minutes since last ``.__call__()`` before inotify watcher will stop watching given file. @@ -67,7 +68,7 @@ def create_tree_watcher(pl, watcher_type='auto', expire_time=10): Logger. :param str watcher_type: Watcher type. Currently the only supported types are ``inotify`` (linux - only), ``dummy`` and ``auto``. + only), ``uv``, ``dummy`` and ``auto``. :param int expire_time: Number of minutes since last ``.__call__()`` before inotify watcher will stop watching given file. From 6484341222e380201a862dd54e5fdd87e0315c57 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Sep 2014 21:51:46 +0400 Subject: [PATCH 1432/1472] Rename `is_watched` method to `is_watching` --- powerline/lib/vcs/__init__.py | 2 +- powerline/lib/watcher/inotify.py | 2 +- powerline/lib/watcher/stat.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/lib/vcs/__init__.py b/powerline/lib/vcs/__init__.py index 9d8f60a0..96dee683 100644 --- a/powerline/lib/vcs/__init__.py +++ b/powerline/lib/vcs/__init__.py @@ -53,7 +53,7 @@ def get_branch_name(directory, config_file, get_func, create_watcher): with branch_lock: # Check if the repo directory was moved/deleted fw = branch_watcher(create_watcher) - is_watched = fw.is_watched(directory) + is_watched = fw.is_watching(directory) try: changed = fw(directory) except OSError as e: diff --git a/powerline/lib/watcher/inotify.py b/powerline/lib/watcher/inotify.py index ea7edea4..6708b21e 100644 --- a/powerline/lib/watcher/inotify.py +++ b/powerline/lib/watcher/inotify.py @@ -101,7 +101,7 @@ class INotifyFileWatcher(INotify): self.watches[path] = wd self.modified[path] = False - def is_watched(self, path): + def is_watching(self, path): with self.lock: return realpath(path) in self.watches diff --git a/powerline/lib/watcher/stat.py b/powerline/lib/watcher/stat.py index 3ac70c74..0c089716 100644 --- a/powerline/lib/watcher/stat.py +++ b/powerline/lib/watcher/stat.py @@ -23,7 +23,7 @@ class StatFileWatcher(object): with self.lock: self.watches.pop(path, None) - def is_watched(self, path): + def is_watching(self, path): with self.lock: return realpath(path) in self.watches From b638a8e1c9168ddd8c862571296f9072860254b9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Sep 2014 21:58:42 +0400 Subject: [PATCH 1433/1472] Add tests for is_watching method Currently they fail for uv-based watcher. --- tests/test_watcher.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_watcher.py b/tests/test_watcher.py index 13aecbea..956faa7a 100644 --- a/tests/test_watcher.py +++ b/tests/test_watcher.py @@ -141,6 +141,40 @@ class TestFilesystemWatchers(TestCase): tw = create_tree_watcher(get_fallback_logger(), 'uv') return self.do_test_tree_watcher(tw) + def test_inotify_file_watcher_is_watching(self): + try: + w = create_file_watcher(pl=get_fallback_logger(), watcher_type='inotify') + except INotifyError: + raise SkipTest('INotify is not available') + return self.do_test_file_watcher_is_watching(w) + + def test_stat_file_watcher_is_watching(self): + w = create_file_watcher(pl=get_fallback_logger(), watcher_type='stat') + return self.do_test_file_watcher_is_watching(w) + + def test_uv_file_watcher_is_watching(self): + try: + w = create_file_watcher(pl=get_fallback_logger(), watcher_type='uv') + except UvNotFound: + raise SkipTest('Pyuv is not available') + return self.do_test_file_watcher_is_watching(w) + + def do_test_file_watcher_is_watching(self, w): + try: + f1, f2, f3 = map(lambda x: os.path.join(INOTIFY_DIR, 'file%d' % x), (1, 2, 3)) + with open(f1, 'wb'): + with open(f2, 'wb'): + with open(f3, 'wb'): + pass + ne = os.path.join(INOTIFY_DIR, 'notexists') + self.assertRaises(OSError, w, ne) + self.assertTrue(w(f1)) + self.assertFalse(w.is_watching(ne)) + self.assertTrue(w.is_watching(f1)) + self.assertFalse(w.is_watching(f2)) + finally: + clear_dir(INOTIFY_DIR) + old_cwd = None From c564b645304a4144c37264cf3afa24d8c362661f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Sep 2014 22:01:50 +0400 Subject: [PATCH 1434/1472] Add `is_watching` method to UvWatcher class Fixes #1052 --- powerline/lib/watcher/uv.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/lib/watcher/uv.py b/powerline/lib/watcher/uv.py index 26e9dbfe..88b64366 100644 --- a/powerline/lib/watcher/uv.py +++ b/powerline/lib/watcher/uv.py @@ -87,6 +87,10 @@ class UvWatcher(object): return watch.close(partial(self._stopped_watching, path)) + def is_watching(self, path): + with self.lock: + return path in self.watches + def __del__(self): try: lock = self.lock From ad519b5e919bcd0efe48219c684ca19e9ef55ddc Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Sep 2014 22:05:00 +0400 Subject: [PATCH 1435/1472] Add realpath() calls to Uv watchers where appropriate --- powerline/lib/watcher/uv.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/powerline/lib/watcher/uv.py b/powerline/lib/watcher/uv.py index 88b64366..9feed1fa 100644 --- a/powerline/lib/watcher/uv.py +++ b/powerline/lib/watcher/uv.py @@ -63,6 +63,7 @@ class UvWatcher(object): self.loop = start_uv_thread() def watch(self, path): + path = realpath(path) with self.lock: if path not in self.watches: try: @@ -80,6 +81,7 @@ class UvWatcher(object): raise def unwatch(self, path): + path = realpath(path) with self.lock: try: watch = self.watches.pop(path) @@ -89,7 +91,7 @@ class UvWatcher(object): def is_watching(self, path): with self.lock: - return path in self.watches + return realpath(path) in self.watches def __del__(self): try: @@ -119,6 +121,7 @@ class UvFileWatcher(UvWatcher): self.events.pop(path, None) def __call__(self, path): + path = realpath(path) with self.lock: events = self.events.pop(path, None) if events: @@ -139,7 +142,7 @@ class UvTreeWatcher(UvWatcher): self.watch_directory(self.basedir) def watch_directory(self, path): - os.path.walk(path, self.watch_one_directory, None) + os.path.walk(realpath(path), self.watch_one_directory, None) def watch_one_directory(self, arg, dirname, fnames): try: From a2c6d79b2f89fd1f8d4cd47936d42b02de4dd173 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 Sep 2014 22:09:07 +0400 Subject: [PATCH 1436/1472] Return False in place of None to indicate absense of changes Fixes #1052: another part of it --- powerline/lib/watcher/uv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/lib/watcher/uv.py b/powerline/lib/watcher/uv.py index 9feed1fa..02fdfcc6 100644 --- a/powerline/lib/watcher/uv.py +++ b/powerline/lib/watcher/uv.py @@ -129,6 +129,7 @@ class UvFileWatcher(UvWatcher): if path not in self.watches: self.watch(path) return True + return False class UvTreeWatcher(UvWatcher): From c50838bd2c4a74c7282dd2b37c77305b8c03cb91 Mon Sep 17 00:00:00 2001 From: Collin Grady Date: Tue, 2 Sep 2014 18:23:52 -0700 Subject: [PATCH 1437/1472] Corrects alignment issue between inactive and active windows in tmux. Adresses #1029 --- powerline/bindings/tmux/powerline.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 81928706..3fb316a3 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -8,7 +8,7 @@ set -g status-interval 2 set -g status-left-length 20 set -g status-right '#(eval $POWERLINE_COMMAND tmux right -R pane_id=`tmux display -p "#D"`)' set -g status-right-length 150 -set -g window-status-format "#[fg=colour244,bg=colour234]#I #[fg=colour240] #[default]#W " +set -g window-status-format "#[fg=colour244,bg=colour234] #I #[fg=colour240] #[default]#W " set -g window-status-current-format "#[fg=colour234,bg=colour31]#[fg=colour117,bg=colour31] #I  #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]" # Legacy status-left definition to be overwritten for tmux Versions 1.8+ From eda93c87fd48e0c520c8add1f6bda56c23ba546e Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 08:19:17 +0400 Subject: [PATCH 1438/1472] Add default_log_stream Powerline attribute, make it sys.stdout in Vim Reason for changes in VimPowerline: try python import sys; sys.stderr.write('abc') catch echomsg v:exception endtry reports an exception. This should not happen because stack trace that ought to be printed by the logger will not be shown to the user, making it hard to determine the error and turning some recoverable errors to non-recoverable when using powerline.vim. --- powerline/__init__.py | 24 ++++++++++++++++-------- powerline/vim.py | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 78aa9bb0..1d01d5d5 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -103,7 +103,7 @@ class PowerlineLogger(object): _fallback_logger = None -def get_fallback_logger(): +def get_fallback_logger(stream=None): global _fallback_logger if _fallback_logger: return _fallback_logger @@ -112,7 +112,7 @@ def get_fallback_logger(): formatter = logging.Formatter(log_format) level = logging.WARNING - handler = logging.StreamHandler() + handler = logging.StreamHandler(stream) handler.setLevel(level) handler.setFormatter(formatter) @@ -195,7 +195,7 @@ def load_config(cfg_path, find_config_files, config_loader, loader_callback=None return ret -def _get_log_handler(common_config): +def _get_log_handler(common_config, stream=None): '''Get log handler. :param dict common_config: @@ -211,17 +211,17 @@ def _get_log_handler(common_config): os.mkdir(log_dir) return logging.FileHandler(log_file) else: - return logging.StreamHandler() + return logging.StreamHandler(stream) -def create_logger(common_config): +def create_logger(common_config, stream=None): '''Create logger according to provided configuration ''' log_format = common_config['log_format'] formatter = logging.Formatter(log_format) level = getattr(logging, common_config['log_level']) - handler = _get_log_handler(common_config) + handler = _get_log_handler(common_config, stream) handler.setLevel(level) handler.setFormatter(formatter) @@ -456,7 +456,7 @@ class Powerline(object): self.import_paths = self.common_config['paths'] if not self.logger: - self.logger = create_logger(self.common_config) + self.logger = create_logger(self.common_config, self.default_log_stream) if not self.pl: self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext) @@ -562,6 +562,14 @@ class Powerline(object): else: self.renderer = renderer + default_log_stream = sys.stdout + '''Default stream for default log handler + + Usually it is ``sys.stderr``, but there is sometimes a reason to prefer + ``sys.stdout`` or a custom file-like object. It is not supposed to be used + to write to some file. + ''' + def setup_components(self, components): '''Run component-specific setup @@ -847,7 +855,7 @@ class Powerline(object): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' exception = kwargs.pop('exception', None) - pl = getattr(self, 'pl', None) or get_fallback_logger() + pl = getattr(self, 'pl', None) or get_fallback_logger(self.default_log_stream) if exception: try: reraise(exception) diff --git a/powerline/vim.py b/powerline/vim.py index a443b74c..ec754107 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -31,6 +31,8 @@ class VimPowerline(Powerline): self.pyeval = pyeval self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' + default_log_stream = sys.stdout + def add_local_theme(self, key, config): '''Add local themes at runtime (during vim session). From a919b453ee600a445717638e87f3c1180c4b1f23 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 08:27:36 +0400 Subject: [PATCH 1439/1472] Fix some errors in troubleshooting code --- powerline/bindings/vim/plugin/powerline.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 8f839236..0685727c 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -99,6 +99,7 @@ finally echohl None call s:rcmd("def powerline_troubleshoot():") call s:rcmd(" import sys") + call s:rcmd(" import vim") call s:rcmd(" if sys.version_info < (2, 6):") call s:rcmd(" print('Too old python version: ' + sys.version + ' (first supported is 2.6)')") call s:rcmd(" elif sys.version_info[0] == 3 and sys.version_info[1] < 2:") @@ -123,10 +124,11 @@ finally call s:rcmd(" print('neither it is installed system-wide')") call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") - call s:rcmd(" if powerline_appended_path is not None and real_this_dir != powerline_appended_path:") + call s:rcmd(" this_dir_par = os.path.dirname(real_this_dir)") + call s:rcmd(" if powerline_appended_path is not None and this_dir_par != powerline_appended_path:") call s:rcmd(" print('Check your installation: this script is symlinked somewhere')") call s:rcmd(" print('where powerline is not present: {0!r} != {1!r}.'.format(") - call s:rcmd(" real_this_dir, sys.path[-1]))") + call s:rcmd(" real_this_dir, powerline_appended_path))") call s:rcmd(" elif real_powerline_dir != real_this_dir:") call s:rcmd(" print('It appears that you have two powerline versions installed:')") call s:rcmd(" print('one in ' + real_powerline_dir + ', other in ' + real_this_dir + '.')") From 0bb14dbe9c7962bf4f834b4b4032edc252eec855 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 08:28:47 +0400 Subject: [PATCH 1440/1472] Make it immune to the absense of powerline_appended_path --- powerline/bindings/vim/plugin/powerline.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index 0685727c..5ec007c0 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -125,6 +125,7 @@ finally call s:rcmd(" real_powerline_dir = os.path.realpath(powerline_dir)") call s:rcmd(" real_this_dir = os.path.realpath(this_dir)") call s:rcmd(" this_dir_par = os.path.dirname(real_this_dir)") + call s:rcmd(" powerline_appended_path = globals().get('powerline_appended_path')") call s:rcmd(" if powerline_appended_path is not None and this_dir_par != powerline_appended_path:") call s:rcmd(" print('Check your installation: this script is symlinked somewhere')") call s:rcmd(" print('where powerline is not present: {0!r} != {1!r}.'.format(") From 8588885b045400ff892171d1b1a39f2d77b7a08f Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 08:32:36 +0400 Subject: [PATCH 1441/1472] In vim tests check for printed messages --- tests/test_local_overrides.vim | 9 +++++++++ tests/test_tabline.vim | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index 050e393e..ec902856 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -3,6 +3,9 @@ set encoding=utf-8 let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' let g:powerline_config_overrides = {'common': {'default_top_theme': 'ascii'}} let g:powerline_theme_overrides__default = {'segment_data': {'line_current_symbol': {'contents': 'LN '}, 'branch': {'before': 'B '}}} + +redir => g:messages + try python import powerline.vim let pycmd = 'python' @@ -36,4 +39,10 @@ if result isnot# '%#Pl_22_24320_148_11523840_bold# NORMAL %#Pl_148_11523840_236_ cquit endif +redir END +if g:messages =~ '\S' + call writefile(['Non-empty messages:', g:messages], 'message.fail') + cquit +endif + qall! diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 00c807a7..9d829ea6 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -6,6 +6,8 @@ edit abc tabedit def tabedit ghi +redir => g:messages + try let &columns = 80 let result = eval(&tabline[2:]) @@ -45,4 +47,10 @@ if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_25 cquit endif +redir END +if g:messages =~ '\S' + call writefile(['Non-empty messages:', g:messages], 'message.fail') + cquit +endif + qall! From 2a73f031a020b100a0112dab7eae0e65f5ea26f1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 08:46:03 +0400 Subject: [PATCH 1442/1472] Add download_url, license and classifiers to setup.py --- setup.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c34d8424..12d6717f 100755 --- a/setup.py +++ b/setup.py @@ -57,10 +57,28 @@ setup( version='beta', description='The ultimate statusline/prompt utility.', long_description=README, - classifiers=[], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Environment :: Plugins', + 'Intended Audience :: End Users/Desktop', + 'License :: OSI Approved :: MIT License', + 'Natural Language :: English', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', + ], + download_url='https://github.com/Lokaltog/powerline/archive/develop.zip', author='Kim Silkebaekken', author_email='kim.silkebaekken+vim@gmail.com', url='https://github.com/Lokaltog/powerline', + license='MIT', # XXX Python 3 doesn't allow compiled C files to be included in the scripts # list below. This is because Python 3 distutils tries to decode the file to # ASCII, and fails when powerline-client is a binary. From 2a515b7849f9c41055a2296fcb7d08048599ca12 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 08:46:29 +0400 Subject: [PATCH 1443/1472] Use PyPI name in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 12d6717f..3c670819 100755 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ else: can_use_scripts = False setup( - name='Powerline', + name='powerline-status', version='beta', description='The ultimate statusline/prompt utility.', long_description=README, From a221e82af129d695aa429ca9b8b8533f3a1cbd35 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 09:09:45 +0400 Subject: [PATCH 1444/1472] Also include client files in the distribution --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 97a85bd5..e7b4e96c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ recursive-include powerline *.json *.vim recursive-include powerline/bindings *.* +recursive-include client *.* From 1d4349ce72566908b55882771d09d68fe2f4ce83 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 09:13:08 +0400 Subject: [PATCH 1445/1472] Exclude *.pyc files in bindings directory --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index e7b4e96c..1e03cb74 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ recursive-include powerline *.json *.vim recursive-include powerline/bindings *.* +recursive-exclude powerline/bindings *.pyc *.pyo recursive-include client *.* From bd719ba1343295b4e6e1fb4548b198b3d1742979 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 19:05:30 +0400 Subject: [PATCH 1446/1472] Make too long line in segment.py more readable --- powerline/segment.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/powerline/segment.py b/powerline/segment.py index c098756a..a1763c71 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -258,7 +258,11 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge highlight_group = segment.get('highlight_group') or name if segment_type in ('function', 'segment_list'): - args = dict(((str(k), v) for k, v in get_key(True, segment, module, function_name, name, 'args', {}).items())) + args = dict(( + (str(k), v) + for k, v in + get_key(True, segment, module, function_name, name, 'args', {}).items() + )) if segment_type == 'segment_list': # Handle startup and shutdown of _contents_func? From 479bc7fca4f644329ef34698088f2335f16260b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 19:09:15 +0400 Subject: [PATCH 1447/1472] Do not raise from gen_segment_getter --- powerline/segment.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/powerline/segment.py b/powerline/segment.py index a1763c71..cb233380 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -234,7 +234,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge try: get_segment_info = segment_getters[segment_type] except KeyError: - raise TypeError('Unknown segment type: {0}'.format(segment_type)) + pl.error('Unknown segment type: {0}', segment_type) + return None try: contents, _contents_func, module, function_name, name = get_segment_info(data, segment) From b5a551eb5615d74c281bc65d61182cbf7b119986 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 19:34:29 +0400 Subject: [PATCH 1448/1472] Fix typos in exclude/include_modes documentation --- docs/source/configuration/reference.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 915ff020..d60ca7ad 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -494,11 +494,11 @@ ascii Theme without any unicode characters at all .. _config-themes-seg-exclude_modes: ``exclude_modes`` - A list of modes where this segment will be excluded: The segment is + A list of modes where this segment will be excluded: the segment is included in all modes, *except* for the modes in this list. ``include_modes`` - A list of modes where this segment will be included: The segment is + A list of modes where this segment will be included: the segment is *not* included in any modes, *except* for the modes in this list. .. _config-themes-seg-display: From ea2fd28292ff1cb294d027286eb6c0427a093a0c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 19:38:22 +0400 Subject: [PATCH 1449/1472] Merge exclude_modes and include_modes documentation --- docs/source/configuration/reference.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index d60ca7ad..c9f62495 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -493,13 +493,14 @@ ascii Theme without any unicode characters at all .. _config-themes-seg-exclude_modes: - ``exclude_modes`` - A list of modes where this segment will be excluded: the segment is - included in all modes, *except* for the modes in this list. - - ``include_modes`` - A list of modes where this segment will be included: the segment is - *not* included in any modes, *except* for the modes in this list. + ``exclude_modes``, ``include_modes`` + A list of modes where this segment will be excluded: the segment is not + included or is included in all modes, *except* for the modes in one of + these lists respectively. If ``exclude_modes`` is not present then it + acts like an empty list (segment is not excluded from any modes). + Without ``include_modes`` it acts like a list with all possible modes + (segment is included in all modes). When there are both + ``exclude_modes`` overrides ``include_modes``. .. _config-themes-seg-display: From 9947bb300af60ccba736d6559472cd92a6f1b35d Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 19:50:45 +0400 Subject: [PATCH 1450/1472] Reference sections where all segments are listed --- docs/source/configuration/listers.rst | 2 ++ docs/source/configuration/reference.rst | 6 ++++++ docs/source/configuration/segments.rst | 2 ++ 3 files changed, 10 insertions(+) diff --git a/docs/source/configuration/listers.rst b/docs/source/configuration/listers.rst index 107159a1..dc544b91 100644 --- a/docs/source/configuration/listers.rst +++ b/docs/source/configuration/listers.rst @@ -1,3 +1,5 @@ +.. _config-listers: + **************** Lister reference **************** diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index c9f62495..d45ed832 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -380,6 +380,9 @@ ascii Theme without any unicode characters at all The segment contents is the return value of the function defined in the :ref:`function option `. + List of function segments is available in :ref:`Segment reference + ` section. + ``string`` A static string segment where the contents is defined in the :ref:`contents option `, and the @@ -392,6 +395,9 @@ ascii Theme without any unicode characters at all ` and :ref:`args ` options. + List of lister segments is available in :ref:`Lister reference + ` section. + .. _config-themes-seg-name: ``name`` diff --git a/docs/source/configuration/segments.rst b/docs/source/configuration/segments.rst index ce32302a..7b6edaaf 100644 --- a/docs/source/configuration/segments.rst +++ b/docs/source/configuration/segments.rst @@ -1,3 +1,5 @@ +.. _config-segments: + ***************** Segment reference ***************** From 96b2cb8f10ba11ab76f0eec94c5f2ee60c1e33fa Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 19:51:10 +0400 Subject: [PATCH 1451/1472] Fix spaces-only line in configuration.rst --- docs/source/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 8ed3fe7d..febf010d 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -108,7 +108,7 @@ segments that you may want to customize right away: The weather segment will try to find your location using a GeoIP lookup, so unless you're on a VPN you probably won't have to change the location query. - + If you want to change the location query or the temperature unit you'll have to update the segment arguments. Open a theme file, scroll down to the weather segment and update it to include unit/location query From bc557bd656a0622dbc7c81a8098f4ecc02957fc5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 20:04:18 +0400 Subject: [PATCH 1452/1472] Use replace_item to add module `bar` to sys.modules Tests should not leave modified environment. --- tests/test_configuration.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index c0dbf8e5..29bb963f 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -11,7 +11,7 @@ import tests.vim as vim_module from tests import TestCase from tests.lib.config_mock import get_powerline, get_powerline_raw, swap_attributes -from tests.lib import Args +from tests.lib import Args, replace_item def highlighted_string(s, group, **kwargs): @@ -438,7 +438,6 @@ class TestSegmentAttributes(TestRender): def test_no_attributes(self, p, config): def m1(divider=',', **kwargs): return divider.join(kwargs.keys()) + divider - sys.modules['bar'] = Args(m1=m1) config['themes/test/default']['segments'] = { 'left': [ { @@ -446,7 +445,8 @@ class TestSegmentAttributes(TestRender): } ] } - self.assertRenderEqual(p, '{56} pl,{6-}>>{--}') + with replace_item(sys.modules, 'bar', Args(m1=m1)): + self.assertRenderEqual(p, '{56} pl,{6-}>>{--}') @add_args def test_segment_datas(self, p, config): @@ -464,7 +464,6 @@ class TestSegmentAttributes(TestRender): } } } - sys.modules['bar'] = Args(m1=m1) config['themes/test/default']['segments'] = { 'left': [ { @@ -472,7 +471,8 @@ class TestSegmentAttributes(TestRender): } ] } - self.assertRenderEqual(p, '{56} pl;{6-}>>{--}') + with replace_item(sys.modules, 'bar', Args(m1=m1)): + self.assertRenderEqual(p, '{56} pl;{6-}>>{--}') @add_args def test_expand(self, p, config): @@ -483,7 +483,6 @@ class TestSegmentAttributes(TestRender): return ('-' * amount) + segment['contents'] m1.expand = expand - sys.modules['bar'] = Args(m1=m1) config['themes/test/default']['segments'] = { 'left': [ { @@ -492,7 +491,8 @@ class TestSegmentAttributes(TestRender): } ] } - self.assertRenderEqual(p, '{56} ----pl,{6-}>>{--}', width=10) + with replace_item(sys.modules, 'bar', Args(m1=m1)): + self.assertRenderEqual(p, '{56} ----pl,{6-}>>{--}', width=10) @add_args def test_truncate(self, p, config): @@ -503,7 +503,6 @@ class TestSegmentAttributes(TestRender): return segment['contents'][:-amount] m1.truncate = truncate - sys.modules['bar'] = Args(m1=m1) config['themes/test/default']['segments'] = { 'left': [ { @@ -511,7 +510,8 @@ class TestSegmentAttributes(TestRender): } ] } - self.assertRenderEqual(p, '{56} p{6-}>>{--}', width=4) + with replace_item(sys.modules, 'bar', Args(m1=m1)): + self.assertRenderEqual(p, '{56} p{6-}>>{--}', width=4) class TestSegmentData(TestRender): From 7bf025ca2b873655e8f1ec06a2199672d0ebefb5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 20:34:06 +0400 Subject: [PATCH 1453/1472] Add `exclude_/include_function` support This is first step towards fixing #1046. --- docs/source/configuration/reference.rst | 17 +++++ docs/source/develop/segments.rst | 12 +++- powerline/lint/__init__.py | 40 +++++++++--- powerline/segment.py | 73 ++++++++++++++++++---- powerline/theme.py | 4 +- tests/test_configuration.py | 83 ++++++++++++++++++++++++- 6 files changed, 203 insertions(+), 26 deletions(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index d45ed832..0e8b384d 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -508,6 +508,23 @@ ascii Theme without any unicode characters at all (segment is included in all modes). When there are both ``exclude_modes`` overrides ``include_modes``. + .. _config-themes-seg-exclude_function: + + ``exclude_function``, ``include_function`` + Function name in a form ``{name}`` or ``{module}.{name}`` (in the first + form ``{module}`` defaults to ``powerline.selectors.{ext}``). Determines + under which condition specific segment will be included or excluded. By + default segment is always included and never excluded. + ``exclude_function`` overrides ``include_function``. + + .. note:: + Options :ref:`exclude_/include_modes + ` complement + ``exclude_/include_functions``: segment will be included if it is + included by either ``include_mode`` or ``include_function`` and will + be excluded if it is excluded by either ``exclude_mode`` or + ``exclude_function``. + .. _config-themes-seg-display: ``display`` diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index e1dd1b64..55838ae7 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -254,9 +254,15 @@ Segment dictionary contains the following keys: ``side`` Segment side: ``right`` or ``left``. - ``exclude_modes``, ``include_modes`` - :ref:`Mode display control lists `. May be - empty, but may not be ``None``. + ``display_condition``` + Contains function that takes three position parameters: + :py:class:`powerline.PowerlineLogger` instance, :ref:`segment_info + ` dictionary and current mode and returns either ``True`` + or ``False`` to indicate whether particular segment should be processed. + + This key is constructed based on :ref:`exclude_/include_modes keys + ` and :ref:`exclude_/include_function keys + `. ``width``, ``align`` :ref:`Width and align options `. May be ``None``. diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index bf88d2c6..6a5d86d5 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -9,6 +9,7 @@ import logging from collections import defaultdict from copy import copy +from functools import partial from powerline.lint.markedjson import load from powerline import generate_config_finder, get_config_paths, load_config @@ -478,18 +479,18 @@ def check_matcher_func(ext, match_name, data, context, echoerr): echoerr(context='Error while loading matcher functions', problem='failed to load module {0}'.format(match_module), problem_mark=match_name.mark) - return True, True + return True, False, True except AttributeError: echoerr(context='Error while loading matcher functions', problem='failed to load matcher function {0}'.format(match_function), problem_mark=match_name.mark) - return True, True + return True, False, True if not callable(func): echoerr(context='Error while loading matcher functions', problem='loaded "function" {0} is not callable'.format(match_function), problem_mark=match_name.mark) - return True, True + return True, False, True if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'): if func.func_code.co_argcount != 1: @@ -502,7 +503,7 @@ def check_matcher_func(ext, match_name, data, context, echoerr): problem_mark=match_name.mark ) - return True, False + return True, False, False def check_ext(ext, data, context, echoerr): @@ -561,6 +562,9 @@ def check_top_theme(theme, data, context, echoerr): return True, False, False +function_name_re = '^(\w+\.)*[a-zA-Z_]\w*$' + + divider_spec = Spec().type(unicode).len( 'le', 3, (lambda value: 'Divider {0!r} is too large!'.format(value))).copy ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy @@ -608,7 +612,8 @@ main_spec = (Spec( local_themes=Spec( __tabline__=ext_theme_spec(), ).unknown_spec( - lambda *args: check_matcher_func('vim', *args), ext_theme_spec() + Spec().re(function_name_re).func(partial(check_matcher_func, 'vim')), + ext_theme_spec() ), ).optional(), ipython=ext_spec().update( @@ -801,6 +806,7 @@ shell_colorscheme_spec = (Spec( generic_keys = set(( 'exclude_modes', 'include_modes', + 'exclude_function', 'include_function', 'width', 'align', 'name', 'draw_soft_divider', 'draw_hard_divider', @@ -935,7 +941,7 @@ def check_full_segment_data(segment, data, context, echoerr): return check_key_compatibility(segment_copy, data, context, echoerr) -def import_segment(name, data, context, echoerr, module): +def import_function(function_type, name, data, context, echoerr, module): context_has_marks(context) havemarks(name, module) @@ -949,7 +955,7 @@ def import_segment(name, data, context, echoerr, module): problem_mark=module.mark) return None except AttributeError: - echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), + echoerr(context='Error while loading {0} function (key {key})'.format(function_type, key=context_key(context)), problem='failed to load function {0} from module {1}'.format(name, module), problem_mark=name.mark) return None @@ -964,6 +970,10 @@ def import_segment(name, data, context, echoerr, module): return func +def import_segment(*args, **kwargs): + return import_function('segment', *args, **kwargs) + + def check_segment_function(function_name, data, context, echoerr): havemarks(function_name) ext = data['ext'] @@ -1334,6 +1344,17 @@ def get_all_possible_functions(data, context, echoerr): yield func +def check_exinclude_function(name, data, context, echoerr): + ext = data['ext'] + module, name = name.rpartition('.')[::2] + if not module: + module = MarkedUnicode('powerline.selectors.' + ext, None) + func = import_function('selector', name, data, context, echoerr, module=module) + if not func: + return True, False, True + return True, False, False + + args_spec = Spec( pl=Spec().error('pl object must be set by powerline').optional(), segment_info=Spec().error('Segment info dictionary must be set by powerline').optional(), @@ -1341,12 +1362,15 @@ args_spec = Spec( highlight_group_spec = Spec().type(unicode).copy segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy sub_segments_spec = Spec() +exinclude_spec = Spec().re(function_name_re).func(check_exinclude_function).copy segment_spec = Spec( type=Spec().oneof(type_keys).optional(), name=Spec().re('^[a-zA-Z_]\w*$').optional(), - function=Spec().re('^(\w+\.)*[a-zA-Z_]\w*$').func(check_segment_function).optional(), + function=Spec().re(function_name_re).func(check_segment_function).optional(), exclude_modes=Spec().list(vim_mode_spec()).optional(), include_modes=Spec().list(vim_mode_spec()).optional(), + exclude_function=exinclude_spec().optional(), + include_function=exinclude_spec().optional(), draw_hard_divider=Spec().type(bool).optional(), draw_soft_divider=Spec().type(bool).optional(), draw_inner_divider=Spec().type(bool).optional(), diff --git a/powerline/segment.py b/powerline/segment.py index cb233380..f7a8f1a1 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -113,13 +113,7 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors subsegment['priority'] *= subsegment_update['priority_multiplier'] subsegment_mode = subsegment_update.get('mode') - if subsegment_mode and ( - subsegment_mode in subsegment['exclude_modes'] - or ( - subsegment['include_modes'] - and subsegment_mode not in subsegment['include_modes'] - ) - ): + if subsegment_mode and not subsegment['display_condition'](pl, segment_info, subsegment_mode): continue process_segment( @@ -218,6 +212,9 @@ def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colo parsed_segments.append(segment) +always_true = lambda pl, segment_info, mode: True + + def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr, top_theme): data = { 'default_module': default_module or 'powerline.segments.' + ext, @@ -229,6 +226,60 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, function_name, name, module, default) data['get_key'] = get_key + def get_selector(function_name): + if '.' in function_name: + module, function_name = function_name.rpartition('.')[::2] + else: + module = 'powerline.selectors.' + ext + function = get_module_attr(module, function_name, prefix='segment_generator/selector_function') + if not function: + pl.error('Failed to get segment selector, ignoring it') + return function + + def get_segment_selector(segment, selector_type): + try: + function_name = segment[selector_type + '_function'] + except KeyError: + function = None + else: + function = get_selector(function_name) + try: + modes = segment[selector_type + '_modes'] + except KeyError: + modes = None + + if modes: + if function: + return lambda pl, segment_info, mode: ( + mode in modes + or function(pl=pl, segment_info=segment_info, mode=mode) + ) + else: + return lambda pl, segment_info, mode: mode in modes + else: + if function: + return lambda pl, segment_info, mode: ( + function(pl=pl, segment_info=segment_info, mode=mode) + ) + else: + return None + + def gen_display_condition(segment): + include_function = get_segment_selector(segment, 'include') + exclude_function = get_segment_selector(segment, 'exclude') + if include_function: + if exclude_function: + return lambda *args: ( + include_function(*args) + and not exclude_function(*args)) + else: + return include_function + else: + if exclude_function: + return lambda *args: not exclude_function(*args) + else: + return always_true + def get(segment, side): segment_type = segment.get('type', 'function') try: @@ -265,6 +316,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge get_key(True, segment, module, function_name, name, 'args', {}).items() )) + display_condition = gen_display_condition(segment) + if segment_type == 'segment_list': # Handle startup and shutdown of _contents_func? subsegments = [ @@ -292,8 +345,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'draw_hard_divider': None, 'draw_inner_divider': None, 'side': side, - 'exclude_modes': segment.get('exclude_modes', []), - 'include_modes': segment.get('include_modes', []), + 'display_condition': display_condition, 'width': None, 'align': None, 'expand': None, @@ -342,8 +394,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'draw_soft_divider': segment.get('draw_soft_divider', True), 'draw_inner_divider': segment.get('draw_inner_divider', False), 'side': side, - 'exclude_modes': segment.get('exclude_modes', []), - 'include_modes': segment.get('include_modes', []), + 'display_condition': display_condition, 'width': segment.get('width'), 'align': segment.get('align', 'l'), 'expand': expand_func, diff --git a/powerline/theme.py b/powerline/theme.py index 9fa90e54..32a8aeee 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -136,9 +136,7 @@ class Theme(object): parsed_segments = [] for segment in self.segments[line][side]: # No segment-local modes at this point - if mode not in segment['exclude_modes'] and ( - not segment['include_modes'] or mode in segment['include_modes'] - ): + if segment['display_condition'](self.pl, segment_info, mode): process_segment( self.pl, side, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 29bb963f..aff0341e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -389,7 +389,7 @@ class TestThemeHierarchy(TestRender): ]) -class TestModes(TestRender): +class TestDisplayCondition(TestRender): @add_args def test_include_modes(self, p, config): config['themes/test/default']['segments'] = { @@ -432,6 +432,87 @@ class TestModes(TestRender): self.assertRenderEqual(p, '{56} s1{6-}>>{--}', mode='m2') self.assertRenderEqual(p, '{56} s2{6-}>>{--}', mode='m3') + @add_args + def test_exinclude_function_nonexistent_module(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s1', 'g1', exclude_function='xxx_nonexistent_module.foo'), + highlighted_string('s2', 'g1', exclude_function='xxx_nonexistent_module.foo', include_function='xxx_nonexistent_module.bar'), + highlighted_string('s3', 'g1', include_function='xxx_nonexistent_module.bar'), + ] + } + self.assertRenderEqual(p, '{56} s1{56}>{56}s2{56}>{56}s3{6-}>>{--}') + + @add_args + def test_exinclude_function(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s1', 'g1', exclude_function='mod.foo'), + highlighted_string('s2', 'g1', exclude_function='mod.foo', include_function='mod.bar'), + highlighted_string('s3', 'g1', include_function='mod.bar'), + ] + } + launched = set() + fool = [None] + barl = [None] + + def foo(*args, **kwargs): + launched.add('foo') + self.assertEqual(set(kwargs.keys()), set(('pl', 'segment_info', 'mode'))) + self.assertEqual(args, ()) + return fool[0] + + def bar(*args, **kwargs): + launched.add('bar') + self.assertEqual(set(kwargs.keys()), set(('pl', 'segment_info', 'mode'))) + self.assertEqual(args, ()) + return barl[0] + + with replace_item(sys.modules, 'mod', Args(foo=foo, bar=bar)): + fool[0] = True + barl[0] = True + self.assertRenderEqual(p, '{56} s3{6-}>>{--}') + self.assertEqual(launched, set(('foo', 'bar'))) + + fool[0] = False + barl[0] = True + self.assertRenderEqual(p, '{56} s1{56}>{56}s2{56}>{56}s3{6-}>>{--}') + self.assertEqual(launched, set(('foo', 'bar'))) + + fool[0] = False + barl[0] = False + self.assertRenderEqual(p, '{56} s1{6-}>>{--}') + self.assertEqual(launched, set(('foo', 'bar'))) + + fool[0] = True + barl[0] = False + self.assertRenderEqual(p, '{--}') + self.assertEqual(launched, set(('foo', 'bar'))) + + @add_args + def test_exinclude_modes_override_functions(self, p, config): + config['themes/test/default']['segments'] = { + 'left': [ + highlighted_string('s1', 'g1', exclude_function='mod.foo', exclude_modes=['m2']), + highlighted_string('s2', 'g1', exclude_function='mod.foo', include_modes=['m2']), + highlighted_string('s3', 'g1', include_function='mod.foo', exclude_modes=['m2']), + highlighted_string('s4', 'g1', include_function='mod.foo', include_modes=['m2']), + ] + } + fool = [None] + + def foo(*args, **kwargs): + return fool[0] + + with replace_item(sys.modules, 'mod', Args(foo=foo)): + fool[0] = True + self.assertRenderEqual(p, '{56} s4{6-}>>{--}', mode='m2') + self.assertRenderEqual(p, '{56} s3{56}>{56}s4{6-}>>{--}', mode='m1') + + fool[0] = False + self.assertRenderEqual(p, '{56} s2{56}>{56}s4{6-}>>{--}', mode='m2') + self.assertRenderEqual(p, '{56} s1{6-}>>{--}', mode='m1') + class TestSegmentAttributes(TestRender): @add_args From c5ca1b23c402585dfdfcec52153e7cff7b50088c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 21:32:26 +0400 Subject: [PATCH 1454/1472] Use class setup/teardown methods to use vim module mock --- tests/test_configuration.py | 12 +- tests/test_provided_config_files.py | 16 ++- tests/test_segments.py | 181 ++++++++++++++-------------- 3 files changed, 109 insertions(+), 100 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index aff0341e..714bba83 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -684,13 +684,13 @@ class TestVim(TestCase): winnr = window.number self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_5_12583104_6_32896_NONE#\xa0\u201cbar\u201d%#Pl_6_32896_NONE_None_NONE#>>') + @classmethod + def setUpClass(cls): + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) -def setUpModule(): - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) - - -def tearDownModule(): - sys.path.pop(0) + @classmethod + def tearDownClass(cls): + sys.path.pop(0) if __name__ == '__main__': diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 214748ea..05dd68a9 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -18,7 +18,7 @@ VBLOCK = chr(ord('V') - 0x40) SBLOCK = chr(ord('S') - 0x40) -class TestConfig(TestCase): +class TestVimConfig(TestCase): def test_vim(self): from powerline.vim import VimPowerline cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') @@ -76,6 +76,16 @@ class TestConfig(TestCase): finally: vim_module._start_mode('n') + @classmethod + def setUpClass(cls): + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) + + @classmethod + def tearDownClass(cls): + sys.path.pop(0) + + +class TestConfig(TestCase): def test_tmux(self): from powerline.segments import common from imp import reload @@ -151,10 +161,7 @@ def setUpModule(): saved_get_config_paths = powerline.get_config_paths path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'powerline', 'config_files') powerline.get_config_paths = lambda: [path] - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) old_cwd = os.getcwd() - from powerline.segments import vim - globals()['vim'] = vim def tearDownModule(): @@ -164,7 +171,6 @@ def tearDownModule(): powerline.get_config_paths = saved_get_config_paths os.chdir(old_cwd) old_cwd = None - sys.path.pop(0) if __name__ == '__main__': diff --git a/tests/test_segments.py b/tests/test_segments.py index 641d5f81..fd010022 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -15,9 +15,6 @@ from tests.lib import Args, urllib_read, replace_attr, new_module, replace_modul from tests import TestCase, SkipTest -vim = None - - def get_dummy_guess(**kwargs): if 'directory' in kwargs: def guess(path, create_watcher): @@ -781,18 +778,18 @@ class TestVim(TestCase): def test_mode(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'NORMAL') - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'i': 'INS'}), 'NORMAL') - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'n': 'NORM'}), 'NORM') + self.assertEqual(self.vim.mode(pl=pl, segment_info=segment_info), 'NORMAL') + self.assertEqual(self.vim.mode(pl=pl, segment_info=segment_info, override={'i': 'INS'}), 'NORMAL') + self.assertEqual(self.vim.mode(pl=pl, segment_info=segment_info, override={'n': 'NORM'}), 'NORM') with vim_module._with('mode', 'i') as segment_info: - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'INSERT') + self.assertEqual(self.vim.mode(pl=pl, segment_info=segment_info), 'INSERT') with vim_module._with('mode', chr(ord('V') - 0x40)) as segment_info: - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'V-BLCK') - self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') + self.assertEqual(self.vim.mode(pl=pl, segment_info=segment_info), 'V-BLCK') + self.assertEqual(self.vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') def test_visual_range(self): pl = Pl() - vr = partial(vim.visual_range, pl=pl) + vr = partial(self.vim.visual_range, pl=pl) vim_module.current.window.cursor = [0, 0] try: with vim_module._with('mode', 'i') as segment_info: @@ -839,116 +836,116 @@ class TestVim(TestCase): def test_modified_indicator(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.modified_indicator(pl=pl, segment_info=segment_info), None) segment_info['buffer'][0] = 'abc' try: - self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), '+') - self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info, text='-'), '-') + self.assertEqual(self.vim.modified_indicator(pl=pl, segment_info=segment_info), '+') + self.assertEqual(self.vim.modified_indicator(pl=pl, segment_info=segment_info, text='-'), '-') finally: vim_module._bw(segment_info['bufnr']) def test_paste_indicator(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.paste_indicator(pl=pl, segment_info=segment_info), None) with vim_module._with('options', paste=1): - self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info), 'PASTE') - self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info, text='P'), 'P') + self.assertEqual(self.vim.paste_indicator(pl=pl, segment_info=segment_info), 'PASTE') + self.assertEqual(self.vim.paste_indicator(pl=pl, segment_info=segment_info, text='P'), 'P') def test_readonly_indicator(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.readonly_indicator(pl=pl, segment_info=segment_info), None) with vim_module._with('bufoptions', readonly=1): - self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), 'RO') - self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info, text='L'), 'L') + self.assertEqual(self.vim.readonly_indicator(pl=pl, segment_info=segment_info), 'RO') + self.assertEqual(self.vim.readonly_indicator(pl=pl, segment_info=segment_info, text='L'), 'L') def test_file_scheme(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_scheme(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.file_scheme(pl=pl, segment_info=segment_info), None) with vim_module._with('buffer', '/tmp/’’/abc') as segment_info: - self.assertEqual(vim.file_scheme(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.file_scheme(pl=pl, segment_info=segment_info), None) with vim_module._with('buffer', 'zipfile:/tmp/abc.zip::abc/abc.vim') as segment_info: - self.assertEqual(vim.file_scheme(pl=pl, segment_info=segment_info), 'zipfile') + self.assertEqual(self.vim.file_scheme(pl=pl, segment_info=segment_info), 'zipfile') def test_file_directory(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), None) with replace_env('HOME', '/home/foo', os.environ): with vim_module._with('buffer', '/tmp/’’/abc') as segment_info: - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/’’/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/’’/') with vim_module._with('buffer', b'/tmp/\xFF\xFF/abc') as segment_info: - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp//') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), '/tmp//') with vim_module._with('buffer', '/tmp/abc') as segment_info: - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/') os.environ['HOME'] = '/tmp' - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '~/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), '~/') with vim_module._with('buffer', 'zipfile:/tmp/abc.zip::abc/abc.vim') as segment_info: - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=False), 'zipfile:/tmp/abc.zip::abc/') - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=True), '/tmp/abc.zip::abc/') - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/abc.zip::abc/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=False), 'zipfile:/tmp/abc.zip::abc/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=True), '/tmp/abc.zip::abc/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/abc.zip::abc/') os.environ['HOME'] = '/tmp' - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=False), 'zipfile:/tmp/abc.zip::abc/') - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=True), '/tmp/abc.zip::abc/') - self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/abc.zip::abc/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=False), 'zipfile:/tmp/abc.zip::abc/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info, remove_scheme=True), '/tmp/abc.zip::abc/') + self.assertEqual(self.vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/abc.zip::abc/') def test_file_name(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), None) - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True), [ + self.assertEqual(self.vim.file_name(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True), [ {'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']} ]) - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True, no_file_text='X'), [ + self.assertEqual(self.vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True, no_file_text='X'), [ {'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']} ]) with vim_module._with('buffer', '/tmp/abc') as segment_info: - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), 'abc') + self.assertEqual(self.vim.file_name(pl=pl, segment_info=segment_info), 'abc') with vim_module._with('buffer', '/tmp/’’') as segment_info: - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), '’’') + self.assertEqual(self.vim.file_name(pl=pl, segment_info=segment_info), '’’') with vim_module._with('buffer', b'/tmp/\xFF\xFF') as segment_info: - self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), '') + self.assertEqual(self.vim.file_name(pl=pl, segment_info=segment_info), '') def test_file_size(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_size(pl=pl, segment_info=segment_info), '0 B') + self.assertEqual(self.vim.file_size(pl=pl, segment_info=segment_info), '0 B') with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info: - self.assertEqual(vim.file_size(pl=pl, segment_info=segment_info), '0 B') + self.assertEqual(self.vim.file_size(pl=pl, segment_info=segment_info), '0 B') def test_file_opts(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.file_format(pl=pl, segment_info=segment_info), [ + self.assertEqual(self.vim.file_format(pl=pl, segment_info=segment_info), [ {'divider_highlight_group': 'background:divider', 'contents': 'unix'} ]) - self.assertEqual(vim.file_encoding(pl=pl, segment_info=segment_info), [ + self.assertEqual(self.vim.file_encoding(pl=pl, segment_info=segment_info), [ {'divider_highlight_group': 'background:divider', 'contents': 'utf-8'} ]) - self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.file_type(pl=pl, segment_info=segment_info), None) with vim_module._with('bufoptions', filetype='python'): - self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), [ + self.assertEqual(self.vim.file_type(pl=pl, segment_info=segment_info), [ {'divider_highlight_group': 'background:divider', 'contents': 'python'} ]) def test_window_title(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.window_title(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.window_title(pl=pl, segment_info=segment_info), None) with vim_module._with('wvars', quickfix_title='Abc'): - self.assertEqual(vim.window_title(pl=pl, segment_info=segment_info), 'Abc') + self.assertEqual(self.vim.window_title(pl=pl, segment_info=segment_info), 'Abc') def test_line_percent(self): pl = Pl() segment_info = vim_module._get_segment_info() segment_info['buffer'][0:-1] = [str(i) for i in range(100)] try: - self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '1') + self.assertEqual(self.vim.line_percent(pl=pl, segment_info=segment_info), '1') vim_module._set_cursor(50, 0) - self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '50') - self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info, gradient=True), [ + self.assertEqual(self.vim.line_percent(pl=pl, segment_info=segment_info), '50') + self.assertEqual(self.vim.line_percent(pl=pl, segment_info=segment_info, gradient=True), [ {'contents': '50', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 50 * 100.0 / 101} ]) finally: @@ -959,9 +956,9 @@ class TestVim(TestCase): segment_info = vim_module._get_segment_info() segment_info['buffer'][0:-1] = [str(i) for i in range(99)] try: - self.assertEqual(vim.line_count(pl=pl, segment_info=segment_info), '100') + self.assertEqual(self.vim.line_count(pl=pl, segment_info=segment_info), '100') vim_module._set_cursor(50, 0) - self.assertEqual(vim.line_count(pl=pl, segment_info=segment_info), '100') + self.assertEqual(self.vim.line_count(pl=pl, segment_info=segment_info), '100') finally: vim_module._bw(segment_info['bufnr']) @@ -971,18 +968,18 @@ class TestVim(TestCase): try: segment_info['buffer'][0:-1] = [str(i) for i in range(99)] vim_module._set_cursor(49, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info), '50%') - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [ + self.assertEqual(self.vim.position(pl=pl, segment_info=segment_info), '50%') + self.assertEqual(self.vim.position(pl=pl, segment_info=segment_info, gradient=True), [ {'contents': '50%', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 50.0} ]) vim_module._set_cursor(0, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info), 'Top') + self.assertEqual(self.vim.position(pl=pl, segment_info=segment_info), 'Top') vim_module._set_cursor(97, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Final') + self.assertEqual(self.vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Final') segment_info['buffer'][0:-1] = [str(i) for i in range(2)] vim_module._set_cursor(0, 0) - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Todo') - self.assertEqual(vim.position(pl=pl, segment_info=segment_info, gradient=True), [ + self.assertEqual(self.vim.position(pl=pl, segment_info=segment_info, position_strings={'top': 'Comienzo', 'bottom': 'Final', 'all': 'Todo'}), 'Todo') + self.assertEqual(self.vim.position(pl=pl, segment_info=segment_info, gradient=True), [ {'contents': 'All', 'highlight_group': ['position_gradient', 'position'], 'gradient_level': 0.0} ]) finally: @@ -991,34 +988,34 @@ class TestVim(TestCase): def test_cursor_current(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.line_current(pl=pl, segment_info=segment_info), '1') - self.assertEqual(vim.col_current(pl=pl, segment_info=segment_info), '1') - self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info), [{ + self.assertEqual(self.vim.line_current(pl=pl, segment_info=segment_info), '1') + self.assertEqual(self.vim.col_current(pl=pl, segment_info=segment_info), '1') + self.assertEqual(self.vim.virtcol_current(pl=pl, segment_info=segment_info), [{ 'highlight_group': ['virtcol_current_gradient', 'virtcol_current', 'col_current'], 'contents': '1', 'gradient_level': 100.0 / 80, }]) - self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info, gradient=False), [{ + self.assertEqual(self.vim.virtcol_current(pl=pl, segment_info=segment_info, gradient=False), [{ 'highlight_group': ['virtcol_current', 'col_current'], 'contents': '1', }]) def test_modified_buffers(self): pl = Pl() - self.assertEqual(vim.modified_buffers(pl=pl), None) + self.assertEqual(self.vim.modified_buffers(pl=pl), None) def test_branch(self): pl = Pl() create_watcher = get_fallback_create_watcher() - branch = partial(vim.branch, pl=pl, create_watcher=create_watcher) + branch = partial(self.vim.branch, pl=pl, create_watcher=create_watcher) with vim_module._with('buffer', '/foo') as segment_info: - with replace_attr(vim, 'guess', get_dummy_guess(status=lambda: None)): - with replace_attr(vim, 'tree_status', lambda repo, pl: None): + with replace_attr(self.vim, 'guess', get_dummy_guess(status=lambda: None)): + with replace_attr(self.vim, 'tree_status', lambda repo, pl: None): self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'} ]) self.assertEqual(branch(segment_info=segment_info, status_colors=True), [ {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'} ]) - with replace_attr(vim, 'guess', get_dummy_guess(status=lambda: 'DU')): - with replace_attr(vim, 'tree_status', lambda repo, pl: 'DU'): + with replace_attr(self.vim, 'guess', get_dummy_guess(status=lambda: 'DU')): + with replace_attr(self.vim, 'tree_status', lambda repo, pl: 'DU'): self.assertEqual(branch(segment_info=segment_info, status_colors=False), [ {'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'} ]) @@ -1029,23 +1026,23 @@ class TestVim(TestCase): def test_file_vcs_status(self): pl = Pl() create_watcher = get_fallback_create_watcher() - file_vcs_status = partial(vim.file_vcs_status, pl=pl, create_watcher=create_watcher) + file_vcs_status = partial(self.vim.file_vcs_status, pl=pl, create_watcher=create_watcher) with vim_module._with('buffer', '/foo') as segment_info: - with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')): + with replace_attr(self.vim, 'guess', get_dummy_guess(status=lambda file: 'M')): self.assertEqual(file_vcs_status(segment_info=segment_info), [ {'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'} ]) - with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: None)): + with replace_attr(self.vim, 'guess', get_dummy_guess(status=lambda file: None)): self.assertEqual(file_vcs_status(segment_info=segment_info), None) with vim_module._with('buffer', '/bar') as segment_info: with vim_module._with('bufoptions', buftype='nofile'): - with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')): + with replace_attr(self.vim, 'guess', get_dummy_guess(status=lambda file: 'M')): self.assertEqual(file_vcs_status(segment_info=segment_info), None) def test_trailing_whitespace(self): pl = Pl() with vim_module._with('buffer', 'tws') as segment_info: - trailing_whitespace = partial(vim.trailing_whitespace, pl=pl, segment_info=segment_info) + trailing_whitespace = partial(self.vim.trailing_whitespace, pl=pl, segment_info=segment_info) self.assertEqual(trailing_whitespace(), None) self.assertEqual(trailing_whitespace(), None) vim_module.current.buffer[0] = ' ' @@ -1064,20 +1061,20 @@ class TestVim(TestCase): def test_tabnr(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=True), '1') - self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=False), None) + self.assertEqual(self.vim.tabnr(pl=pl, segment_info=segment_info, show_current=True), '1') + self.assertEqual(self.vim.tabnr(pl=pl, segment_info=segment_info, show_current=False), None) def test_bufnr(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.bufnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['bufnr'])) - self.assertEqual(vim.bufnr(pl=pl, segment_info=segment_info, show_current=False), None) + self.assertEqual(self.vim.bufnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['bufnr'])) + self.assertEqual(self.vim.bufnr(pl=pl, segment_info=segment_info, show_current=False), None) def test_winnr(self): pl = Pl() segment_info = vim_module._get_segment_info() - self.assertEqual(vim.winnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['winnr'])) - self.assertEqual(vim.winnr(pl=pl, segment_info=segment_info, show_current=False), None) + self.assertEqual(self.vim.winnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['winnr'])) + self.assertEqual(self.vim.winnr(pl=pl, segment_info=segment_info, show_current=False), None) def test_single_tab(self): pl = Pl() @@ -1096,25 +1093,35 @@ class TestVim(TestCase): pl = Pl() with vim_module._with('tabpage'): with vim_module._with('buffer', '1') as segment_info: - self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) vim_module.current.buffer[0] = ' ' - self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ + self.assertEqual(self.vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ 'contents': '+', 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], }]) vim_module._undo() - self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) old_buffer = vim_module.current.buffer vim_module._new('2') segment_info = vim_module._get_segment_info() - self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) old_buffer[0] = ' ' - self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), None) - self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ + self.assertEqual(self.vim.modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(self.vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ 'contents': '+', 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], }]) + @classmethod + def setUpClass(cls): + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) + from powerline.segments import vim + cls.vim = vim + + @classmethod + def tearDownClass(cls): + sys.path.pop(0) + old_cwd = None @@ -1122,18 +1129,14 @@ old_cwd = None def setUpModule(): global old_cwd global __file__ - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) old_cwd = os.getcwd() __file__ = os.path.abspath(__file__) os.chdir(os.path.dirname(__file__)) - from powerline.segments import vim - globals()['vim'] = vim def tearDownModule(): global old_cwd os.chdir(old_cwd) - sys.path.pop(0) if __name__ == '__main__': From 54e12b1515d21951f281eb5674f0946a274fa120 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 20:43:35 +0400 Subject: [PATCH 1455/1472] Use new functionality to replace tabbuflister and single_tab segment --- docs/source/configuration.rst | 1 + docs/source/selectors.rst | 17 +++++++ docs/source/selectors/vim.rst | 6 +++ .../config_files/themes/vim/tabline.json | 48 ++++++++++++++----- powerline/listers/vim.py | 8 ---- powerline/segments/vim/__init__.py | 27 ----------- powerline/selectors/__init__.py | 0 powerline/selectors/vim.py | 10 ++++ tests/test_segments.py | 13 ----- tests/test_selectors.py | 36 ++++++++++++++ 10 files changed, 107 insertions(+), 59 deletions(-) create mode 100644 docs/source/selectors.rst create mode 100644 docs/source/selectors/vim.rst create mode 100644 powerline/selectors/__init__.py create mode 100644 powerline/selectors/vim.py create mode 100644 tests/test_selectors.py diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index febf010d..fa7c420d 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -134,4 +134,5 @@ References configuration/reference configuration/segments configuration/listers + configuration/selectors configuration/local diff --git a/docs/source/selectors.rst b/docs/source/selectors.rst new file mode 100644 index 00000000..f9367b27 --- /dev/null +++ b/docs/source/selectors.rst @@ -0,0 +1,17 @@ +.. _config-selectors: + +****************** +Selector functions +****************** + +Selector functions are functions that return ``True`` or ``False`` depending on +application state. They are used for :ref:`exclude_function and include_function +segment options `. + +Available selectors +=================== + +.. toctree:: + :glob: + + selectors/* diff --git a/docs/source/selectors/vim.rst b/docs/source/selectors/vim.rst new file mode 100644 index 00000000..5097320e --- /dev/null +++ b/docs/source/selectors/vim.rst @@ -0,0 +1,6 @@ +************* +Vim selectors +************* + +.. automodule:: powerline.selectors.vim + :members: diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 9be639eb..1a7ce6ae 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -4,18 +4,12 @@ "left": [ { "type": "segment_list", - "function": "powerline.listers.vim.tabbuflister", + "function": "powerline.listers.vim.tablister", + "exclude_function": "single_tab", "segments": [ { "function": "tabnr", "after": " ", - "exclude_modes": ["tab", "buf", "buf_nc"], - "priority": 5 - }, - { - "function": "bufnr", - "after": " ", - "exclude_modes": ["tab", "buf", "tab_nc"], "priority": 5 }, { @@ -31,12 +25,33 @@ }, { "function": "tab_modified_indicator", - "exclude_modes": ["buf", "buf_nc"], + "priority": 5 + } + ] + }, + { + "type": "segment_list", + "function": "powerline.listers.vim.bufferlister", + "include_function": "single_tab", + "segments": [ + { + "function": "bufnr", + "after": " ", "priority": 5 }, + { + "function": "file_directory", + "priority": 40 + }, + { + "function": "file_name", + "args": { + "display_no_file": true + }, + "priority": 10 + }, { "function": "modified_indicator", - "exclude_modes": ["tab", "tab_nc"], "priority": 5 } ] @@ -51,7 +66,18 @@ ], "right": [ { - "function": "single_tab" + "type": "string", + "contents": "Bufs", + "name": "single_tab", + "highlight_group": ["single_tab"], + "include_function": "single_tab" + }, + { + "type": "string", + "contents": "Tabs", + "name": "many_tabs", + "highlight_group": ["many_tabs"], + "exclude_function": "single_tab" } ] } diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index bd24e07a..c3a1c9b2 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -110,11 +110,3 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): or int(vim_getbufoption(buf_segment_info, 'buflisted')) ) ) - - -@requires_segment_info -def tabbuflister(**kwargs): - if len(list_tabpages()) == 1: - return bufferlister(**kwargs) - else: - return tablister(**kwargs) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 4200b12b..ba007c83 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -14,7 +14,6 @@ except ImportError: from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, buffer_name, vim_getwinvar, register_buffer_cache, current_tabpage, - list_tabpages, list_tabpage_buffers_segment_info) from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group @@ -622,29 +621,3 @@ def winnr(pl, segment_info, show_current=False): winnr = segment_info['winnr'] if show_current or winnr != vim.current.window.number: return str(winnr) - - -def single_tab(pl, single_text='Bufs', multiple_text='Tabs'): - '''Show one text if there is only one tab and another if there are many - - Mostly useful for tabline to indicate what kind of data is shown there. - - :param str single_text: - Text displayed when there is only one tabpage. May be None if you do not - want to display anything. - :param str multiple_text: - Text displayed when there is more then one tabpage. May be None if you - do not want to display anything. - - Highlight groups used: ``single_tab``, ``many_tabs``. - ''' - if len(list_tabpages()) == 1: - return single_text and [{ - 'contents': single_text, - 'highlight_group': ['single_tab'], - }] - else: - return multiple_text and [{ - 'contents': multiple_text, - 'highlight_group': ['many_tabs'], - }] diff --git a/powerline/selectors/__init__.py b/powerline/selectors/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/powerline/selectors/vim.py b/powerline/selectors/vim.py new file mode 100644 index 00000000..d111de95 --- /dev/null +++ b/powerline/selectors/vim.py @@ -0,0 +1,10 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.bindings.vim import list_tabpages + + +def single_tab(pl, segment_info, mode): + '''Returns True if Vim has only one tab opened + ''' + return len(list_tabpages()) == 1 diff --git a/tests/test_segments.py b/tests/test_segments.py index fd010022..67dc1cac 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -1076,19 +1076,6 @@ class TestVim(TestCase): self.assertEqual(self.vim.winnr(pl=pl, segment_info=segment_info, show_current=True), str(segment_info['winnr'])) self.assertEqual(self.vim.winnr(pl=pl, segment_info=segment_info, show_current=False), None) - def test_single_tab(self): - pl = Pl() - single_tab = partial(vim.single_tab, pl=pl) - with vim_module._with('tabpage'): - self.assertEqual(single_tab(), [{'highlight_group': ['many_tabs'], 'contents': 'Tabs'}]) - self.assertEqual(single_tab(single_text='s', multiple_text='m'), [{'highlight_group': ['many_tabs'], 'contents': 'm'}]) - self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['many_tabs'], 'contents': 'm'}]) - self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['many_tabs'], 'contents': 'Tabs'}]) - self.assertEqual(single_tab(), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}]) - self.assertEqual(single_tab(single_text='s', multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 's'}]) - self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}]) - self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['single_tab'], 'contents': 's'}]) - def test_segment_info(self): pl = Pl() with vim_module._with('tabpage'): diff --git a/tests/test_selectors.py b/tests/test_selectors.py new file mode 100644 index 00000000..a127ae9e --- /dev/null +++ b/tests/test_selectors.py @@ -0,0 +1,36 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import sys + +from functools import partial + +import tests.vim as vim_module + +from tests.lib import Pl +from tests import TestCase + + +class TestVim(TestCase): + def test_single_tab(self): + pl = Pl() + single_tab = partial(self.vim.single_tab, pl=pl, segment_info=None, mode=None) + with vim_module._with('tabpage'): + self.assertEqual(single_tab(), False) + self.assertEqual(single_tab(), True) + + @classmethod + def setUpClass(cls): + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) + from powerline.selectors import vim + cls.vim = vim + + @classmethod + def tearDownClass(cls): + sys.path.pop(0) + + +if __name__ == '__main__': + from tests import main + main() From 786543c977647915b9d5e5fa880ce8373fd67411 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 20:58:13 +0400 Subject: [PATCH 1456/1472] Filter None values when computing subsegments for segment listers --- powerline/segment.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index f7a8f1a1..7fc54650 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -321,8 +321,11 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge if segment_type == 'segment_list': # Handle startup and shutdown of _contents_func? subsegments = [ - get(subsegment, side) - for subsegment in segment['segments'] + subsegment + for subsegment in ( + get(subsegment, side) + for subsegment in segment['segments'] + ) if subsegment ] return { 'name': name or function_name, From adc08d0cd8d9606d72d68b66d4245acc7c4498d4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 20:59:45 +0400 Subject: [PATCH 1457/1472] Filter subsegments for segment lister prior to using lister --- powerline/segment.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/powerline/segment.py b/powerline/segment.py index 7fc54650..96fd2652 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -102,6 +102,11 @@ def get_attr_func(contents_func, key, args, is_space_func=False): def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colorscheme, lister, subsegments, patcher_args): + subsegments = [ + subsegment + for subsegment in subsegments + if subsegment['display_condition'](pl, segment_info, mode) + ] for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) old_pslen = len(parsed_segments) @@ -112,17 +117,13 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors if subsegment_update['priority_multiplier'] and subsegment['priority']: subsegment['priority'] *= subsegment_update['priority_multiplier'] - subsegment_mode = subsegment_update.get('mode') - if subsegment_mode and not subsegment['display_condition'](pl, segment_info, subsegment_mode): - continue - process_segment( pl, side, subsegment_info, parsed_segments, subsegment, - subsegment_mode or mode, + subsegment_update.get('mode', mode), colorscheme, ) new_pslen = len(parsed_segments) From 667cd4bce58beea9a7ef92f389d5285a068e2cc6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 21:21:46 +0400 Subject: [PATCH 1458/1472] Assume highlight group is always an iterable According to the documentation and linter `'highlight_group': 'string'` was not correct even at the current stage, but it worked. This commit fixes this situation. --- powerline/colorscheme.py | 12 ++---------- powerline/segment.py | 2 +- powerline/segments/common.py | 4 ++-- powerline/segments/shell.py | 12 +++++++++--- tests/test_segments.py | 12 ++++++------ 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 4ae8d74f..8d718863 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -32,14 +32,6 @@ def pick_gradient_value(grad_list, gradient_level): return grad_list[int(round(gradient_level * (len(grad_list) - 1) / 100))] -def hl_iter(value): - if type(value) is list: - for v in value: - yield v - else: - yield value - - class Colorscheme(object): def __init__(self, colorscheme_config, colors_config): '''Initialize a colorscheme.''' @@ -105,12 +97,12 @@ class Colorscheme(object): def get_highlighting(self, groups, mode, gradient_level=None): trans = self.translations.get(mode, {}) - for group in hl_iter(groups): + for group in groups: group_props = self.get_group_props(mode, trans, group) if group_props: break else: - raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(hl_iter(groups))) + raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups)) if gradient_level is None: pick_color = self.colors.__getitem__ diff --git a/powerline/segment.py b/powerline/segment.py index 96fd2652..0f19f786 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -142,7 +142,7 @@ def set_segment_highlighting(pl, colorscheme, segment): ) if segment['divider_highlight_group']: segment['divider_highlight'] = colorscheme.get_highlighting( - segment['divider_highlight_group'], + (segment['divider_highlight_group'],), segment['mode'] ) else: diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 144e9596..bb83ea89 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -750,7 +750,7 @@ def user(pl, segment_info=None, hide_user=None): euid = _geteuid() return [{ 'contents': username, - 'highlight_group': 'user' if euid != 0 else ['superuser', 'user'], + 'highlight_group': ['user'] if euid != 0 else ['superuser', 'user'], }] if 'psutil' not in globals(): user = requires_segment_info(user) @@ -966,7 +966,7 @@ class EmailIMAPSegment(KwThreadedSegment): elif type(unread_count) != int or not max_msgs: return [{ 'contents': str(unread_count), - 'highlight_group': 'email_alert', + 'highlight_group': ['email_alert'], }] else: return [{ diff --git a/powerline/segments/shell.py b/powerline/segments/shell.py index e7e71f19..f1ac2845 100644 --- a/powerline/segments/shell.py +++ b/powerline/segments/shell.py @@ -29,7 +29,7 @@ def last_status(pl, segment_info): ''' if not segment_info['args'].last_exit_code: return None - return [{'contents': str(segment_info['args'].last_exit_code), 'highlight_group': 'exit_fail'}] + return [{'contents': str(segment_info['args'].last_exit_code), 'highlight_group': ['exit_fail']}] @requires_segment_info @@ -40,8 +40,14 @@ def last_pipe_status(pl, segment_info): ''' last_pipe_status = segment_info['args'].last_pipe_status if any(last_pipe_status): - return [{'contents': str(status), 'highlight_group': 'exit_fail' if status else 'exit_success', 'draw_inner_divider': True} - for status in last_pipe_status] + return [ + { + 'contents': str(status), + 'highlight_group': ['exit_fail' if status else 'exit_success'], + 'draw_inner_divider': True + } + for status in last_pipe_status + ] else: return None diff --git a/tests/test_segments.py b/tests/test_segments.py index 67dc1cac..2cc6545b 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -30,7 +30,7 @@ class TestShell(TestCase): pl = Pl() segment_info = {'args': Args(last_exit_code=10)} self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), [ - {'contents': '10', 'highlight_group': 'exit_fail'} + {'contents': '10', 'highlight_group': ['exit_fail']} ]) segment_info['args'].last_exit_code = 0 self.assertEqual(shell.last_status(pl=pl, segment_info=segment_info), None) @@ -45,9 +45,9 @@ class TestShell(TestCase): self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), None) segment_info['args'].last_pipe_status = [0, 2, 0] self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=segment_info), [ - {'contents': '0', 'highlight_group': 'exit_success', 'draw_inner_divider': True}, - {'contents': '2', 'highlight_group': 'exit_fail', 'draw_inner_divider': True}, - {'contents': '0', 'highlight_group': 'exit_success', 'draw_inner_divider': True} + {'contents': '0', 'highlight_group': ['exit_success'], 'draw_inner_divider': True}, + {'contents': '2', 'highlight_group': ['exit_fail'], 'draw_inner_divider': True}, + {'contents': '0', 'highlight_group': ['exit_success'], 'draw_inner_divider': True} ]) def test_jobnum(self): @@ -335,10 +335,10 @@ class TestCommon(TestCase): with replace_attr(common, 'psutil', new_psutil): with replace_attr(common, '_geteuid', lambda: 5): self.assertEqual(common.user(pl=pl, segment_info=segment_info), [ - {'contents': 'def', 'highlight_group': 'user'} + {'contents': 'def', 'highlight_group': ['user']} ]) self.assertEqual(common.user(pl=pl, segment_info=segment_info, hide_user='abc'), [ - {'contents': 'def', 'highlight_group': 'user'} + {'contents': 'def', 'highlight_group': ['user']} ]) self.assertEqual(common.user(pl=pl, segment_info=segment_info, hide_user='def'), None) with replace_attr(common, '_geteuid', lambda: 0): From b7504c7178aa4a4d36827cb8697c3c407db4690c Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 22:08:36 +0400 Subject: [PATCH 1459/1472] Do not require using `priority_multiplier` key --- powerline/segment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segment.py b/powerline/segment.py index 0f19f786..7c11ed8d 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -114,7 +114,7 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors if subsegment_update: subsegment = subsegment.copy() subsegment.update(subsegment_update) - if subsegment_update['priority_multiplier'] and subsegment['priority']: + if 'priority_multiplier' in subsegment_update and subsegment['priority']: subsegment['priority'] *= subsegment_update['priority_multiplier'] process_segment( From 1a5bbbea9634e848211ee393c197ff98d90f48f0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 4 Sep 2014 22:20:21 +0400 Subject: [PATCH 1460/1472] Replace segment-local modes with highlight_group_prefix option Fixes #1046 --- docs/source/develop/listers.rst | 5 --- docs/source/develop/segments.rst | 9 ++-- .../colorschemes/vim/__main__.json | 11 ++++- .../colorschemes/vim/default.json | 42 ++----------------- .../colorschemes/vim/solarized.json | 20 --------- .../colorschemes/vim/solarizedlight.json | 20 --------- powerline/listers/vim.py | 30 +++++++------ powerline/segment.py | 27 ++++++------ powerline/theme.py | 1 - tests/test_tabline.vim | 6 +-- 10 files changed, 51 insertions(+), 120 deletions(-) diff --git a/docs/source/develop/listers.rst b/docs/source/develop/listers.rst index 85513718..37917fe2 100644 --- a/docs/source/develop/listers.rst +++ b/docs/source/develop/listers.rst @@ -40,11 +40,6 @@ resulting segment. In addition to :ref:`usual keys that describe segment ` the following keys may be present (it is advised that *only* the following keys will be used): -.. _dev-listers-mode: - -``mode`` - Segment-specific mode. Used to alter segment highlighting. - ``priority_multiplier`` Value (usually a ``float``) used to multiply segment priority. It is useful for finer-grained controlling which segments disappear first: e.g. when diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 55838ae7..6454caa8 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -228,6 +228,12 @@ Segment dictionary contains the following keys: ``highlight_group``, ``divider_highlight_group`` Used highlight groups. May be ``None``. + ``highlight_group_prefix`` + If this key is present then given prefix will be prepended to each highlight + group (both regular and divider) used by this segment in a form + ``{prefix}:{group}`` (note the colon). This key is mostly useful for + :ref:`segment listers `. + .. _dev-segments-seg-around: ``before``, ``after`` @@ -281,9 +287,6 @@ Segment dictionary contains the following keys: ``shutdown`` :ref:`Shutdown function `. Accepts no argument. - ``mode`` - :ref:`Segment-specific mode `. May be ``None``. - Segments layout =============== diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index e7d995b8..6adb61ce 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -17,6 +17,15 @@ "many_tabs": "line_current", "bufnr": "file_directory", "winnr": "information:unimportant", - "tabnr": "file_directory" + "tabnr": "file_directory", + + "tab_nc:file_directory": "information:unimportant", + "tab_nc:file_name": "tab_nc:file_directory", + "tab_nc:tabnr": "tab_nc:file_directory", + + "buf_nc:file_directory": "tab_nc:file_directory", + "buf_nc:file_name": "tab_nc:file_name", + "buf_nc:bufnr": "tab_nc:tabnr", + "buf_nc:modified_indicator": "tab_nc:modified_indicator" } } diff --git a/powerline/config_files/colorschemes/vim/default.json b/powerline/config_files/colorschemes/vim/default.json index 53583b2e..dd62fdb0 100644 --- a/powerline/config_files/colorschemes/vim/default.json +++ b/powerline/config_files/colorschemes/vim/default.json @@ -30,7 +30,9 @@ "attached_clients": { "fg": "gray8", "bg": "gray2", "attr": [] }, "error": { "fg": "brightestred", "bg": "darkred", "attr": ["bold"] }, "warning": { "fg": "brightyellow", "bg": "darkorange", "attr": ["bold"] }, - "current_tag": { "fg": "gray9", "bg": "gray2", "attr": [] } + "current_tag": { "fg": "gray9", "bg": "gray2", "attr": [] }, + + "tab_nc:modified_indicator": { "fg": "brightyellow", "bg": "gray2", "attr": ["bold"] } }, "mode_translations": { "nc": { @@ -52,44 +54,6 @@ "dark_green_gray": "gray5" } }, - "tab_nc": { - "colors": { - "brightyellow": "darkorange", - "brightestred": "darkred", - "gray0": "gray0", - "gray1": "gray0", - "gray2": "gray0", - "gray3": "gray1", - "gray4": "gray1", - "gray5": "gray1", - "gray6": "gray1", - "gray7": "gray4", - "gray8": "gray4", - "gray9": "gray4", - "gray10": "gray5", - "white": "gray6", - "dark_green_gray": "gray5" - } - }, - "buf_nc": { - "colors": { - "brightyellow": "darkorange", - "brightestred": "darkred", - "gray0": "gray0", - "gray1": "gray0", - "gray2": "gray0", - "gray3": "gray1", - "gray4": "gray1", - "gray5": "gray1", - "gray6": "gray1", - "gray7": "gray4", - "gray8": "gray4", - "gray9": "gray4", - "gray10": "gray5", - "white": "gray6", - "dark_green_gray": "gray5" - } - }, "i": { "colors": { "gray0": "darkestblue", diff --git a/powerline/config_files/colorschemes/vim/solarized.json b/powerline/config_files/colorschemes/vim/solarized.json index 63038fbc..b0fa497d 100644 --- a/powerline/config_files/colorschemes/vim/solarized.json +++ b/powerline/config_files/colorschemes/vim/solarized.json @@ -44,26 +44,6 @@ "solarized:base3": "solarized:base1" } }, - "tab_nc": { - "colors": { - "solarized:base01": "solarized:base02", - "solarized:base00": "solarized:base02", - "solarized:base0": "solarized:base01", - "solarized:base1": "solarized:base00", - "solarized:base2": "solarized:base0", - "solarized:base3": "solarized:base1" - } - }, - "buf_nc": { - "colors": { - "solarized:base01": "solarized:base02", - "solarized:base00": "solarized:base02", - "solarized:base0": "solarized:base01", - "solarized:base1": "solarized:base00", - "solarized:base2": "solarized:base0", - "solarized:base3": "solarized:base1" - } - }, "i": { "groups": { "background": { "fg": "solarized:base3", "bg": "solarized:base01", "attr": [] }, diff --git a/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline/config_files/colorschemes/vim/solarizedlight.json index da38bb88..f6c1e178 100644 --- a/powerline/config_files/colorschemes/vim/solarizedlight.json +++ b/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -43,26 +43,6 @@ "solarized:base03": "solarized:base1" } }, - "tab_nc": { - "colors": { - "solarized:base2": "solarized:base01", - "solarized:base0": "solarized:base01", - "solarized:base00": "solarized:base2", - "solarized:base1": "solarized:base0", - "solarized:base02": "solarized:base00", - "solarized:base03": "solarized:base1" - } - }, - "buf_nc": { - "colors": { - "solarized:base2": "solarized:base01", - "solarized:base0": "solarized:base01", - "solarized:base00": "solarized:base2", - "solarized:base1": "solarized:base0", - "solarized:base02": "solarized:base00", - "solarized:base03": "solarized:base1" - } - }, "i": { "groups": { "background": { "fg": "solarized:base03", "bg": "solarized:base2", "attr": [] }, diff --git a/powerline/listers/vim.py b/powerline/listers/vim.py index c3a1c9b2..c1995957 100644 --- a/powerline/listers/vim.py +++ b/powerline/listers/vim.py @@ -10,7 +10,7 @@ except ImportError: vim = {} -def tabpage_updated_segment_info(segment_info, tabpage, mode): +def tabpage_updated_segment_info(segment_info, tabpage): segment_info = segment_info.copy() window = tabpage.window buffer = window.buffer @@ -22,7 +22,6 @@ def tabpage_updated_segment_info(segment_info, tabpage, mode): window_id=int(window.vars.get('powerline_window_id', -1)), buffer=buffer, bufnr=buffer.number, - mode=mode, ) return segment_info @@ -35,8 +34,7 @@ def tablister(pl, segment_info, **kwargs): ``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local ones and additional ``tabpage`` and ``tabnr`` keys. - Sets segment ``mode`` to either ``tab`` (for current tab page) or ``tab_nc`` - (for all other tab pages). + Adds either ``tab:`` or ``tab_nc:`` prefix to all segment highlight groups. Works best with vim-7.4 or later: earlier versions miss tabpage object and thus window objects are not available as well. @@ -49,15 +47,15 @@ def tablister(pl, segment_info, **kwargs): return dct return ( - (lambda tabpage, mode: ( - tabpage_updated_segment_info(segment_info, tabpage, mode), - add_multiplier(tabpage, {'mode': mode}) + (lambda tabpage, prefix: ( + tabpage_updated_segment_info(segment_info, tabpage), + add_multiplier(tabpage, {'highlight_group_prefix': prefix}) ))(tabpage, 'tab' if tabpage == cur_tabpage else 'tab_nc') for tabpage in list_tabpages() ) -def buffer_updated_segment_info(segment_info, buffer, mode): +def buffer_updated_segment_info(segment_info, buffer): segment_info = segment_info.copy() segment_info.update( window=None, @@ -65,7 +63,6 @@ def buffer_updated_segment_info(segment_info, buffer, mode): window_id=None, buffer=buffer, bufnr=buffer.number, - mode=mode, ) return segment_info @@ -78,8 +75,7 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and ``window_id`` keys set to None. - Sets segment ``mode`` to either ``buf`` (for current buffer) or ``buf_nc`` - (for all other buffers). + Adds either ``buf:`` or ``buf_nc:`` prefix to all segment highlight groups. :param bool show_unlisted: True if unlisted buffers should be shown as well. Current buffer is @@ -95,12 +91,14 @@ def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): return ( ( buf_segment_info, - add_multiplier(buf_segment_info['buffer'], {'mode': buf_segment_info['mode']}) + add_multiplier(buf_segment_info['buffer'], {'highlight_group_prefix': prefix}) ) - for buf_segment_info in ( - buffer_updated_segment_info( - segment_info, - buffer, + for buf_segment_info, prefix in ( + ( + buffer_updated_segment_info( + segment_info, + buffer + ), ('buf' if buffer is cur_buffer else 'buf_nc') ) for buffer in vim.buffers diff --git a/powerline/segment.py b/powerline/segment.py index 7c11ed8d..abcbe8a4 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -123,7 +123,7 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors subsegment_info, parsed_segments, subsegment, - subsegment_update.get('mode', mode), + mode, colorscheme, ) new_pslen = len(parsed_segments) @@ -133,17 +133,23 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors return None -def set_segment_highlighting(pl, colorscheme, segment): +def set_segment_highlighting(pl, colorscheme, segment, mode): + try: + highlight_group_prefix = segment['highlight_group_prefix'] + except KeyError: + hl_groups = lambda hlgs: hlgs + else: + hl_groups = lambda hlgs: [highlight_group_prefix + ':' + hlg for hlg in hlgs] + hlgs try: segment['highlight'] = colorscheme.get_highlighting( - segment['highlight_group'], - segment['mode'], + hl_groups(segment['highlight_group']), + mode, segment.get('gradient_level') ) if segment['divider_highlight_group']: segment['divider_highlight'] = colorscheme.get_highlighting( - (segment['divider_highlight_group'],), - segment['mode'] + hl_groups([segment['divider_highlight_group']]), + mode ) else: segment['divider_highlight'] = None @@ -156,7 +162,6 @@ def set_segment_highlighting(pl, colorscheme, segment): def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colorscheme): segment = segment.copy() - segment['mode'] = mode pl.prefix = segment['name'] if segment['type'] in ('function', 'segment_list'): try: @@ -202,14 +207,14 @@ def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colo if draw_inner_divider is not None: segment_copy['draw_soft_divider'] = draw_inner_divider draw_inner_divider = segment_copy.pop('draw_inner_divider', None) - if set_segment_highlighting(pl, colorscheme, segment_copy): + if set_segment_highlighting(pl, colorscheme, segment_copy, mode): append(segment_copy) else: segment['contents'] = contents - if set_segment_highlighting(pl, colorscheme, segment): + if set_segment_highlighting(pl, colorscheme, segment, mode): parsed_segments.append(segment) elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): - if set_segment_highlighting(pl, colorscheme, segment): + if set_segment_highlighting(pl, colorscheme, segment, mode): parsed_segments.append(segment) @@ -356,7 +361,6 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'truncate': None, 'startup': None, 'shutdown': None, - 'mode': None, '_rendered_raw': '', '_rendered_hl': '', '_len': None, @@ -405,7 +409,6 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'truncate': truncate_func, 'startup': startup_func, 'shutdown': shutdown_func, - 'mode': None, '_rendered_raw': '', '_rendered_hl': '', '_len': None, diff --git a/powerline/theme.py b/powerline/theme.py index 32a8aeee..6bfc9b45 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -135,7 +135,6 @@ class Theme(object): for side in [side] if side else ['left', 'right']: parsed_segments = [] for segment in self.segments[line][side]: - # No segment-local modes at this point if segment['display_condition'](self.pl, segment_info, mode): process_segment( self.pl, diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 9d829ea6..66b60a49 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -16,7 +16,7 @@ catch cquit endtry -if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#abc %#Pl_247_10395294_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#2 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' call writefile(['Unexpected tabline', result], 'message.fail') cquit endif @@ -30,7 +30,7 @@ catch cquit endtry -if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#abc %#Pl_247_10395294_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#2 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (2)', result], 'message.fail') cquit endif @@ -42,7 +42,7 @@ catch call writefile(['Exception while evaluating &tabline (3)', v:exception], 'message.fail') endtry -if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#abc %#Pl_247_10395294_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#2 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (3)', result], 'message.fail') cquit endif From d569c0b3dd05dc12a9a2e54a4cbe683c626e7eec Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 08:13:35 +0400 Subject: [PATCH 1461/1472] Skip identical highlighting Fixes #1049 --- powerline/renderer.py | 58 ++++++++++++++++++-------------------- powerline/renderers/vim.py | 11 +++++++- tests/test_tabline.vim | 6 ++-- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 2e97e16f..9a6ae6b2 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -360,50 +360,46 @@ class Renderer(object): contents_highlighted = '' draw_divider = segment['draw_' + divider_type + '_divider'] - # Pad segments first + contents_raw = contents_raw.translate(self.np_character_translations) + + # XXX Make sure self.hl() calls are called in the same order + # segments are displayed. This is needed for Vim renderer to work. if draw_divider: divider_raw = self.escape(theme.get_divider(side, divider_type)) if side == 'left': contents_raw = outer_padding + contents_raw + (divider_spaces * ' ') else: contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding + + if divider_type == 'soft': + divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' + divider_fg = segment[divider_highlight_group_key]['fg'] + divider_bg = segment[divider_highlight_group_key]['bg'] + else: + divider_fg = segment['highlight']['bg'] + divider_bg = compare_segment['highlight']['bg'] + + if side == 'left': + if render_highlighted: + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + segment['_rendered_raw'] = contents_raw + divider_raw + segment['_rendered_hl'] = contents_highlighted + divider_highlighted + else: + if render_highlighted: + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + segment['_rendered_raw'] = divider_raw + contents_raw + segment['_rendered_hl'] = divider_highlighted + contents_highlighted else: if side == 'left': contents_raw = outer_padding + contents_raw else: contents_raw = contents_raw + outer_padding - # Replace spaces with no-break spaces - contents_raw = contents_raw.translate(self.np_character_translations) - - # Apply highlighting to padded dividers and contents - if render_highlighted: - if draw_divider: - if divider_type == 'soft': - divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' - divider_fg = segment[divider_highlight_group_key]['fg'] - divider_bg = segment[divider_highlight_group_key]['bg'] - else: - divider_fg = segment['highlight']['bg'] - divider_bg = compare_segment['highlight']['bg'] - divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) - - # Append padded raw and highlighted segments to the rendered segment variables - if draw_divider: - if side == 'left': - segment['_rendered_raw'] = contents_raw + divider_raw - segment['_rendered_hl'] = contents_highlighted + divider_highlighted - else: - segment['_rendered_raw'] = divider_raw + contents_raw - segment['_rendered_hl'] = divider_highlighted + contents_highlighted - else: - if side == 'left': - segment['_rendered_raw'] = contents_raw - segment['_rendered_hl'] = contents_highlighted - else: - segment['_rendered_raw'] = contents_raw - segment['_rendered_hl'] = contents_highlighted + segment['_rendered_raw'] = contents_raw + segment['_rendered_hl'] = contents_highlighted yield segment def escape(self, string): diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 4d97fe2f..9cb806f7 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -40,6 +40,7 @@ class VimRenderer(Renderer): kwargs['ambigious'] = 2 super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} + self.prev_highlight = None def shutdown(self): self.theme.shutdown() @@ -126,7 +127,15 @@ class VimRenderer(Renderer): False, the argument is reset to the terminal defaults. If an argument is a valid color or attribute, it's added to the vim highlight group. ''' - # We don't need to explicitly reset attributes in vim, so skip those calls + # In order not to hit E541 two consequent identical highlighting + # specifiers may be squashed into one. + attr = attr or 0 # Normalize `attr` + if (fg, bg, attr) == self.prev_highlight: + return '' + self.prev_highlight = (fg, bg, attr) + + # We don't need to explicitly reset attributes in vim, so skip those + # calls if not attr and not bg and not fg: return '' diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 66b60a49..785bb46c 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -16,7 +16,7 @@ catch cquit endtry -if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#abc %#Pl_247_10395294_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#2 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' call writefile(['Unexpected tabline', result], 'message.fail') cquit endif @@ -30,7 +30,7 @@ catch cquit endtry -if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#abc %#Pl_247_10395294_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#2 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (2)', result], 'message.fail') cquit endif @@ -42,7 +42,7 @@ catch call writefile(['Exception while evaluating &tabline (3)', v:exception], 'message.fail') endtry -if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#abc %#Pl_247_10395294_236_3158064_NONE# %#Pl_247_10395294_236_3158064_NONE#2 %#Pl_247_10395294_236_3158064_NONE#./%#Pl_247_10395294_236_3158064_NONE#def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (3)', result], 'message.fail') cquit endif From 60fb311463d9815ffc8f5881ee30b6baabdc851a Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 08:35:20 +0400 Subject: [PATCH 1462/1472] Add a way to profile Vim bindings --- docs/source/develop/tips-and-tricks.rst | 21 +++++++++++++++++++ .../bindings/vim/autoload/powerline/debug.vim | 20 ++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 docs/source/develop/tips-and-tricks.rst create mode 100644 powerline/bindings/vim/autoload/powerline/debug.vim diff --git a/docs/source/develop/tips-and-tricks.rst b/docs/source/develop/tips-and-tricks.rst new file mode 100644 index 00000000..c850659c --- /dev/null +++ b/docs/source/develop/tips-and-tricks.rst @@ -0,0 +1,21 @@ +**************************************** +Tips and tricks for powerline developers +**************************************** + +Profiling powerline in Vim +========================== + +Given that current directory is the root of the powerline repository the +following command may be used: + +.. code-block:: sh + + vim --cmd 'let g:powerline_pyeval="powerline#debug#profile_pyeval"' \ + --cmd 'set rtp=powerline/bindings/vim' \ + -c 'runtime! plugin/powerline.vim' \ + {other arguments if needed} + +After some time run ``:WriteProfiling {filename}`` Vim command. Currently this +only works with recent Vim and python-2*. It should be easy to modify +:file:`powerline/bindings/vim/autoload/powerline/debug.vim` to suit other +needs. diff --git a/powerline/bindings/vim/autoload/powerline/debug.vim b/powerline/bindings/vim/autoload/powerline/debug.vim new file mode 100644 index 00000000..244319ac --- /dev/null +++ b/powerline/bindings/vim/autoload/powerline/debug.vim @@ -0,0 +1,20 @@ +python import cProfile +python powerline_pr = cProfile.Profile() + +function powerline#debug#profile_pyeval(s) + python powerline_pr.enable() + try + let ret = pyeval(a:s) + finally + python powerline_pr.disable() + endtry + return ret +endfunction + +function powerline#debug#write_profile(fname) + python import vim + python powerline_pr.dump_stats(vim.eval('a:fname')) + python powerline_pr = cProfile.Profile() +endfunction + +command -nargs=1 -complete=file WriteProfiling :call powerline#debug#write_profile() From f916fe819fcd7475b54b53abba56194fa0657fee Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 08:57:54 +0400 Subject: [PATCH 1463/1472] Use less precise algorythm for width computations under stress Makes #1047 far less bad --- powerline/renderer.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 9a6ae6b2..dccc8f15 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -266,20 +266,32 @@ class Renderer(object): for segment in chain(segments_priority, no_priority_segments): if segment['truncate'] is not None: segment['contents'] = segment['truncate'](self.pl, current_width - width, segment) - for segment in segments_priority: - if current_width <= width: - break - segments.remove(segment) + + segments_priority = iter(segments_priority) + if current_width > width and len(segments) > 100: + # When there are too many segments use faster, but less correct + # algorythm for width computation + diff = current_width - width + for segment in segments_priority: + segments.remove(segment) + diff -= segment['_len'] + if diff <= 0: + break current_width = self._render_length(theme, segments, divider_widths) + if current_width > width: + # When there are not too much use more precise, but much slower + # width computation. It also finishes computations in case + # previous variant did not free enough space. + for segment in segments_priority: + segments.remove(segment) + current_width = self._render_length(theme, segments, divider_widths) + if current_width <= width: + break + del segments_priority # Distribute the remaining space on spacer segments segments_spacers = [segment for segment in segments if segment['expand'] is not None] if segments_spacers: - if not segments_priority: - # Update segment['_len'] and current_width if not already done - # (is not done in shells where there is nothing to remove - # because “priority” key is not specified) - current_width = self._render_length(theme, segments, divider_widths) distribute_len, distribute_len_remainder = divmod(width - current_width, len(segments_spacers)) for segment in segments_spacers: segment['contents'] = ( From 42bd619866fb5262f4fc1e35e581ba7f47b25c5c Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 19:23:07 +0400 Subject: [PATCH 1464/1472] Remove `{module}.{function}` default highlighting group It is both not documented and not supported by linter in any case. --- powerline/segment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segment.py b/powerline/segment.py index abcbe8a4..edcda434 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -311,7 +311,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge pass if segment_type == 'function': - highlight_group = [module + '.' + function_name, function_name] + highlight_group = [function_name] else: highlight_group = segment.get('highlight_group') or name From 68263beec40b8ee8b41ed50b237f29a4466b45b0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 19:36:31 +0400 Subject: [PATCH 1465/1472] Show current buffer, window and tabpage number by default Fixes #1048 --- powerline/segments/vim/__init__.py | 6 +++--- tests/test_tabline.vim | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index ba007c83..9f59b7c4 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -584,7 +584,7 @@ def trailing_whitespace(pl, segment_info): @requires_segment_info -def tabnr(pl, segment_info, show_current=False): +def tabnr(pl, segment_info, show_current=True): '''Show tabpage number :param bool show_current: @@ -600,7 +600,7 @@ def tabnr(pl, segment_info, show_current=False): @requires_segment_info -def bufnr(pl, segment_info, show_current=False): +def bufnr(pl, segment_info, show_current=True): '''Show buffer number :param bool show_current: @@ -612,7 +612,7 @@ def bufnr(pl, segment_info, show_current=False): @requires_segment_info -def winnr(pl, segment_info, show_current=False): +def winnr(pl, segment_info, show_current=True): '''Show window number :param bool show_current: diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 785bb46c..2db32045 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -16,7 +16,7 @@ catch cquit endtry -if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#3 ./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                         %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Tabs ' call writefile(['Unexpected tabline', result], 'message.fail') cquit endif @@ -30,7 +30,7 @@ catch cquit endtry -if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#3 ./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                         %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (2)', result], 'message.fail') cquit endif @@ -42,7 +42,7 @@ catch call writefile(['Exception while evaluating &tabline (3)', v:exception], 'message.fail') endtry -if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' +if result isnot# '%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  2 ./def %#Pl_236_3158064_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#3 ./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                         %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' call writefile(['Unexpected tabline (3)', result], 'message.fail') cquit endif From c0459dbebe452eb796e42e0df592bbebb3a62431 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 20:27:52 +0400 Subject: [PATCH 1466/1472] Rename `beta` version to `dev` --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3c670819..d953241a 100755 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ else: setup( name='powerline-status', - version='beta', + version='dev', description='The ultimate statusline/prompt utility.', long_description=README, classifiers=[ From dc5969e8df710305f12642f5b8267c866b04dc4f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 20:33:26 +0400 Subject: [PATCH 1467/1472] Mention `powerline-status` PyPI package in installation documentation Fixes #1057 --- docs/source/installation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index f31e9264..c55fcdcc 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -54,6 +54,12 @@ and you will have to do something like:: (``git+https://github.com/Lokaltog/powerline``) protocols. ``git`` protocol should be the fastest, but least secure one though. +To install release version uploaded to PyPI use + +.. code-block:: + + pip install powerline-status + Fonts installation ================== From 51e778cd411b0411af2da1f5fb86d9d2519f7b6f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 20:36:09 +0400 Subject: [PATCH 1468/1472] Always use `code-block` to format code there --- docs/source/installation.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index c55fcdcc..9fa1b87f 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -33,16 +33,22 @@ Pip installation This project is currently unavailable from PyPI due to a naming conflict with an unrelated project, thus you will have to use the following command to install -powerline with ``pip``:: +powerline with ``pip``: + +.. code-block:: sh pip install --user git+git://github.com/Lokaltog/powerline -. You may also choose to clone powerline repository somewhere and use:: +. You may also choose to clone powerline repository somewhere and use + +.. code-block:: sh pip install -e --user {path_to_powerline} , but note that in this case ``pip`` will not install ``powerline`` executable -and you will have to do something like:: +and you will have to do something like + +.. code-block:: sh ln -s {path_to_powerline}/scripts/powerline ~/.local/bin @@ -56,7 +62,7 @@ and you will have to do something like:: To install release version uploaded to PyPI use -.. code-block:: +.. code-block:: sh pip install powerline-status From 4bcb0e154f4ece39aa84783d95b11dacffb0a3fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 20:39:22 +0400 Subject: [PATCH 1469/1472] Include tips-and-tricks for developers into develop.rst toctree --- docs/source/develop.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/develop.rst b/docs/source/develop.rst index 6ee44f27..bf454988 100644 --- a/docs/source/develop.rst +++ b/docs/source/develop.rst @@ -10,3 +10,4 @@ Developer guide develop/listers develop/local-themes develop/extensions + develop/tips-and-tricks From 9eceaf3f3fb23706d18f32042906c64c118a8327 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 20:39:40 +0400 Subject: [PATCH 1470/1472] Move docs/source/selectors* to the appropriate place --- docs/source/{ => configuration}/selectors.rst | 0 docs/source/{ => configuration}/selectors/vim.rst | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/source/{ => configuration}/selectors.rst (100%) rename docs/source/{ => configuration}/selectors/vim.rst (100%) diff --git a/docs/source/selectors.rst b/docs/source/configuration/selectors.rst similarity index 100% rename from docs/source/selectors.rst rename to docs/source/configuration/selectors.rst diff --git a/docs/source/selectors/vim.rst b/docs/source/configuration/selectors/vim.rst similarity index 100% rename from docs/source/selectors/vim.rst rename to docs/source/configuration/selectors/vim.rst From 41f573659b53079a3ccf36563b828d13ea1f301f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 20:40:09 +0400 Subject: [PATCH 1471/1472] Fix typo in label ID --- docs/source/configuration/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 0e8b384d..76931690 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -396,7 +396,7 @@ ascii Theme without any unicode characters at all ` options. List of lister segments is available in :ref:`Lister reference - ` section. + ` section. .. _config-themes-seg-name: From 05cb133dd4e0f289f222cf8097a77e958660f597 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 5 Sep 2014 22:13:51 +0400 Subject: [PATCH 1472/1472] =?UTF-8?q?Replace=20g:powerline=5Fconfig=5Fpath?= =?UTF-8?q?=20Vim=20variable=20with=20=E2=80=A6=5Fpaths=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is for consistency with other powerline bindings that now all do have a way to specify a list of paths. --- docs/source/configuration/local.rst | 8 ++++---- powerline/vim.py | 2 +- tests/test_configuration.py | 2 +- tests/test_local_overrides.vim | 2 +- tests/test_plugin_file.vim | 2 +- tests/test_provided_config_files.py | 2 +- tests/test_tabline.vim | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index c66b5c78..385bdfcf 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -22,10 +22,10 @@ Vim configuration can be overridden using the following options: redefine some value (e.g. segment) in list, only the whole list itself: only dictionaries are merged recursively. -``g:powerline_config_path`` - Path (must be expanded, ``~`` shortcut is not supported). Points to the - directory which will be searched for configuration. When this option is - present, none of the other locations are searched. +``g:powerline_config_paths`` + Paths list (each path must be expanded, ``~`` shortcut is not supported). + Points to the list of directories which will be searched for configuration. + When this option is present, none of the other locations are searched. ``g:powerline_no_python_error`` If this variable is set to a true value it will prevent Powerline from reporting diff --git a/powerline/vim.py b/powerline/vim.py index ec754107..d0123707 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -119,7 +119,7 @@ class VimPowerline(Powerline): def get_config_paths(self): try: - return [vim_getvar('powerline_config_path')] + return vim_getvar('powerline_config_paths') except KeyError: return super(VimPowerline, self).get_config_paths() diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 714bba83..5172fb42 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -646,7 +646,7 @@ class TestVim(TestCase): from powerline.vim import VimPowerline import powerline as powerline_module import vim - vim.vars['powerline_config_path'] = '/' + vim.vars['powerline_config_paths'] = ['/'] with swap_attributes(config, powerline_module): with vim_module._with('environ', TEST='abc'): with get_powerline_raw(config, VimPowerline) as powerline: diff --git a/tests/test_local_overrides.vim b/tests/test_local_overrides.vim index ec902856..f6cf0931 100755 --- a/tests/test_local_overrides.vim +++ b/tests/test_local_overrides.vim @@ -1,6 +1,6 @@ #!/usr/bin/vim -S set encoding=utf-8 -let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' +let g:powerline_config_paths = [expand(':p:h:h') . '/powerline/config_files'] let g:powerline_config_overrides = {'common': {'default_top_theme': 'ascii'}} let g:powerline_theme_overrides__default = {'segment_data': {'line_current_symbol': {'contents': 'LN '}, 'branch': {'before': 'B '}}} diff --git a/tests/test_plugin_file.vim b/tests/test_plugin_file.vim index 27a85411..e139c44f 100755 --- a/tests/test_plugin_file.vim +++ b/tests/test_plugin_file.vim @@ -1,6 +1,6 @@ #!/usr/bin/vim -S set encoding=utf-8 -let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' +let g:powerline_config_paths = [expand(':p:h:h') . '/powerline/config_files'] tabedit abc tabedit def try diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 05dd68a9..381cd842 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -58,7 +58,7 @@ class TestVimConfig(TestCase): with vim_module._with('bufname', '/tmp/foo.txt'): out = powerline.render(vim_module.current.window, 1, vim_module.current.window.number, is_tabline=True) outputs[out] = (-1, (None, None), 'tab') - with vim_module._with('globals', powerline_config_path=cfg_path): + with vim_module._with('globals', powerline_config_paths=[cfg_path]): exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) try: for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']: diff --git a/tests/test_tabline.vim b/tests/test_tabline.vim index 2db32045..a47fafaf 100755 --- a/tests/test_tabline.vim +++ b/tests/test_tabline.vim @@ -1,6 +1,6 @@ #!/usr/bin/vim -S set encoding=utf-8 -let g:powerline_config_path = expand(':p:h:h') . '/powerline/config_files' +let g:powerline_config_paths = [expand(':p:h:h') . '/powerline/config_files'] source powerline/bindings/vim/plugin/powerline.vim edit abc tabedit def