Release 2.2

- Added support for newest psutil version.
- Added support for non-SSL IMAP4 connection.
- Added support for clickable tab names in Vim.
- Added support for truncating tmux segments.
- Added support for new (i3ipc) module that interacts with i3.
- Added support for i3 modes.
- Fixed coloring of network\_load segment.
- Fixed dash bindings on OS X.
- Fixed parsing numbers starting with 2 supplied by POWERLINE\_\*\_OVERRIDES
  environment variables.
This commit is contained in:
Foo 2015-06-27 01:51:31 +03:00
commit 029d54a64a
38 changed files with 632 additions and 224 deletions

View File

@ -15,20 +15,27 @@ written in Python.
Powerline provides default configurations in the following locations:
:ref:`Main configuration <config-main>`
:file:`powerline/config.json`
:file:`{powerline}/config.json`
:ref:`Colorschemes <config-colorschemes>`
:file:`powerline/colorschemes/{name}.json`,
:file:`powerline/colorscheme/{extension}/__main__.json`,
:file:`powerline/colorschemes/{extension}/{name}.json`
:file:`{powerline}/colorschemes/{name}.json`,
:file:`{powerline}/colorschemes/{extension}/__main__.json`,
:file:`{powerline}/colorschemes/{extension}/{name}.json`
:ref:`Themes <config-themes>`
:file:`powerline/themes/{top_theme}.json`,
:file:`powerline/themes/{extension}/__main__.json`,
:file:`powerline/themes/{extension}/default.json`
: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
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.
Here `{powerline}` is one of the following:
#. The default configuration directory located in the main package:
:file:`{powerline_root}/powerline/config_files`. May be absent in some
packages (e.g. when installing via Gentoo ebuilds).
#. If variable ``$XDG_CONFIG_DIRS`` is set and non-empty then to any
:file:`{directory}/powerline` where `{directory}` is a directory listed in
a colon-separated ``$XDG_CONFIG_DIRS`` list. Directories are checked in
reverse order.
#. User configuration directory located in :file:`$XDG_CONFIG_HOME/powerline`.
This usually corresponds to :file:`~/.config/powerline` on all platforms.
If per-instance configuration is needed please refer to :ref:`Local
configuration overrides <local-configuration-overrides>`.
@ -37,17 +44,8 @@ configuration overrides <local-configuration-overrides>`.
.. note::
Existing multiple configuration files that have the same name, but are placed
in different directories, 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.
in different directories, will be merged. Merging happens in the order given
in the above list of possible `{powerline}` meanings.
When merging configuration only dictionaries are merged and they are merged
recursively: keys from next file overrule those from the previous unless

View File

@ -502,7 +502,7 @@ ascii Theme without any unicode characters at all
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.
segments (i.e. with a greater priority number) being removed first.
.. _config-themes-seg-draw_divider:

View File

@ -153,10 +153,34 @@ All keys in segments returned by the function override those obtained from
Detailed description of used dictionary keys:
.. _dev-segments-contents:
``contents``
Text displayed by segment. Should be a ``unicode`` (Python2) or ``str``
(Python3) instance.
``literal_contents``
Text that needs to be output literally (i.e. without passing through
:py:meth:`powerline.renderer.strwidth` to determine length, through
:py:meth:`powerline.renderer.escape` to escape special characters and
through :py:meth:`powerline.renderer.hl` to highlight it). Should be a tuple
``(contents_length, contents)`` where ``contents_length`` is an integer and
``contents`` is a ``unicode`` (Python2) or ``str`` (Python3) instance.
If this key is present and its second value is true then other contents keys
(:ref:`contents <dev-segments-contents>`, :ref:`after
<config-themes-seg-after>`, :ref:`before <config-themes-seg-before>`) will
be ignored.
.. note::
If target is inclusion of the segment in powerline upstream all segment
functions that output *only* subsegments with ``literal_contents`` key
must contain the following string in documentation::
No highlight groups are used (literal segment).
String must be present on the separate line.
.. _dev-segments-draw_inner_divider:
``draw_hard_divider``, ``draw_soft_divider``, ``draw_inner_divider``

View File

@ -37,6 +37,19 @@ Generic requirements
Until mercurial and bazaar support Python-3 or PyPy powerline will not
support repository information when running in these interpreters.
.. _repository-root:
.. note::
When using ``pip`` ``{repository_root}`` directory referenced in
documentation may be found using ``pip show powerline-status``. In the output
of ``pip show`` there is a line like ``Location: {path}``, that ``{path}`` is
``{repository_root}``. Unless it is ``--editable`` installation this is only
applicable for ``{repository_root}/powerline/…`` paths: something like
``{repository_root}/scripts/powerline-render`` is not present.
When using other packages referenced paths may not exist, in this case refer
to package documentation.
Pip installation
================

View File

@ -25,7 +25,7 @@ If the repository was just cloned the following line needs to be added to the
set rtp+={repository_root}/powerline/bindings/vim
where ``{repository_root}`` is the absolute path to the Powerline installation
directory.
directory (see :ref:`repository root <repository-root>`).
If pathogen is used and Powerline functionality is not needed outside of Vim
then it is possible to simply add Powerline as a bundle and point the path above
@ -82,7 +82,8 @@ Tmux statusline
===============
Add the following lines to :file:`.tmux.conf`, where ``{repository_root}`` is
the absolute path to the Powerline installation directory::
the absolute path to the Powerline installation directory (see :ref:`repository
root <repository-root>`)::
source "{repository_root}/powerline/bindings/tmux/powerline.conf"

View File

@ -16,7 +16,8 @@ Bash prompt
===========
Add the following line to the :file:`bashrc`, where ``{repository_root}`` is the
absolute path to the Powerline installation directory:
absolute path to the Powerline installation directory (see :ref:`repository root
<repository-root>`):
.. code-block:: bash
@ -48,7 +49,8 @@ Zsh prompt
==========
Add the following line to the :file:`zshrc`, where ``{repository_root}`` is the
absolute path to the Powerline installation directory:
absolute path to the Powerline installation directory (see :ref:`repository root
<repository-root>`):
.. code-block:: bash
@ -58,7 +60,8 @@ Fish prompt
===========
Add the following line to :file:`config.fish`, where ``{repository_root}`` is
the absolute path to the Powerline installation directory:
the absolute path to the Powerline installation directory (see :ref:`repository
root <repository-root>`):
.. code-block:: bash
@ -77,9 +80,10 @@ many \*nix distributions. To use it add
. {repository_root}/powerline/bindings/rc/powerline.rc
to :file:`rcrc` file (usually :file:`~/.rcrc`) and make sure ``rc`` is started
as a login shell (with ``-l`` argument): otherwise this configuration file is
not read.
(``{repository_root}`` is the absolute path to the Powerline installation
directory, see :ref:`repository root <repository-root>`) to :file:`rcrc` file
(usually :file:`~/.rcrc`) and make sure ``rc`` is started as a login shell (with
``-l`` argument): otherwise this configuration file is not read.
.. warning::
Original Plan9 shell and its \*nix port are not supported because they are
@ -97,6 +101,9 @@ After launching busybox run the following command:
. {repository_root}/powerline/bindings/shell/powerline.sh
where ``{repository_root}`` is the absolute path to the Powerline installation
directory (see :ref:`repository root <repository-root>`).
Mksh users may put this line into ``~/.mkshrc`` file. Dash users may use the
following in ``~/.profile``:

View File

@ -11,7 +11,8 @@ Awesome widget
background and updates the statusline with ``awesome-client``.
Add the following to :file:`rc.lua`, where ``{repository_root}`` is the absolute
path to Powerline installation directory:
path to Powerline installation directory (see :ref:`repository root
<repository-root>`):
.. code-block:: lua
@ -51,8 +52,8 @@ Add the following to :file:`~/.config/qtile/config.py`:
.. _bar-usage:
LemonBoys bar
==============
bar-aint-recursive
==================
To run the bar simply pipe the output of the binding script into ``bar`` and
specify appropriate options, for example like this::
@ -63,6 +64,9 @@ to run with i3, simply ``exec`` this in i3 config file::
exec python /path/to/powerline/bindings/bar/powerline-bar.py --i3 | bar
Running the binding in i3-mode will require `i3ipc <https://github.com/acrisci/i3ipc-python>`_
(or the outdated `i3-py <https://github.com/ziberna/i3-py>`_).
See the `bar documentation <https://github.com/LemonBoy/bar>`_ for more
information and options.

