Merge pull request #893 from ZyX-I/multiline-prompt

Multiline prompt support

Fixes #462
This commit is contained in:
ZyX-I 2014-06-24 23:50:03 +04:00
commit 35522b1511
16 changed files with 516 additions and 222 deletions

View File

@ -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 <config-themes-seg-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 <config-themes-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

View File

@ -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.

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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
}

View File

@ -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'))

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -1,151 +1,136 @@
# vim:fileencoding=utf-8:noet
'''Dynamic configuration files tests.'''
import tests.vim as vim_module
import sys
import os
import json
from tests.lib import Args, urllib_read, replace_attr
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
VBLOCK = chr(ord('V') - 0x40)
SBLOCK = chr(ord('S') - 0x40)
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'],
},
],
},
},
}
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'}),
(('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())
self.assertEqual(len(buffers), len(local_themes))
outputs = {}
i = 0
with vim_module._with('split'):
with VimPowerline() as powerline:
def check_output(mode, args, kwargs):
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('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:
for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']:
check_output(mode, None, None)
for args, kwargs in buffers:
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:
vim_module._start_mode('n')
def test_tmux(self):
from powerline.segments import common
from imp import reload
reload(common)
from powerline.shell import ShellPowerline
with replace_attr(common, 'urllib_read', urllib_read):
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], jobnum=0, ext=['shell'], renderer_module='zsh_prompt')
segment_info = {'args': args}
with ShellPowerline(args, run_once=False) as powerline:
powerline.render(segment_info=segment_info)
with ShellPowerline(args, run_once=False) as powerline:
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
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:
powerline.render(segment_info={'args': args})
def test_ipython(self):
from powerline.ipython import IpythonPowerline
class IpyPowerline(IpythonPowerline):
path = None
config_overrides = None
theme_overrides = {}
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 imp import reload
reload(common)
from powerline import Powerline
with replace_attr(common, 'urllib_read', urllib_read):
Powerline(ext='wm', renderer_module='pango_markup', run_once=True).render()
reload(common)
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
old_cwd = None
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 old_cwd
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
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
def tearDownModule():
global old_cwd
os.chdir(old_cwd)
old_cwd = None
sys.path.pop(0)
tearDownModule = setUpModule
if __name__ == '__main__':

View File

@ -0,0 +1,153 @@
# vim:fileencoding=utf-8:noet
'''Dynamic configuration files tests.'''
import tests.vim as vim_module
import sys
import os
import json
from tests.lib import Args, urllib_read, replace_attr
from tests import TestCase
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')
buffers = (
(('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())
self.assertEqual(len(buffers), len(local_themes))
outputs = {}
i = 0
with vim_module._with('split'):
with VimPowerline() as powerline:
def check_output(mode, args, kwargs):
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('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:
for mode in ['n', 'nc', 'no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'i', 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!']:
check_output(mode, None, None)
for args, kwargs in buffers:
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:
vim_module._start_mode('n')
def test_tmux(self):
from powerline.segments import common
from imp import reload
reload(common)
from powerline.shell import ShellPowerline
with replace_attr(common, 'urllib_read', urllib_read):
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], jobnum=0, ext=['shell'], renderer_module='zsh_prompt')
segment_info = {'args': args}
with ShellPowerline(args, run_once=False) as powerline:
powerline.render(segment_info=segment_info)
with ShellPowerline(args, run_once=False) as powerline:
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
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:
powerline.render(segment_info={'args': args})
def test_ipython(self):
from powerline.ipython import IpythonPowerline
class IpyPowerline(IpythonPowerline):
path = None
config_overrides = None
theme_overrides = {}
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 imp import reload
reload(common)
from powerline import Powerline
with replace_attr(common, 'urllib_read', urllib_read):
Powerline(ext='wm', renderer_module='pango_markup', run_once=True).render()
reload(common)
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()
from powerline.segments import vim
globals()['vim'] = vim
def tearDownModule():
global old_cwd
os.chdir(old_cwd)
old_cwd = None
sys.path.pop(0)
if __name__ == '__main__':
from tests import main
main()