View File

@ -5,11 +5,10 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import sys
import time
from threading import Lock
from threading import Lock, Timer
from argparse import ArgumentParser
from powerline import Powerline
from powerline.lib.monotonic import monotonic
from powerline.lib.encoding import get_unicode_writer
@ -20,14 +19,6 @@ class BarPowerline(Powerline):
super(BarPowerline, self).init(ext='wm', renderer_module='bar')
def render(event=None, data=None, sub=None):
global lock
with lock:
write(powerline.render())
write('\n')
sys.stdout.flush()
if __name__ == '__main__':
parser = ArgumentParser(description='Powerline BAR bindings.')
parser.add_argument(
@ -36,17 +27,37 @@ if __name__ == '__main__':
)
args = parser.parse_args()
powerline = BarPowerline()
interval = 0.5
lock = Lock()
modes = ['default']
write = get_unicode_writer(encoding='utf-8')
def render(reschedule=False):
if reschedule:
Timer(0.5, render, kwargs={'reschedule': True}).start()
global lock
with lock:
write(powerline.render(mode=modes[0]))
write('\n')
sys.stdout.flush()
def update(evt):
modes[0] = evt.change
render()
render(reschedule=True)
if args.i3:
import i3
sub = i3.Subscription(render, 'workspace')
try:
import i3ipc
except ImportError:
import i3
i3.Subscription(lambda evt, data, sub: print(render()), 'workspace')
else:
conn = i3ipc.Connection()
conn.on('workspace::focus', lambda conn, evt: render())
conn.on('mode', lambda conn, evt: update(evt))
conn.main()
while True:
start_time = monotonic()
render()
time.sleep(max(interval - (monotonic() - start_time), 0.1))
time.sleep(1e10)

View File

@ -66,7 +66,7 @@ _powerline_set_append_trap() {
_powerline_create_temp() {
if test -z "$_POWERLINE_TEMP" || ! test -e "$_POWERLINE_TEMP" ; then
_POWERLINE_TEMP="$(mktemp)"
_POWERLINE_TEMP="$(mktemp "${TMPDIR:-/tmp}/powerline.XXXXXXXX")"
_powerline_append_trap 'rm $_POWERLINE_TEMP' EXIT
fi
}
@ -74,7 +74,7 @@ _powerline_create_temp() {
_powerline_set_set_jobs() {
if _powerline_has_jobs_in_subshell "$@" ; then
_powerline_set_jobs() {
_POWERLINE_JOBS="$(jobs -p|wc -l)"
_POWERLINE_JOBS="$(jobs -p|wc -l|tr -d ' ')"
}
else
_powerline_set_append_trap "$@"
@ -90,7 +90,7 @@ _powerline_set_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)"
_POWERLINE_JOBS="$(wc -l < $_POWERLINE_TEMP | tr -d ' ')"
}
fi
_powerline_set_set_jobs() {

View File

@ -8,5 +8,5 @@ set -g window-status-format "#[$_POWERLINE_WINDOW_COLOR]$_POWERLINE_LEFT_HARD_DI
set -g window-status-current-format "#[$_POWERLINE_WINDOW_CURRENT_HARD_DIVIDER_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#[$_POWERLINE_WINDOW_CURRENT_COLOR]#I $_POWERLINE_LEFT_SOFT_DIVIDER#[$_POWERLINE_WINDOW_NAME_COLOR]#W #[$_POWERLINE_WINDOW_CURRENT_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER"
# Legacy status-left definition to be overwritten for tmux Versions 1.8+
set -g status-left "#[$_POWERLINE_SESSION_COLOR] #S #[$_POWERLINE_SESSION_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#(env \"\$POWERLINE_COMMAND\" tmux left)"
set -g status-left "#[$_POWERLINE_SESSION_COLOR] #S #[$_POWERLINE_SESSION_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#(env \"\$POWERLINE_COMMAND\" tmux left -R pane_id=`tmux display -p '#D'`)"
# vim: ft=tmux

View File

@ -0,0 +1,3 @@
set -g status-right '#(env "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS tmux right -R pane_id=`tmux display -p "#D"` --width=`tmux display -p "#{client_width}"` -R width_adjust=`tmux show-options -g status-left-length | cut -d" " -f 2`)'
set -g status-left "#[$_POWERLINE_SESSION_COLOR] #S #[$_POWERLINE_SESSION_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#(env \"\$POWERLINE_COMMAND\" tmux left --width=`tmux display -p '#{client_width}'` -R width_adjust=`tmux show-options -g status-right-length | cut -d' ' -f2` -R pane_id=`tmux display -p '#D'`)"
# vim: ft=tmux

View File

@ -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 -qg status-left "#{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_FG]#[bg=$_POWERLINE_SESSION_PREFIX_BG]#[$_POWERLINE_SESSION_PREFIX_ATTR],#[fg=$_POWERLINE_SESSION_FG]#[bg=$_POWERLINE_SESSION_BG]#[$_POWERLINE_SESSION_ATTR]} #S #{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_BG],#[fg=$_POWERLINE_SESSION_BG]}#[bg=$_POWERLINE_BACKGROUND_BG]#[nobold]$_POWERLINE_LEFT_HARD_DIVIDER#(env \$POWERLINE_COMMAND \$POWERLINE_COMMAND_ARGS tmux left)"
set -qg status-left "#{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_FG]#[bg=$_POWERLINE_SESSION_PREFIX_BG]#[$_POWERLINE_SESSION_PREFIX_ATTR],#[fg=$_POWERLINE_SESSION_FG]#[bg=$_POWERLINE_SESSION_BG]#[$_POWERLINE_SESSION_ATTR]} #S #{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_BG],#[fg=$_POWERLINE_SESSION_BG]}#[bg=$_POWERLINE_BACKGROUND_BG]#[nobold]$_POWERLINE_LEFT_HARD_DIVIDER#(env \$POWERLINE_COMMAND \$POWERLINE_COMMAND_ARGS tmux left --width=`tmux display -p '#{client_width}'` -R width_adjust=`tmux show-options -g status-right-length | cut -d' ' -f2` -R pane_id=`tmux display -p '#D'`)"
# vim: ft=tmux

View File

@ -27,6 +27,7 @@
"internal_ip": { "fg": "gray8", "bg": "gray0", "attrs": [] },
"network_load": { "fg": "gray8", "bg": "gray0", "attrs": [] },
"network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attrs": [] },
"network_load:divider": "background:divider",
"system_load": { "fg": "gray8", "bg": "gray0", "attrs": [] },
"system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attrs": [] },
"environment": { "fg": "gray8", "bg": "gray0", "attrs": [] },

View File

@ -21,6 +21,7 @@
"cwd:current_folder": "information:regular",
"cwd:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] },
"network_load": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] },
"network_load:divider": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] },
"hostname": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] },
"environment": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] },
"attached_clients": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] },

View File

@ -7,6 +7,9 @@
"function": "powerline.listers.vim.tablister",
"exclude_function": "single_tab",
"segments": [
{
"function": "tab"
},
{
"function": "tabnr",
"after": " ",
@ -29,6 +32,12 @@
}
]
},
{
"function": "tab",
"args": {
"end": true
}
},
{
"type": "segment_list",
"function": "powerline.listers.vim.bufferlister",

View File

@ -25,7 +25,7 @@ def parse_value(s):
'''
if not s:
return REMOVE_THIS_KEY
elif s[0] in '"{[0193456789-' or s in ('null', 'true', 'false'):
elif s[0] in '"{[0123456789-' or s in ('null', 'true', 'false'):
return json.loads(s)
else:
return s

View File

@ -198,10 +198,8 @@ args_spec = Spec(
segment_info=Spec().error('Segment info dictionary must be set by powerline').optional(),
).unknown_spec(Spec(), Spec()).optional().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(),
segment_spec_base = Spec(
name=Spec().re('^[a-zA-Z_]\w*$').optional(),
function=Spec().re(function_name_re).func(check_segment_function).optional(),
exclude_modes=Spec().list(vim_mode_spec()).optional(),
@ -230,10 +228,14 @@ segment_spec = Spec(
':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)
del sub_segments_spec
).func(check_full_segment_data).copy
subsegment_spec = segment_spec_base().update(
type=Spec().oneof(set((key for key in type_keys if key != 'segment_list'))).optional(),
)
segment_spec = segment_spec_base().update(
type=Spec().oneof(type_keys).optional(),
segments=Spec().optional().list(subsegment_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})'),

View File

@ -381,7 +381,10 @@ def check_segment_function(function_name, data, context, echoerr):
hl_groups = []
divider_hl_group = None
hadproblem = False
if func.__doc__:
NO_H_G_USED_STR = 'No highlight groups are used (literal segment).'
H_G_USED_STR = 'Highlight groups used: '
LHGUS = len(H_G_USED_STR)
D_H_G_USED_STR = 'Divider highlight group used: '
@ -391,6 +394,20 @@ def check_segment_function(function_name, data, context, echoerr):
for i, line in enumerate(func.__doc__.split('\n')):
if H_G_USED_STR in line:
idx = line.index(H_G_USED_STR) + LHGUS
if hl_groups is None:
idx -= LHGUS
mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx)
echoerr(
context='Error while checking theme (key {key})'.format(key=context.key),
context_mark=function_name.mark,
problem=(
'found highlight group definition in addition to sentense stating that '
'no highlight groups are used'
),
problem_mark=mark,
)
hadproblem = True
continue
hl_groups.append((
line[idx:],
(mark_name, i + 1, idx + 1, func.__doc__),
@ -400,10 +417,24 @@ def check_segment_function(function_name, data, context, echoerr):
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)
elif NO_H_G_USED_STR in line:
idx = line.index(NO_H_G_USED_STR)
if hl_groups:
mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx)
echoerr(
context='Error while checking theme (key {key})'.format(key=context.key),
context_mark=function_name.mark,
problem=(
'found sentense stating that no highlight groups are used '
'in addition to highlight group definition'
),
problem_mark=mark,
)
hadproblem = True
continue
hl_groups = None
pointer += len(line) + len('\n')
hadproblem = False
if divider_hl_group:
r = hl_exists(divider_hl_group, data, context, echoerr, allow_gradients=True)
if r:
@ -466,7 +497,7 @@ def check_segment_function(function_name, data, context, echoerr):
h[0], list_sep.join(r))
)
hadproblem = True
else:
elif hl_groups is not None:
r = hl_exists(function_name, data, context, echoerr, allow_gradients=True)
if r:
echoerr(

View File

@ -391,7 +391,10 @@ class Renderer(object):
segment['contents'] = translate_np(segment['contents'])
if calculate_contents_len:
for segment in segments:
segment['_contents_len'] = self.strwidth(segment['contents'])
if segment['literal_contents'][1]:
segment['_contents_len'] = segment['literal_contents'][0]
else:
segment['_contents_len'] = self.strwidth(segment['contents'])
def _render_length(self, theme, segments, divider_widths):
'''Update segments lengths and return them
@ -399,24 +402,52 @@ class Renderer(object):
segments_len = len(segments)
ret = 0
divider_spaces = theme.get_spaces()
prev_segment = theme.EMPTY_SEGMENT
try:
first_segment = next(iter((
segment
for segment in segments
if not segment['literal_contents'][1]
)))
except StopIteration:
first_segment = None
try:
last_segment = next(iter((
segment
for segment in reversed(segments)
if not segment['literal_contents'][1]
)))
except StopIteration:
last_segment = None
for index, segment in enumerate(segments):
side = segment['side']
segment_len = segment['_contents_len']
if not segment['literal_contents'][1]:
if side == 'left':
if segment is not last_segment:
compare_segment = next(iter((
segment
for segment in segments[index + 1:]
if not segment['literal_contents'][1]
)))
else:
compare_segment = theme.EMPTY_SEGMENT
else:
compare_segment = prev_segment
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'
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')
))
outer_padding = int(bool(
segment is first_segment
if side == 'left' else
segment is last_segment
))
draw_divider = segment['draw_' + divider_type + '_divider']
segment_len += outer_padding
if draw_divider:
segment_len += divider_widths[side][divider_type] + divider_spaces
draw_divider = segment['draw_' + divider_type + '_divider']
segment_len += outer_padding
if draw_divider:
segment_len += divider_widths[side][divider_type] + divider_spaces
prev_segment = segment
segment['_len'] = segment_len
ret += segment_len
@ -435,61 +466,92 @@ class Renderer(object):
'''
segments_len = len(segments)
divider_spaces = theme.get_spaces()
prev_segment = theme.EMPTY_SEGMENT
try:
first_segment = next(iter((
segment
for segment in segments
if not segment['literal_contents'][1]
)))
except StopIteration:
first_segment = None
try:
last_segment = next(iter((
segment
for segment in reversed(segments)
if not segment['literal_contents'][1]
)))
except StopIteration:
last_segment = None
for index, segment in enumerate(segments):
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 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_highlighted = ''
contents_raw = segment['contents']
contents_highlighted = ''
draw_divider = segment['draw_' + divider_type + '_divider']
# 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 not segment['literal_contents'][1]:
if side == 'left':
contents_raw = outer_padding + contents_raw + (divider_spaces * ' ')
if segment is not last_segment:
compare_segment = next(iter((
segment
for segment in segments[index + 1:]
if not segment['literal_contents'][1]
)))
else:
compare_segment = theme.EMPTY_SEGMENT
else:
contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding
compare_segment = prev_segment
outer_padding = int(bool(
segment is first_segment
if side == 'left' else
segment is last_segment
)) * ' '
divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard'
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 = ''
contents_raw = segment['contents']
contents_highlighted = ''
draw_divider = segment['draw_' + divider_type + '_divider']
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
# 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 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
if side == 'left':
contents_raw = outer_padding + contents_raw
else:
contents_raw = contents_raw + outer_padding
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
segment['_rendered_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted
prev_segment = segment
else:
if side == 'left':
contents_raw = outer_padding + contents_raw
else:
contents_raw = contents_raw + outer_padding
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
segment['_rendered_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted
segment['_rendered_raw'] = ' ' * segment['literal_contents'][0]
segment['_rendered_hl'] = segment['literal_contents'][1]
yield segment
def escape(self, string):

View File

@ -35,10 +35,10 @@ class BarRenderer(Renderer):
return text + contents + '%{F-B--u}'
def render(self):
def render(self, *args, **kwargs):
return '%{{l}}{0}%{{r}}{1}'.format(
super(BarRenderer, self).render(side='left'),
super(BarRenderer, self).render(side='right'),
super(BarRenderer, self).render(side='left', *args, **kwargs),
super(BarRenderer, self).render(side='right', *args, **kwargs),
)

View File

@ -31,6 +31,13 @@ class TmuxRenderer(Renderer):
character_translations = Renderer.character_translations.copy()
character_translations[ord('#')] = '##[]'
def render(self, width=None, segment_info={}, **kwargs):
if width and segment_info:
width -= segment_info.get('width_adjust', 0)
if width < 10:
width = 10
return super(TmuxRenderer, self).render(width=width, segment_info=segment_info, **kwargs)
def hlstyle(self, fg=None, bg=None, attrs=None):
'''Highlight a segment.'''
# We dont need to explicitly reset attributes, so skip those calls

View File

@ -127,6 +127,8 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors
colorscheme,
)
new_pslen = len(parsed_segments)
while parsed_segments[new_pslen - 1]['literal_contents'][1]:
new_pslen -= 1
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
@ -134,6 +136,8 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors
def set_segment_highlighting(pl, colorscheme, segment, mode):
if segment['literal_contents'][1]:
return True
try:
highlight_group_prefix = segment['highlight_group_prefix']
except KeyError:
@ -228,6 +232,7 @@ get_fallback_segment = {
'before': None,
'after': None,
'contents': '',
'literal_contents': (0, ''),
'priority': None,
'draw_soft_divider': True,
'draw_hard_divider': True,
@ -245,6 +250,7 @@ get_fallback_segment = {
'_contents_len': None,
}.copy
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,
@ -373,6 +379,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
)
),
'contents': None,
'literal_contents': None,
'priority': None,
'draw_soft_divider': None,
'draw_hard_divider': None,
@ -421,6 +428,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
'after': get_key(False, segment, module, function_name, name, 'after', ''),
'contents_func': contents_func,
'contents': contents,
'literal_contents': (0, ''),
'priority': segment.get('priority', None),
'draw_hard_divider': segment.get('draw_hard_divider', True),
'draw_soft_divider': segment.get('draw_soft_divider', True),

View File

@ -3,36 +3,37 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import re
from imaplib import IMAP4_SSL_PORT, IMAP4_SSL, IMAP4
from collections import namedtuple
from powerline.lib.threaded import KwThreadedSegment
from powerline.segments import with_docstring
_IMAPKey = namedtuple('Key', 'username password server port folder')
_IMAPKey = namedtuple('Key', 'username password server port folder use_ssl')
class EmailIMAPSegment(KwThreadedSegment):
interval = 60
@staticmethod
def key(username, password, server='imap.gmail.com', port=993, folder='INBOX', **kwargs):
return _IMAPKey(username, password, server, port, folder)
def key(username, password, server='imap.gmail.com', port=IMAP4_SSL_PORT, folder='INBOX', use_ssl=None, **kwargs):
if use_ssl is None:
use_ssl = (port == IMAP4_SSL_PORT)
return _IMAPKey(username, password, server, port, folder, use_ssl)
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
except imaplib.IMAP4.error as e:
unread_count = str(e)
if key.use_ssl:
mail = IMAP4_SSL(key.server, key.port)
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))
mail = IMAP4(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))
return unread_count
@staticmethod
@ -53,7 +54,7 @@ class EmailIMAPSegment(KwThreadedSegment):
email_imap_alert = with_docstring(EmailIMAPSegment(),
'''Return unread e-mail count for IMAP servers.
('''Return unread e-mail count for IMAP servers.
:param str username:
login username
@ -69,6 +70,9 @@ email_imap_alert = with_docstring(EmailIMAPSegment(),
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.
:param bool use_ssl:
If ``True`` then use SSL connection. If ``False`` then do not use it.
Default is ``True`` if port is equal to {ssl_port} and ``False`` otherwise.
Highlight groups used: ``email_alert_gradient`` (gradient), ``email_alert``.
''')
''').format(ssl_port=IMAP4_SSL_PORT))

View File

@ -247,7 +247,7 @@ class NetworkLoadSegment(KwThreadedSegment):
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',
'divider_highlight_group': 'network_load:divider',
'highlight_groups': hl_groups,
})
if is_gradient:
@ -287,7 +287,7 @@ falls back to reading
Maximum number of sent bytes per second. Is only used to compute gradient
level.
Divider highlight group used: ``background:divider``.
Divider highlight group used: ``network_load: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``.
''')

View File

@ -208,7 +208,14 @@ class MpdPlayerSegment(PlayerSegment):
mpd = with_docstring(MpdPlayerSegment(),
('''Return Music Player Daemon information
Requires mpc command to be acessible from $PATH or ``mpd`` Python module.
Requires ``mpd`` Python module (e.g. |python-mpd2|_ or |python-mpd|_ Python
package) or alternatively the ``mpc`` command to be acessible from $PATH.
.. |python-mpd| replace:: ``python-mpd``
.. _python-mpd: https://pypi.python.org/pypi/python-mpd
.. |python-mpd2| replace:: ``python-mpd2``
.. _python-mpd2: https://pypi.python.org/pypi/python-mpd2
{0}
:param str host:

View File

@ -131,10 +131,12 @@ if os.path.exists('/proc/uptime'):
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)
if hasattr(psutil, 'boot_time'):
def _get_uptime():
return int(time() - psutil.boot_time())
else:
def _get_uptime():
return int(time() - psutil.BOOT_TIME)
else:
def _get_uptime():
raise NotImplementedError

View File

@ -1,7 +1,10 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import i3
from powerline.theme import requires_segment_info
conn = None
def calcgrp(w):
@ -16,12 +19,46 @@ def calcgrp(w):
return group
def workspaces(pl):
'''Return workspace list
def workspaces(pl, only_show=None, strip=0):
'''Return list of used workspaces
Highlight groups used: ``workspace``, ``w_visible``, ``w_focused``, ``w_urgent``
:param list only_show:
Specifies which workspaces to show. Valid entries are ``"visible"``,
``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
are shown.
:param int strip:
Specifies how many characters from the front of each workspace name
should be stripped (e.g. to remove workspace numbers). Defaults to zero.
Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``.
'''
global conn
if not conn:
try:
import i3ipc
except ImportError:
import i3 as conn
else:
conn = i3ipc.Connection()
return [{
'contents': w['name'],
'contents': w['name'][min(len(w['name']), strip):],
'highlight_groups': calcgrp(w)
} for w in i3.get_workspaces()]
} for w in conn.get_workspaces() if not only_show or any((w[typ] for typ in only_show))]
@requires_segment_info
def mode(pl, segment_info, names={'default': None}):
'''Returns current i3 mode
:param dict names:
Specifies the string to show for various modes.
Use ``null`` to hide a mode (``default`` is hidden by default).
Highligh groups used: ``mode``
'''
mode = segment_info['mode']
if mode in names:
return names[mode]
return mode

View File

@ -740,3 +740,21 @@ def csv_col_current(pl, segment_info, display_name='auto', name_format=' ({colum
'contents': name_format.format(column_name=column_name),
'highlight_groups': ['csv:column_name', 'csv'],
}] if column_name else [])
@requires_segment_info
def tab(pl, segment_info, end=False):
'''Mark start of the clickable region for tabpage
:param bool end:
In place of starting region for the current tab end it.
No highlight groups are used (literal segment).
'''
try:
return [{
'contents': None,
'literal_contents': (0, '%{tabnr}T'.format(tabnr=('' if end else segment_info['tabnr']))),
}]
except KeyError:
return None

View File

@ -59,7 +59,7 @@ else:
def get_version():
base_version = '2.1.4'
base_version = '2.2'
base_version += '.dev9999'
try:
return base_version + '+git.' + str(subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip())
@ -70,7 +70,7 @@ def get_version():
setup(
name='powerline-status',
version='2.1.4',
version='2.2',
description='The ultimate statusline/prompt utility.',
long_description=README,
classifiers=[

View File

@ -28,6 +28,7 @@ class ExpectProcess(threading.Thread):
child = pexpect.spawn(self.cmd, self.args, cwd=self.cwd, env=self.env)
sleep(0.5)
child.setwinsize(self.rows, self.cols)
sleep(0.5)
self.child = child
status = None
while status is None:
@ -44,6 +45,13 @@ class ExpectProcess(threading.Thread):
self.vterm.push(s)
self.buffer.append(s)
def resize(self, rows, cols):
with self.child_lock:
self.rows = rows
self.cols = cols
self.child.setwinsize(rows, cols)
self.vterm.resize(rows, cols)
def __getitem__(self, position):
with self.lock:
return self.vterm.vtscreen[position]

View File

@ -91,6 +91,11 @@ def get_functions(lib):
('cols', ctypes.c_int)
)),
vterm_obtain_screen=(VTermScreen_p, (('vt', VTerm_p),)),
vterm_set_size=(None, (
('vt', VTerm_p),
('rows', ctypes.c_int),
('cols', ctypes.c_int)
)),
vterm_screen_reset=(None, (
('screen', VTermScreen_p),
('hard', ctypes.c_int)
@ -171,6 +176,9 @@ class VTerm(object):
data = data.encode('utf-8')
return self.functions.vterm_input_write(self.vt, data, len(data))
def resize(self, rows, cols):
self.functions.vterm_set_size(self.vt, rows, cols)
def __del__(self):
try:
self.functions.vterm_free(self.vt)

View File

@ -1,5 +1,5 @@
#!/usr/bin/vim -S
if has('multibyte')
if has('multi_byte')
if empty(&encoding)
call writefile(['&encoding option value is empty, even though Vim has +multibyte'], 'message.fail')
cquit

View File

@ -11,6 +11,7 @@ mkdir tests/vterm/path
ln -s "$(which "${PYTHON}")" tests/vterm/path/python
ln -s "$(which bash)" tests/vterm/path
ln -s "$(which env)" tests/vterm/path
ln -s "$(which cut)" tests/vterm/path
ln -s "$PWD/scripts/powerline-render" tests/vterm/path
ln -s "$PWD/scripts/powerline-config" tests/vterm/path

View File

@ -54,6 +54,7 @@ def test_expected_result(p, expected_result, cols, rows, print_logs):
for key, text in result
))
print(shesc_result)
print(result)
print('Expected:')
shesc_expected_result = ''.join((
'{0}{1}\x1b[m'.format(cell_properties_key_to_shell_escape(key), text)
@ -95,6 +96,17 @@ def test_expected_result(p, expected_result, cols, rows, print_logs):
return False
def get_expected_result(tmux_version, expected_result_old, expected_result_1_7=None, expected_result_new=None, expected_result_2_0=None):
if tmux_version >= (2, 0) and expected_result_2_0:
return expected_result_2_0
elif tmux_version >= (1, 8) and expected_result_new:
return expected_result_new
elif tmux_version >= (1, 7) and expected_result_1_7:
return expected_result_1_7
else:
return expected_result_old
def main(attempts=3):
vterm_path = os.path.join(VTERM_TEST_DIR, 'path')
socket_path = 'tmux-socket'
@ -165,8 +177,8 @@ def main(attempts=3):
'POWERLINE_CONFIG_PATHS': os.path.abspath('powerline/config_files'),
'POWERLINE_COMMAND': 'powerline-render',
'POWERLINE_THEME_OVERRIDES': (
'default.segments.right=[{"type":"string","name":"s1","highlight_groups":["cwd"]}];'
'default.segments.left=[{"type":"string","name":"s2","highlight_groups":["background"]}];'
'default.segments.right=[{"type":"string","name":"s1","highlight_groups":["cwd"],"priority":50}];'
'default.segments.left=[{"type":"string","name":"s2","highlight_groups":["background"],"priority":20}];'
'default.segment_data.s1.contents=S1 string here;'
'default.segment_data.s2.contents=S2 string here;'
),
@ -175,8 +187,47 @@ def main(attempts=3):
},
)
p.start()
sleep(2)
expected_result_2_0 = (
sleep(5)
tmux_version = get_tmux_version(get_fallback_logger())
expected_result = get_expected_result(tmux_version, expected_result_old=(
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' S2 string here '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 0 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 1 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((11, 11, 11), (0, 102, 153), 0, 0, 0), ' '),
(((102, 204, 255), (0, 102, 153), 0, 0, 0), '2 | '),
(((255, 255, 255), (0, 102, 153), 1, 0, 0), 'bash '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' ' * 127),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here '),
), expected_result_new=(
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' S2 string here '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 0 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 1 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((11, 11, 11), (0, 102, 153), 0, 0, 0), ' '),
(((102, 204, 255), (0, 102, 153), 0, 0, 0), '2 | '),
(((255, 255, 255), (0, 102, 153), 1, 0, 0), 'bash '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' ' * 127),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here '),
), expected_result_2_0=(
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' S2 string here '),
@ -195,62 +246,69 @@ def main(attempts=3):
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' ' * 128),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here '),
)
expected_result_new = (
))
ret = None
if not test_expected_result(p, expected_result, cols, rows, not attempts):
if attempts:
pass
# Will rerun main later.
else:
ret = False
elif ret is not False:
ret = True
cols = 40
p.resize(rows, cols)
sleep(5)
expected_result = get_expected_result(tmux_version, (
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' ' * cols),
), expected_result_1_7=(
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' S2 string here '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 0 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' <'),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'sh '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 1 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((11, 11, 11), (0, 102, 153), 0, 0, 0), ' '),
(((102, 204, 255), (0, 102, 153), 0, 0, 0), '2 | '),
(((255, 255, 255), (0, 102, 153), 1, 0, 0), 'bash '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here ')
), expected_result_new=(
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' <'),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), 'sh '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((11, 11, 11), (0, 102, 153), 0, 0, 0), ' '),
(((102, 204, 255), (0, 102, 153), 0, 0, 0), '2 | '),
(((255, 255, 255), (0, 102, 153), 1, 0, 0), 'bash '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here ')
), expected_result_2_0=(
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), '<'),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((11, 11, 11), (0, 102, 153), 0, 0, 0), ' '),
(((102, 204, 255), (0, 102, 153), 0, 0, 0), '2 | '),
(((255, 255, 255), (0, 102, 153), 1, 0, 0), 'bash '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' ' * 127),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here '),
)
expected_result_old = (
(((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '),
(((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' S2 string here '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 0 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((133, 133, 133), (11, 11, 11), 0, 0, 0), ' 1 '),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), '| '),
(((188, 188, 188), (11, 11, 11), 0, 0, 0), 'bash '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' '),
(((11, 11, 11), (0, 102, 153), 0, 0, 0), ' '),
(((102, 204, 255), (0, 102, 153), 0, 0, 0), '2 | '),
(((255, 255, 255), (0, 102, 153), 1, 0, 0), 'bash '),
(((0, 102, 153), (11, 11, 11), 0, 0, 0), ' '),
(((255, 255, 255), (11, 11, 11), 0, 0, 0), ' ' * 127),
(((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '),
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here '),
)
tmux_version = get_tmux_version(get_fallback_logger())
if tmux_version >= (2, 0):
expected_result = expected_result_2_0
elif tmux_version >= (1, 8):
expected_result = expected_result_new
else:
expected_result = expected_result_old
(((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here ')
))
if not test_expected_result(p, expected_result, cols, rows, not attempts):
if attempts:
pass
# Will rerun main later.
else:
return False
else:
return True
ret = False
elif ret is not False:
ret = True
if ret is not None:
return ret
finally:
check_call([tmux_exe, '-S', socket_path, 'kill-server'], env={
'PATH': vterm_path,

View File

@ -6,8 +6,9 @@ import os
from functools import partial
from collections import namedtuple
from time import sleep
from powerline.segments import shell, tmux, pdb
from powerline.segments import shell, tmux, pdb, i3wm
from powerline.lib.vcs import get_fallback_create_watcher
from powerline.lib.unicode import out_u
@ -397,8 +398,6 @@ class TestNet(TestCommon):
self.assertEqual(self.module.internal_ip(pl=pl, ipv=6), None)
def test_network_load(self):
from time import sleep
def gb(interface):
return None
@ -430,24 +429,24 @@ class TestNet(TestCommon):
while not self.module.network_load.interfaces.get('eth0', {}).get('prev', (None, None))[1]:
sleep(0.1)
self.assertEqual(self.module.network_load(pl=pl, interface='eth0'), [
{'divider_highlight_group': 'background:divider', 'contents': 'DL 1 KiB/s', 'highlight_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 'UL 2 KiB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 'DL 1 KiB/s', 'highlight_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 'UL 2 KiB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
])
self.assertEqual(self.module.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_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 'r 1 KiB/s', 'highlight_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 's 2 KiB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
])
self.assertEqual(self.module.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_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_groups': ['network_load_sent', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 'r 1 Kibps', 'highlight_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 's 2 Kibps', 'highlight_groups': ['network_load_sent', 'network_load']},
])
self.assertEqual(self.module.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_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 'r 1 kB/s', 'highlight_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 's 2 kB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
])
self.assertEqual(self.module.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_groups': ['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_groups': ['network_load_sent', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 'r 1 KiB/s', 'highlight_groups': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100},
{'divider_highlight_group': 'network_load:divider', 'contents': 's 2 KiB/s', 'highlight_groups': ['network_load_sent', 'network_load']},
])
class ApproxEqual(object):
@ -455,8 +454,8 @@ class TestNet(TestCommon):
return abs(i - 50.0) < 1
self.assertEqual(self.module.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_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_groups': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()},
{'divider_highlight_group': 'network_load:divider', 'contents': 'r 1 KiB/s', 'highlight_groups': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'network_load:divider', 'contents': 's 2 KiB/s', 'highlight_groups': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()},
])
finally:
self.module.network_load.shutdown()
@ -816,6 +815,50 @@ class TestWthr(TestCommon):
self.module.weather.shutdown()
class TestI3WM(TestCase):
def test_workspaces(self):
pl = Pl()
with replace_attr(i3wm, 'conn', Args(get_workspaces=lambda: iter([
{'name': '1: w1', 'focused': False, 'urgent': False, 'visible': False},
{'name': '2: w2', 'focused': False, 'urgent': False, 'visible': True},
{'name': '3: w3', 'focused': False, 'urgent': True, 'visible': True},
{'name': '4: w4', 'focused': True, 'urgent': True, 'visible': True},
]))):
self.assertEqual(i3wm.workspaces(pl=pl), [
{'contents': '1: w1', 'highlight_groups': ['workspace']},
{'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, only_show=None), [
{'contents': '1: w1', 'highlight_groups': ['workspace']},
{'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, only_show=['focused', 'urgent']), [
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, only_show=['visible']), [
{'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, only_show=['visible'], strip=3), [
{'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': 'w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': 'w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
])
def test_mode(self):
pl = Pl()
self.assertEqual(i3wm.mode(pl=pl, segment_info={'mode': 'default'}), None)
self.assertEqual(i3wm.mode(pl=pl, segment_info={'mode': 'test'}), 'test')
self.assertEqual(i3wm.mode(pl=pl, segment_info={'mode': 'default'}, names={'default': 'test'}), 'test')
self.assertEqual(i3wm.mode(pl=pl, segment_info={'mode': 'test'}, names={'default': 'test', 'test': 't'}), 't')
class TestMail(TestCommon):
module_name = 'mail'
@ -1191,6 +1234,18 @@ class TestVim(TestCase):
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_tab(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(self.vim.tab(pl=pl, segment_info=segment_info), [{
'contents': None,
'literal_contents': (0, '%1T'),
}])
self.assertEqual(self.vim.tab(pl=pl, segment_info=segment_info, end=True), [{
'contents': None,
'literal_contents': (0, '%T'),
}])
def test_bufnr(self):
pl = Pl()
segment_info = vim_module._get_segment_info()

View File

@ -188,6 +188,7 @@ 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 "$(which tr)" tests/shell/path
ln -s "$(which uname)" tests/shell/path
ln -s "$(which test)" tests/shell/path
ln -s "$(which pwd)" tests/shell/path

View File

@ -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#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 '
if result isnot# '%1T%#Pl_247_10395294_236_3158064_NONE# 1 ./abc  %2T2 ./def %#Pl_236_3158064_240_5789784_NONE# %3T%#Pl_250_12369084_240_5789784_NONE#3 ./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %T%#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#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 '
if result isnot# '%T%#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#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 '
if result isnot# '%T%#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

27
tools/purge-PRs.py Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import argparse
from getpass import getpass
from github import Github
p = argparse.ArgumentParser(description='Powerline release script')
p.add_argument('-u', '--user', type=str, metavar='USER', help='Github username.', required=True)
p.add_argument('-p', '--password', type=str, metavar='PASS', help='Github password. You will be prompted if it is not supplied.')
if __name__ == '__main__':
args = p.parse_args()
user = args.user
password = args.password or getpass('Password for {0}: '.format(user))
gh = Github(user, password)
grepo = gh.get_repo('powerline/powerline')
for pr in grepo.get_pulls():
if pr.base.ref != 'develop':
issue = grepo.get_issue(pr.number)
issue.create_comment('PRs to any branch, but develop, are not accepted.', )
issue.add_to_labels('s:invalid')
issue.edit(state='closed')