diff --git a/client/powerline.sh b/client/powerline.sh index b8e37956..8bcec224 100755 --- a/client/powerline.sh +++ b/client/powerline.sh @@ -46,7 +46,7 @@ fi done printf '%s\0' "$PWD" $ENV -0 -) | socat -lf/dev/null -t 10 - "$ADDRESS" +) 2>/dev/null | socat -lf/dev/null -t 10 - "$ADDRESS" if test $? -ne 0 ; then powerline-render "$@" diff --git a/docs/source/powerline_autodoc.py b/docs/source/powerline_autodoc.py index 971717df..eba42edb 100644 --- a/docs/source/powerline_autodoc.py +++ b/docs/source/powerline_autodoc.py @@ -1,6 +1,8 @@ # vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, division, absolute_import, print_function) +import os + from inspect import formatargspec from sphinx.ext import autodoc @@ -29,6 +31,34 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter): return formatargspec(*argspec, formatvalue=formatvalue).replace('\\', '\\\\') +class Repr(object): + def __init__(self, repr_contents): + self.repr_contents = repr_contents + + def __repr__(self): + return '<{0}>'.format(self.repr_contents) + + +class EnvironDocumenter(autodoc.AttributeDocumenter): + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + if type(member) is dict and member.get('environ') is os.environ: + return True + else: + return False + + def import_object(self, *args, **kwargs): + ret = super(EnvironDocumenter, self).import_object(*args, **kwargs) + if not ret: + return ret + self.object = self.object.copy() + if 'home' in self.object: + self.object.update(home=Repr('home directory')) + self.object.update(environ=Repr('environ dictionary')) + return True + + def setup(app): autodoc.setup(app) app.add_autodocumenter(ThreadedDocumenter) + app.add_autodocumenter(EnvironDocumenter) diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 86aeadbc..007d9367 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -227,21 +227,23 @@ If your vimrc has something like .. code-block:: vim - autocmd! BufWritePost vimrc :source ~/.vimrc + autocmd! BufWritePost ~/.vimrc :source ~/.vimrc -to automatically source vimrc after saving it you must then add ``nested`` after -pattern (``vimrc`` in this case): +used to automatically source vimrc after saving it then you must add ``nested`` +after pattern (``vimrc`` in this case): .. code-block:: vim - autocmd! BufWritePost vimrc nested :source ~/.vimrc + autocmd! BufWritePost ~/.vimrc nested :source ~/.vimrc . Alternatively move ``:colorscheme`` command out of the vimrc to the file which -will not be automatically resourced. Observed problem is that when you use -``:colorscheme`` command existing highlighting groups are usually cleared, -including those defined by powerline. To workaround this issue powerline hooks -``Colorscheme`` event, but when you source vimrc with ``BufWritePost`` event, -but without ``nested`` this event is not launched. See also `autocmd-nested +will not be automatically resourced. + +Observed problem is that when you use ``:colorscheme`` command existing +highlighting groups are usually cleared, including those defined by powerline. +To workaround this issue powerline hooks ``Colorscheme`` event, but when you +source vimrc with ``BufWritePost`` (or any other) event, but without ``nested`` +this event is not launched. See also `autocmd-nested `_ Vim documentation. diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 5605c891..1ed3b933 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -19,8 +19,22 @@ related Python headers to be installed. Please consult distribution’s documentation for details on how to compile and install packages. Vim version 7.4 or newer is recommended for performance reasons, but Powerline -is known to work on vim-7.0.112 (some segments may not work though as it was not -actually tested). +supports Vim 7.0.112 and higher. + +Shell prompts requirements +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Due to fish having incorrect code for prompt width calculations up to version +2.1 and no way to tell that certain sequence of characters has no width +(``%{…%}`` in zsh and ``\[…\]`` in bash prompts serve exactly this purpose) +users that have fish versions below 2.1 are not supported.. + + +WM widgets requirements +^^^^^^^^^^^^^^^^^^^^^^^ + +Awesome is supported starting from version 3.5.1, inclusive. QTile is supported +from version 0.6, inclusive. .. _usage-terminal-emulators: diff --git a/docs/source/usage/shell-prompts.rst b/docs/source/usage/shell-prompts.rst index f10beedd..c47d8399 100644 --- a/docs/source/usage/shell-prompts.rst +++ b/docs/source/usage/shell-prompts.rst @@ -65,6 +65,8 @@ the absolute path to the Powerline installation directory: set fish_function_path $fish_function_path "{repository_root}/powerline/bindings/fish" powerline-setup +.. warning:: Fish is supported only starting from version 2.1. + Rcsh prompt =========== diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst index bcbf206f..2eb88765 100644 --- a/docs/source/usage/wm-widgets.rst +++ b/docs/source/usage/wm-widgets.rst @@ -31,15 +31,20 @@ Add the following to :file:`~/.config/qtile/config.py`: .. code-block:: python - from powerline.bindings.qtile.widget import Powerline + from libqtile.bar import Bar + from libqtile.config import Screen + from libqtile.widget import Spacer + + from powerline.bindings.qtile.widget import PowerlineTextBox screens = [ Screen( - top=bar.Bar([ - # ... - Powerline(timeout=2), - # ... + top=Bar([ + PowerlineTextBox(update_interval=2, side='left'), + Spacer(), + PowerlineTextBox(update_interval=2, side='right'), ], + 35 # width ), ), ] diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index ac1ab52c..ba4344f0 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -4,13 +4,16 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct import os import re import sys +import subprocess +import shlex from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY from powerline.lib.config import ConfigLoader from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config from powerline.shell import ShellPowerline from powerline.lib.shell import which -from powerline.bindings.tmux import TmuxVersionInfo, run_tmux_command, set_tmux_environment, get_tmux_version +from powerline.bindings.tmux import (TmuxVersionInfo, run_tmux_command, set_tmux_environment, get_tmux_version, + source_tmux_file) from powerline.lib.encoding import get_preferred_output_encoding from powerline.renderers.tmux import attrs_to_tmux_attrs from powerline.commands.main import finish_args @@ -59,7 +62,7 @@ def get_tmux_configs(version): yield (fname, priority + file_version.minor * 10 + file_version.major * 10000) -def source_tmux_files(pl, args): +def source_tmux_files(pl, args, tmux_version=None, source_tmux_file=source_tmux_file): '''Source relevant version-specific tmux configuration files Files are sourced in the following order: @@ -67,15 +70,20 @@ def source_tmux_files(pl, args): * If files for same versions are to be sourced then first _minus files are sourced, then _plus files and then files without _minus or _plus suffixes. ''' - version = get_tmux_version(pl) - run_tmux_command('source', os.path.join(TMUX_CONFIG_DIRECTORY, 'powerline-base.conf')) - for fname, priority in sorted(get_tmux_configs(version), key=(lambda v: v[1])): - run_tmux_command('source', fname) + tmux_version = tmux_version or get_tmux_version(pl) + source_tmux_file(os.path.join(TMUX_CONFIG_DIRECTORY, 'powerline-base.conf')) + for fname, priority in sorted(get_tmux_configs(tmux_version), key=(lambda v: v[1])): + source_tmux_file(fname) if not os.environ.get('POWERLINE_COMMAND'): cmd = deduce_command() if cmd: set_tmux_environment('POWERLINE_COMMAND', deduce_command(), remove=False) - run_tmux_command('refresh-client') + try: + run_tmux_command('refresh-client') + except subprocess.CalledProcessError: + # On tmux-2.0 this command may fail for whatever reason. Since it is + # critical just ignore the failure. + pass class EmptyArgs(object): @@ -87,7 +95,7 @@ class EmptyArgs(object): return None -def init_tmux_environment(pl, args): +def init_tmux_environment(pl, args, set_tmux_environment=set_tmux_environment): '''Initialize tmux environment from tmux configuration ''' powerline = ShellPowerline(finish_args(os.environ, EmptyArgs('tmux', args.config_path))) @@ -164,9 +172,43 @@ def init_tmux_environment(pl, args): ' ' * powerline.renderer.strwidth(left_dividers['hard']))) +TMUX_VAR_RE = re.compile('\$(_POWERLINE_\w+)') + + def tmux_setup(pl, args): - init_tmux_environment(pl, args) - source_tmux_files(pl, args) + tmux_environ = {} + tmux_version = get_tmux_version(pl) + + def set_tmux_environment_nosource(varname, value, remove=True): + tmux_environ[varname] = value + + def replace_cb(match): + return tmux_environ[match.group(1)] + + def replace_env(s): + return TMUX_VAR_RE.subn(replace_cb, s)[0] + + def source_tmux_file_nosource(fname): + with open(fname) as fd: + for line in fd: + if line.startswith('#') or line == '\n': + continue + args = shlex.split(line) + args = [args[0]] + [replace_env(arg) for arg in args[1:]] + run_tmux_command(*args) + + if args.source is None: + args.source = tmux_version < (1, 9) + + if args.source: + ste = set_tmux_environment + stf = source_tmux_file + else: + ste = set_tmux_environment_nosource + stf = source_tmux_file_nosource + + init_tmux_environment(pl, args, set_tmux_environment=ste) + source_tmux_files(pl, args, tmux_version=tmux_version, source_tmux_file=stf) def get_main_config(args): diff --git a/powerline/bindings/qtile/widget.py b/powerline/bindings/qtile/widget.py index 897658c9..92e3a27e 100644 --- a/powerline/bindings/qtile/widget.py +++ b/powerline/bindings/qtile/widget.py @@ -1,22 +1,32 @@ # vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, division, absolute_import, print_function) -from libqtile import bar -from libqtile.widget import base +from libqtile.bar import CALCULATED +from libqtile.widget import TextBox -from powerline import Powerline as PowerlineCore +from powerline import Powerline -class Powerline(base._TextBox): - def __init__(self, timeout=2, text=' ', width=bar.CALCULATED, **config): - base._TextBox.__init__(self, text, width, **config) - self.timeout_add(timeout, self.update) - self.powerline = PowerlineCore(ext='wm', renderer_module='pango_markup') +class QTilePowerline(Powerline): + def do_setup(self, obj): + obj.powerline = self + + +class PowerlineTextBox(TextBox): + # TODO Replace timeout argument with update_interval argument in next major + # release. + def __init__(self, timeout=2, text=b' ', width=CALCULATED, side='right', update_interval=None, **config): + super(PowerlineTextBox, self).__init__(text, width, **config) + self.side = side + self.update_interval = update_interval or timeout + self.did_run_timer_setup = False + powerline = QTilePowerline(ext='wm', renderer_module='pango_markup') + powerline.setup(self) def update(self): if not self.configured: return True - self.text = self.powerline.render(side='right') + self.text = self.powerline.render(side=self.side).encode('utf-8') self.bar.draw() return True @@ -26,12 +36,26 @@ class Powerline(base._TextBox): def cmd_get(self): return self.text + def timer_setup(self): + if not self.did_run_timer_setup: + self.did_run_timer_setup = True + self.timeout_add(self.update_interval, self.update) + def _configure(self, qtile, bar): - base._TextBox._configure(self, qtile, bar) + super(PowerlineTextBox, self)._configure(qtile, bar) + if self.layout.markup: + # QTile-0.9.1: no need to recreate layout or run timer_setup + return self.layout = self.drawer.textlayout( self.text, self.foreground, self.font, self.fontsize, self.fontshadow, - markup=True) + markup=True, + ) + self.timer_setup() + + +# TODO: Remove this at next major release +Powerline = PowerlineTextBox diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index 7ae7237f..5b4dc739 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -224,8 +224,7 @@ fi # Strips the leading `-`: it may be present when shell is a login shell _POWERLINE_USED_SHELL=${0#-} -_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/usr} -_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/bin/} +_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL##*/} if "${POWERLINE_CONFIG_COMMAND}" shell uses tmux ; then _powerline_init_tmux_support $_POWERLINE_USED_SHELL diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 502e95e3..bba34280 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -43,7 +43,7 @@ if ( { $POWERLINE_CONFIG_COMMAND shell --shell=tcsh uses prompt } ) then endif alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND:q $POWERLINE_COMMAND_ARGS shell left -r .tcsh --renderer-arg=client_id=$$ --last-exit-code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' - alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND:q $POWERLINE_COMMAND_ARGS shell right -r .tcsh --renderer-arg=client_id=$$ --last-exit-code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND:q $POWERLINE_COMMAND_ARGS shell right -r .tcsh --renderer-arg=client_id=$$ --last-exit-code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' diff --git a/powerline/bindings/tmux/__init__.py b/powerline/bindings/tmux/__init__.py index 6ccda8b5..afa61afb 100644 --- a/powerline/bindings/tmux/__init__.py +++ b/powerline/bindings/tmux/__init__.py @@ -50,7 +50,21 @@ def set_tmux_environment(varname, value, remove=True): ''' run_tmux_command('set-environment', '-g', varname, value) if remove: - run_tmux_command('set-environment', '-r', varname) + try: + run_tmux_command('set-environment', '-r', varname) + except subprocess.CalledProcessError: + # On tmux-2.0 this command may fail for whatever reason. Since it is + # critical just ignore the failure. + pass + + +def source_tmux_file(fname): + '''Source tmux configuration file + + :param str fname: + Full path to the sourced file. + ''' + run_tmux_command('source', fname) NON_DIGITS = re.compile('[^0-9]+') diff --git a/powerline/bindings/tmux/powerline-base.conf b/powerline/bindings/tmux/powerline-base.conf index cc0eedc8..b7d283fb 100644 --- a/powerline/bindings/tmux/powerline-base.conf +++ b/powerline/bindings/tmux/powerline-base.conf @@ -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#(eval \$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)" # vim: ft=tmux diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index cbe5f957..1f93d272 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -178,7 +178,8 @@ class Prompt(object): width=zsh.columns() - zle_rprompt_indent, segment_info=segment_info, ): - r += line + '\n' + if line: + r += line + '\n' r += self.powerline.render( width=zsh.columns(), side=self.side, diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 02b8431f..9c902440 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -12,6 +12,12 @@ _powerline_columns_fallback() { return 0 } +_powerline_append_precmd_function() { + if test -z "${precmd_functions[(re)$1]}" ; then + precmd_functions+=( $1 ) + fi +} + integer _POWERLINE_JOBNUM _powerline_init_tmux_support() { @@ -86,7 +92,7 @@ _powerline_init_modes_support() { _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" fi - precmd_functions+=( _powerline_set_main_keymap_name ) + _powerline_append_precmd_function _powerline_set_main_keymap_name } _powerline_set_jobnum() { @@ -110,17 +116,12 @@ _powerline_update_counter() { _powerline_setup_prompt() { emulate -L zsh - for f in "${precmd_functions[@]}"; do - if [[ "$f" = '_powerline_set_jobnum' ]]; then - return - fi - done - precmd_functions+=( _powerline_set_jobnum ) + _powerline_append_precmd_function _powerline_set_jobnum VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_NO_ZSH_ZPYTHON}" && { zmodload libzpython || zmodload zsh/zpython } &>/dev/null ; then - precmd_functions+=( _powerline_update_counter ) + _powerline_append_precmd_function _powerline_update_counter zpython 'from powerline.bindings.zsh import setup as _powerline_setup' zpython '_powerline_setup(globals())' zpython 'del _powerline_setup' diff --git a/powerline/commands/config.py b/powerline/commands/config.py index 61afb6bc..06c64366 100644 --- a/powerline/commands/config.py +++ b/powerline/commands/config.py @@ -60,7 +60,24 @@ def get_argparser(ArgumentParser=ConfigArgParser): help='If action is `source\' then version-specific tmux configuration ' 'files are sourced, if it is `setenv\' then special ' '(prefixed with `_POWERLINE\') tmux global environment variables ' - 'are filled with data from powerline configuration.' + 'are filled with data from powerline configuration. ' + 'Action `setup\' is just doing `setenv\' then `source\'.' + ) + tpg = tmux_parser.add_mutually_exclusive_group() + tpg.add_argument( + '-s', '--source', action='store_true', default=None, + help='When using `setup\': always use configuration file sourcing. ' + 'By default this is determined automatically based on tmux ' + 'version: this is the default for tmux 1.8 and below.', + ) + tpg.add_argument( + '-n', '--no-source', action='store_false', dest='source', default=None, + help='When using `setup\': in place of sourcing directly execute ' + 'configuration files. That is, read each needed ' + 'powerline-specific configuration file, substitute ' + '`$_POWERLINE_…\' variables with appropriate values and run ' + '`tmux config line\'. This is the default behaviour for ' + 'tmux 1.9 and above.' ) shell_parser = subparsers.add_parser('shell', help='Shell-specific commands') diff --git a/powerline/commands/main.py b/powerline/commands/main.py index 8c41b476..df61f0d9 100644 --- a/powerline/commands/main.py +++ b/powerline/commands/main.py @@ -71,7 +71,7 @@ def get_argparser(ArgumentParser=argparse.ArgumentParser): '(usually `shell\' or `tmux\').' ) parser.add_argument( - 'side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'), + 'side', nargs=1, choices=('left', 'right', 'above', 'aboveleft'), help='Side: `left\' and `right\' represent left and right side ' 'respectively, `above\' emits lines that are supposed to be printed ' 'just above the prompt and `aboveleft\' is like concatenating ' @@ -159,7 +159,8 @@ def write_output(args, powerline, segment_info, write): segment_info=segment_info, mode=segment_info.get('mode', None), ): - write(line + '\n') + if line: + write(line + '\n') args.side = args.side[len('above'):] if args.side: diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json index b1d653c0..53ea5b0a 100644 --- a/powerline/config_files/colorschemes/default.json +++ b/powerline/config_files/colorschemes/default.json @@ -5,6 +5,7 @@ "information:regular": { "fg": "gray10", "bg": "gray4", "attrs": ["bold"] }, "information:highlighted": { "fg": "white", "bg": "gray4", "attrs": [] }, "information:priority": { "fg": "brightyellow", "bg": "mediumorange", "attrs": [] }, + "warning:regular": { "fg": "white", "bg": "brightred", "attrs": ["bold"] }, "critical:failure": { "fg": "white", "bg": "darkestred", "attrs": [] }, "critical:success": { "fg": "white", "bg": "darkestgreen", "attrs": [] }, "background": { "fg": "white", "bg": "gray0", "attrs": [] }, diff --git a/powerline/config_files/colorschemes/solarized.json b/powerline/config_files/colorschemes/solarized.json index 6a573dcd..4c90706e 100644 --- a/powerline/config_files/colorschemes/solarized.json +++ b/powerline/config_files/colorschemes/solarized.json @@ -20,6 +20,7 @@ "cwd": "information:additional", "cwd:current_folder": "information:regular", "cwd:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] }, + "network_load": { "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": [] }, diff --git a/powerline/config_files/colorschemes/tmux/solarized.json b/powerline/config_files/colorschemes/tmux/solarized.json new file mode 100644 index 00000000..20c42d39 --- /dev/null +++ b/powerline/config_files/colorschemes/tmux/solarized.json @@ -0,0 +1,14 @@ +{ + "groups": { + "active_window_status": { "fg": "solarized:blue", "bg": "solarized:base02", "attrs": [] }, + "window_status": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "activity_status": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": [] }, + "bell_status": { "fg": "solarized:red", "bg": "solarized:base02", "attrs": [] }, + "window": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "window:divider": { "fg": "solarized:base01", "bg": "solarized:base02", "attrs": [] }, + "window:current": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "window_name": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, + "session": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "session:prefix": { "fg": "solarized:base01", "bg": "solarized:base3", "attrs": [] } + } +} diff --git a/powerline/config_files/colorschemes/vim/__main__.json b/powerline/config_files/colorschemes/vim/__main__.json index 1f408836..0d460b59 100644 --- a/powerline/config_files/colorschemes/vim/__main__.json +++ b/powerline/config_files/colorschemes/vim/__main__.json @@ -23,9 +23,10 @@ "csv:column_number": "line_current", "csv:column_name": "line_current_symbol", - "tab_nc:file_directory": "information:unimportant", - "tab_nc:file_name": "tab_nc:file_directory", - "tab_nc:tabnr": "tab_nc:file_directory", + "tab_nc:modified_indicator": "modified_indicator", + "tab_nc:file_directory": "information:unimportant", + "tab_nc:file_name": "tab_nc:file_directory", + "tab_nc:tabnr": "tab_nc:file_directory", "buf_nc:file_directory": "tab_nc:file_directory", "buf_nc:file_name": "tab_nc:file_name", diff --git a/powerline/lint/checks.py b/powerline/lint/checks.py index 4fcd71dd..4e93fe06 100644 --- a/powerline/lint/checks.py +++ b/powerline/lint/checks.py @@ -173,41 +173,47 @@ def check_group(group, data, context, echoerr): return True, False, False colorscheme = data['colorscheme'] ext = data['ext'] - configs = [] + configs = None if ext: + def listed_key(d, k): + try: + return [d[k]] + except KeyError: + return [] + if colorscheme == '__main__': - configs.append([config for config in data['ext_colorscheme_configs'][ext].items()]) - configs.append([config for config in data['top_colorscheme_configs'].items()]) + colorscheme_names = set(data['ext_colorscheme_configs'][ext]) + colorscheme_names.update(data['top_colorscheme_configs']) + colorscheme_names.discard('__main__') + configs = [ + ( + name, + listed_key(data['ext_colorscheme_configs'][ext], name) + + listed_key(data['ext_colorscheme_configs'][ext], '__main__') + + listed_key(data['top_colorscheme_configs'], name) + ) + for name in colorscheme_names + ] else: - try: - configs.append([data['ext_colorscheme_configs'][ext][colorscheme]]) - except KeyError: - pass - try: - configs.append([data['ext_colorscheme_configs'][ext]['__main__']]) - except KeyError: - pass - try: - configs.append([data['top_colorscheme_configs'][colorscheme]]) - except KeyError: - pass + configs = [ + ( + colorscheme, + listed_key(data['ext_colorscheme_configs'][ext], colorscheme) + + listed_key(data['ext_colorscheme_configs'][ext], '__main__') + + listed_key(data['top_colorscheme_configs'], colorscheme) + ) + ] else: try: - configs.append([data['top_colorscheme_configs'][colorscheme]]) + configs = [(colorscheme, [data['top_colorscheme_configs'][colorscheme]])] except KeyError: pass - new_echoerr = DelayedEchoErr(echoerr) hadproblem = False - for config_lst in configs: - tofind = len(config_lst) + for new_colorscheme, config_lst in configs: not_found = [] + new_data = data.copy() + new_data['colorscheme'] = new_colorscheme for config in config_lst: - if isinstance(config, tuple): - new_colorscheme, config = config - new_data = data.copy() - new_data['colorscheme'] = new_colorscheme - else: - new_data = data havemarks(config) try: group_data = config['groups'][group] @@ -222,21 +228,17 @@ def check_group(group, data, context, echoerr): ) if chadproblem: hadproblem = True - else: - tofind -= 1 - if not tofind: - return proceed, echo, hadproblem if not proceed: break - if not_found: - new_echoerr( + if not_found and len(not_found) == len(config_lst): + echoerr( context='Error while checking group definition in colorscheme (key {key})'.format( key=context.key), - problem='name {0} is not present in {1} {2} colorschemes: {3}'.format( - group, tofind, ext, ', '.join(not_found)), + problem='name {0} is not present anywhere in {1} {2} {3} colorschemes: {4}'.format( + group, len(not_found), ext, new_colorscheme, ', '.join(not_found)), problem_mark=group.mark ) - new_echoerr.echo_all() + hadproblem = True return True, False, hadproblem diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index d63c638a..732120bc 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -4,6 +4,8 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct import sys import re +from powerline.lib.encoding import get_preferred_output_encoding + NON_PRINTABLE_STR = ( '[^' @@ -130,41 +132,57 @@ class Mark: mark = mark.old_mark if id(mark) in processed_marks: raise ValueError('Trying to dump recursive mark') - if type(where) is str: - return where - else: - return where.encode('utf-8') + return where - def __str__(self): - return self.to_string() + if sys.version_info < (3,): + def __str__(self): + return self.to_string().encode('utf-8') + + def __unicode__(self): + return self.to_string() + else: + def __str__(self): + return self.to_string() + + def __eq__(self, other): + return self is other or ( + self.name == other.name + and self.line == other.line + and self.column == other.column + ) -def echoerr(*args, **kwargs): - stream = kwargs.pop('stream', sys.stderr) - stream.write('\n') - stream.write(format_error(*args, **kwargs) + '\n') +if sys.version_info < (3,): + def echoerr(**kwargs): + stream = kwargs.pop('stream', sys.stderr) + stream.write('\n') + stream.write((format_error(**kwargs) + '\n').encode(get_preferred_output_encoding())) +else: + def echoerr(**kwargs): + stream = kwargs.pop('stream', sys.stderr) + stream.write('\n') + stream.write(format_error(**kwargs) + '\n') -def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None): +def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None, indent=0): lines = [] + indentstr = ' ' * indent if context is not None: - lines.append(context) + lines.append(indentstr + context) if ( context_mark is not None and ( problem is None or problem_mark is None - or context_mark.name != problem_mark.name - or context_mark.line != problem_mark.line - or context_mark.column != problem_mark.column + or context_mark != problem_mark ) ): - lines.append(str(context_mark)) + lines.append(context_mark.to_string(indent=indent)) if problem is not None: - lines.append(problem) + lines.append(indentstr + problem) if problem_mark is not None: - lines.append(str(problem_mark)) + lines.append(problem_mark.to_string(indent=indent)) if note is not None: - lines.append(note) + lines.append(indentstr + note) return '\n'.join(lines) @@ -174,29 +192,48 @@ class MarkedError(Exception): class EchoErr(object): - __slots__ = ('echoerr', 'logger',) + __slots__ = ('echoerr', 'logger', 'indent') - def __init__(self, echoerr, logger): + def __init__(self, echoerr, logger, indent=0): self.echoerr = echoerr self.logger = logger + self.indent = indent - def __call__(self, *args, **kwargs): - self.echoerr(*args, **kwargs) + def __call__(self, **kwargs): + kwargs = kwargs.copy() + kwargs.setdefault('indent', self.indent) + self.echoerr(**kwargs) class DelayedEchoErr(EchoErr): - __slots__ = ('echoerr', 'logger', 'errs') + __slots__ = ('echoerr', 'logger', 'errs', 'message', 'separator_message', 'indent', 'indent_shift') - def __init__(self, echoerr): + def __init__(self, echoerr, message='', separator_message=''): super(DelayedEchoErr, self).__init__(echoerr, echoerr.logger) - self.errs = [] + self.errs = [[]] + self.message = message + self.separator_message = separator_message + self.indent_shift = (4 if message or separator_message else 0) + self.indent = echoerr.indent + self.indent_shift - def __call__(self, *args, **kwargs): - self.errs.append((args, kwargs)) + def __call__(self, **kwargs): + kwargs = kwargs.copy() + kwargs['indent'] = kwargs.get('indent', 0) + self.indent + self.errs[-1].append(kwargs) + + def next_variant(self): + self.errs.append([]) def echo_all(self): - for args, kwargs in self.errs: - self.echoerr(*args, **kwargs) + if self.message: + self.echoerr(problem=self.message, indent=(self.indent - self.indent_shift)) + for variant in self.errs: + if not variant: + continue + if self.separator_message and variant is not self.errs[0]: + self.echoerr(problem=self.separator_message, indent=(self.indent - self.indent_shift)) + for kwargs in variant: + self.echoerr(**kwargs) def __nonzero__(self): return not not self.errs diff --git a/powerline/lint/spec.py b/powerline/lint/spec.py index 6de14fea..e3d072a4 100644 --- a/powerline/lint/spec.py +++ b/powerline/lint/spec.py @@ -309,11 +309,16 @@ class Spec(object): ``self.specs[start:end]`` is matched by the given value. ''' havemarks(value) - new_echoerr = DelayedEchoErr(echoerr) + new_echoerr = DelayedEchoErr( + echoerr, + 'One of the either variants failed. Messages from the first variant:', + 'messages from the next variant:' + ) hadproblem = False for spec in self.specs[start:end]: proceed, hadproblem = spec.match(value, value.mark, data, context, new_echoerr) + new_echoerr.next_variant() if not proceed: break if not hadproblem: diff --git a/powerline/renderer.py b/powerline/renderer.py index c03c42e8..13c7a04f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -378,7 +378,9 @@ class Renderer(object): elif output_width: current_width = self._render_length(theme, segments, divider_widths) - rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() + rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + if rendered_highlighted: + rendered_highlighted += self.hlstyle() return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width) diff --git a/powerline/renderers/shell/tcsh.py b/powerline/renderers/shell/tcsh.py index d0d1d277..bf0697d1 100644 --- a/powerline/renderers/shell/tcsh.py +++ b/powerline/renderers/shell/tcsh.py @@ -12,5 +12,20 @@ class TcshPromptRenderer(ZshPromptRenderer): character_translations[ord('^')] = '\\^' character_translations[ord('!')] = '\\!' + def do_render(self, **kwargs): + ret = super(TcshPromptRenderer, self).do_render(**kwargs) + nbsp = self.character_translations.get(ord(' '), ' ') + end = self.hlstyle() + assert not ret or ret.endswith(end) + if ret.endswith(nbsp + end): + # Exchange nbsp and highlight end because tcsh removes trailing + # %{%} part of the prompt for whatever reason + ret = ret[:-(len(nbsp) + len(end))] + end + nbsp + else: + # We *must* end prompt with non-%{%} sequence for the reasons + # explained above. So add nbsp if it is not already there. + ret += nbsp + return ret + renderer = TcshPromptRenderer diff --git a/powerline/segments/common/bat.py b/powerline/segments/common/bat.py index ba0eaa97..732d8e11 100644 --- a/powerline/segments/common/bat.py +++ b/powerline/segments/common/bat.py @@ -125,6 +125,7 @@ def _get_battery(pl): pl.debug('Using windll to communicate with kernel32 (Windows)') from ctypes import windll library_loader = windll + class PowerClass(Structure): _fields_ = [ ('ACLineStatus', c_byte), diff --git a/powerline/vim.py b/powerline/vim.py index d4f5c2d6..53e23147 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -52,7 +52,31 @@ class VimPowerline(Powerline): super(VimPowerline, self).init('vim', **kwargs) self.last_window_id = 1 self.pyeval = pyeval - self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' + self.construct_window_statusline = self.create_window_statusline_constructor() + + if sys.version_info < (3,): + def create_window_statusline_constructor(self): + window_statusline = b'%!' + str(self.pyeval) + b'(\'powerline.statusline({0})\')' + return window_statusline.format + else: + def create_window_statusline_constructor(self): + startstr = b'%!' + self.pyeval.encode('ascii') + b'(\'powerline.statusline(' + endstr = b')\')' + return lambda idx: ( + startstr + str(idx).encode('ascii') + endstr + ) + + create_window_statusline_constructor.__doc__ = ( + '''Create function which returns &l:stl value being given window index + + Created function must return :py:class:`bytes` instance because this is + what ``window.options['statusline']`` returns (``window`` is + :py:class:`vim.Window` instance). + + :return: + Function with type ``int → bytes``. + ''' + ) default_log_stream = sys.stdout @@ -174,7 +198,7 @@ class VimPowerline(Powerline): pyeval = 'PowerlinePyeval' self.pyeval = pyeval - self.window_statusline = '%!' + pyeval + '(\'powerline.statusline({0})\')' + self.construct_window_statusline = self.create_window_statusline_constructor() self.update_renderer() __main__.powerline = self @@ -240,14 +264,14 @@ class VimPowerline(Powerline): curwindow_id = self.last_window_id self.last_window_id += 1 window.vars['powerline_window_id'] = curwindow_id - statusline = self.window_statusline.format(curwindow_id) + statusline = self.construct_window_statusline(curwindow_id) if window.options['statusline'] != statusline: window.options['statusline'] = statusline if curwindow_id == window_id if window_id else window is vim.current.window: r = (window, curwindow_id, window.number) return r else: - _vim_getwinvar = staticmethod(vim_get_func('getwinvar')) + _vim_getwinvar = staticmethod(vim_get_func('getwinvar', 'bytes')) _vim_setwinvar = staticmethod(vim_get_func('setwinvar')) def win_idx(self, window_id): @@ -260,7 +284,7 @@ class VimPowerline(Powerline): curwindow_id = self.last_window_id self.last_window_id += 1 self._vim_setwinvar(winnr, 'powerline_window_id', curwindow_id) - statusline = self.window_statusline.format(curwindow_id) + statusline = self.construct_window_statusline(curwindow_id) if self._vim_getwinvar(winnr, '&statusline') != statusline: self._vim_setwinvar(winnr, '&statusline', statusline) if curwindow_id == window_id if window_id else window is vim.current.window: diff --git a/scripts/powerline-release.py b/scripts/powerline-release.py index 242beccd..65d46b63 100755 --- a/scripts/powerline-release.py +++ b/scripts/powerline-release.py @@ -105,9 +105,10 @@ def merge(version_string, rev, **kwargs): check_call(['git', 'tag', '-m', 'Release ' + version_string, '-a', version_string]) -def push(version_string, **kwargs): +def push(version_string, rev, **kwargs): check_call(['git', 'push', 'upstream', 'master']) check_call(['git', 'push', 'upstream', version_string]) + check_call(['git', 'push', 'upstream', rev]) def upload(**args): diff --git a/setup.py b/setup.py index 74bdca23..6f1310ca 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ else: def get_version(): - base_version = '2.1' + base_version = '2.1.1' 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', + version='2.1.1', description='The ultimate statusline/prompt utility.', long_description=README, classifiers=[ diff --git a/tests/install.sh b/tests/install.sh index 974971a5..83b97719 100755 --- a/tests/install.sh +++ b/tests/install.sh @@ -5,7 +5,7 @@ git clone --depth=1 git://github.com/powerline/deps tests/bot-ci/deps . tests/bot-ci/scripts/common/main.sh sudo apt-get install -qq libssl1.0.0 -sudo apt-get install -qq screen zsh tcsh mksh busybox socat realpath bc rc tmux +sudo apt-get install -qq zsh tcsh mksh busybox socat realpath bc rc tmux if test -n "$USE_UCS2_PYTHON" ; then pip install virtualenvwrapper diff --git a/tests/run_vim_tests.sh b/tests/run_vim_tests.sh index 5fdc3975..b5bf8723 100755 --- a/tests/run_vim_tests.sh +++ b/tests/run_vim_tests.sh @@ -17,8 +17,12 @@ if test -z "$VIM" ; then if test "$PYTHON_IMPLEMENTATION" != "CPython" ; then exit 0 fi - NEW_VIM="$ROOT/tests/bot-ci/deps/vim/tip-$PYTHON_MM/vim" - OLD_VIM="$ROOT/tests/bot-ci/deps/vim/v7-0-112-$PYTHON_MM/vim" + if test -d "$ROOT/tests/bot-ci/deps" ; then + NEW_VIM="$ROOT/tests/bot-ci/deps/vim/tip-$PYTHON_MM/vim" + OLD_VIM="$ROOT/tests/bot-ci/deps/vim/v7-0-112-$PYTHON_MM/vim" + else + NEW_VIM="vim" + fi if test -e "$OLD_VIM" ; then VIMS="NEW_VIM OLD_VIM" else @@ -39,6 +43,9 @@ test_script() { local vim="$1" local script="$2" echo "Running script $script with $vim" + if ! test -e "$vim" ; then + return 0 + fi if ! "$vim" -u NONE -S $script || test -f message.fail ; then echo "Failed script $script run with $VIM" >&2 cat message.fail >&2 diff --git a/tests/test.sh b/tests/test.sh index 62b525df..fa92949f 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,20 +1,27 @@ #!/bin/bash . tests/bot-ci/scripts/common/main.sh +: ${USER:=`id -un`} +: ${HOME:=`getent passwd $USER | cut -d: -f6`} + +export USER HOME + FAILED=0 -export PATH="/opt/fish/bin:${PATH}" +if test "$TRAVIS" = true ; then + export PATH="/opt/fish/bin:${PATH}" -if test "$PYTHON_IMPLEMENTATION" = "CPython" ; then - export PATH="/opt/zsh-${PYTHON_MM}${USE_UCS2_PYTHON:+-ucs2}/bin:${PATH}" -fi + if test "$PYTHON_IMPLEMENTATION" = "CPython" ; then + export PATH="/opt/zsh-${PYTHON_MM}${USE_UCS2_PYTHON:+-ucs2}/bin:${PATH}" + fi -if test -n "$USE_UCS2_PYTHON" ; then - export LD_LIBRARY_PATH="/opt/cpython-ucs2-$UCS2_PYTHON_VARIANT/lib${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" - set +e - . virtualenvwrapper.sh - workon cpython-ucs2-$UCS2_PYTHON_VARIANT - set -e + if test -n "$USE_UCS2_PYTHON" ; then + export LD_LIBRARY_PATH="/opt/cpython-ucs2-$UCS2_PYTHON_VARIANT/lib${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" + set +e + . virtualenvwrapper.sh + workon cpython-ucs2-$UCS2_PYTHON_VARIANT + set -e + fi fi export PYTHON="${PYTHON:=python}" diff --git a/tests/test_foreign_stl_override.vim b/tests/test_foreign_stl_override.vim new file mode 100644 index 00000000..0cafb558 --- /dev/null +++ b/tests/test_foreign_stl_override.vim @@ -0,0 +1,22 @@ +scriptencoding utf-8 +set encoding=utf-8 +let g:powerline_config_paths = [expand(':p:h:h') . '/powerline/config_files'] +set laststatus=2 +redir => g:messages + try + source powerline/bindings/vim/plugin/powerline.vim + redrawstatus! + vsplit + redrawstatus! + setlocal statusline=«» + redrawstatus! + catch + call writefile(['Unexpected exception', v:exception], 'message.fail') + cquit + endtry +redir END +if g:messages =~# '\v\S' + call writefile(['Unexpected messages'] + split(g:messages, "\n", 1), 'message.fail') + cquit +endif +qall! diff --git a/tests/test_in_vterm/test.sh b/tests/test_in_vterm/test.sh index 447ad97d..8cb61545 100755 --- a/tests/test_in_vterm/test.sh +++ b/tests/test_in_vterm/test.sh @@ -19,8 +19,9 @@ cp -r tests/terminfo tests/vterm FAIL_SUMMARY="" test_tmux() { - if test "$PYTHON_IMPLEMENTATION" = PyPy && test "$PYTHON_VERSION_MAJOR" -eq 3 ; then - # FIXME PyPy3 segfaults for some reason + if test "$PYTHON_IMPLEMENTATION" = PyPy; then + # FIXME PyPy3 segfaults for some reason, PyPy does it as well, but + # occasionally. return 0 fi if ! which "${POWERLINE_TMUX_EXE}" ; then @@ -32,26 +33,16 @@ test_tmux() { echo "Failed vterm test $f" FAILED=1 FAIL_SUMMARY="$FAIL_SUMMARY${NL}F $POWERLINE_TMUX_EXE $f" - for file in tests/vterm/*.log ; do - if ! test -e "$file" ; then - break - fi - echo '____________________________________________________________' - echo "$file:" - echo '============================================================' - cat -v $file - done fi } if test -z "$POWERLINE_TMUX_EXE" && test -d tests/bot-ci/deps/tmux ; then for tmux in tests/bot-ci/deps/tmux/tmux-*/tmux ; do export POWERLINE_TMUX_EXE="$PWD/$tmux" - if test_tmux ; then - rm -f tests/vterm/*.log - fi + test_tmux || true done else + export POWERLINE_TMUX_EXE="${POWERLINE_TMUX_EXE:-tmux}" test_tmux || true fi diff --git a/tests/test_in_vterm/test_tmux.py b/tests/test_in_vterm/test_tmux.py index 2562939d..5968fc22 100755 --- a/tests/test_in_vterm/test_tmux.py +++ b/tests/test_in_vterm/test_tmux.py @@ -3,11 +3,13 @@ from __future__ import (unicode_literals, division, absolute_import, print_function) import os +import sys from time import sleep from subprocess import check_call from itertools import groupby from difflib import ndiff +from glob import glob1 from powerline.lib.unicode import u from powerline.bindings.tmux import get_tmux_version @@ -16,6 +18,9 @@ from powerline import get_fallback_logger from tests.lib.terminal import ExpectProcess +VTERM_TEST_DIR = os.path.abspath('tests/vterm') + + def cell_properties_key_to_shell_escape(cell_properties_key): fg, bg, bold, underline, italic = cell_properties_key return('\x1b[38;2;{0};48;2;{1}{bold}{underline}{italic}m'.format( @@ -27,11 +32,11 @@ def cell_properties_key_to_shell_escape(cell_properties_key): )) -def test_expected_result(p, expected_result, cols, rows): +def test_expected_result(p, expected_result, cols, rows, print_logs): last_line = [] for col in range(cols): last_line.append(p[rows - 1, col]) - attempts = 10 + attempts = 3 result = None while attempts: result = tuple(( @@ -78,21 +83,34 @@ def test_expected_result(p, expected_result, cols, rows): print('Diff:') print('=' * 80) print(''.join((u(line) for line in ndiff([a], [b])))) + if print_logs: + for f in glob1(VTERM_TEST_DIR, '*.log'): + print('_' * 80) + print(os.path.basename(f) + ':') + print('=' * 80) + with open(f, 'r') as F: + for line in F: + sys.stdout.write(line) + os.unlink(f) return False -def main(): - VTERM_TEST_DIR = os.path.abspath('tests/vterm') +def main(attempts=3): vterm_path = os.path.join(VTERM_TEST_DIR, 'path') - socket_path = os.path.join(VTERM_TEST_DIR, 'tmux-socket') + socket_path = 'tmux-socket' rows = 50 cols = 200 tmux_exe = os.path.join(vterm_path, 'tmux') + if os.path.exists('tests/bot-ci/deps/libvterm/libvterm.so'): + lib = 'tests/bot-ci/deps/libvterm/libvterm.so' + else: + lib = os.environ.get('POWERLINE_LIBVTERM', 'libvterm.so') + try: p = ExpectProcess( - lib='tests/bot-ci/deps/libvterm/libvterm.so', + lib=lib, rows=rows, cols=cols, cmd=tmux_exe, @@ -143,7 +161,7 @@ def main(): 'TERMINFO': os.path.join(VTERM_TEST_DIR, 'terminfo'), 'TERM': 'st-256color', 'PATH': vterm_path, - 'SHELL': os.path.join(''), + 'SHELL': os.path.join(VTERM_TEST_DIR, 'path', 'bash'), 'POWERLINE_CONFIG_PATHS': os.path.abspath('powerline/config_files'), 'POWERLINE_COMMAND': 'powerline-render', 'POWERLINE_THEME_OVERRIDES': ( @@ -158,6 +176,26 @@ def main(): ) p.start() sleep(2) + 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 '), + (((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), ' ' * 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 = ( (((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '), (((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '), @@ -174,7 +212,7 @@ def main(): (((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), ' '), + (((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 '), ) @@ -194,21 +232,31 @@ def main(): (((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), ' '), + (((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 < (1, 8): - expected_result = expected_result_old - else: + if tmux_version >= (2, 0): + expected_result = expected_result_2_0 + elif tmux_version >= (1, 8): expected_result = expected_result_new - return test_expected_result(p, expected_result, cols, rows) + else: + expected_result = expected_result_old + 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 finally: check_call([tmux_exe, '-S', socket_path, 'kill-server'], env={ 'PATH': vterm_path, 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH', ''), - }) + }, cwd=VTERM_TEST_DIR) + return main(attempts=(attempts - 1)) if __name__ == '__main__': diff --git a/tests/test_lib.py b/tests/test_lib.py index 8b2581ed..e6b28624 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -39,9 +39,9 @@ else: use_mercurial = True -GIT_REPO = 'git_repo' + os.environ.get('PYTHON', '') -HG_REPO = 'hg_repo' + os.environ.get('PYTHON', '') -BZR_REPO = 'bzr_repo' + os.environ.get('PYTHON', '') +GIT_REPO = 'git_repo' +HG_REPO = 'hg_repo' +BZR_REPO = 'bzr_repo' def thread_number(): diff --git a/tests/test_shells/bash.daemon.ok b/tests/test_shells/bash.daemon.ok index e4535b1e..4f25eb13 100644 --- a/tests/test_shells/bash.daemon.ok +++ b/tests/test_shells/bash.daemon.ok @@ -26,5 +26,16 @@ def   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`'   BRANCH  ⋯  shell  3rd  `echo`  cd ../'«Unicode!»' +  BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_LEFT" +  BRANCH  ⋯  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo + foo   +  BRANCH  ⋯  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR +  BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_FULL" +                                                                                                                                                                                                                                                                                                             +  BRANCH  ⋯  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo +                                                                                                                                                                                                                                                                                                       foo  +  BRANCH  ⋯  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR +                                                                                                                                                                                                                                                                                                             +  BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above   BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC   BRANCH $ABC⋯  shell  3rd  «Unicode!» $ABCfalse diff --git a/tests/test_shells/bash.nodaemon.ok b/tests/test_shells/bash.nodaemon.ok index 56dd1fc5..9bfc9cfe 100644 --- a/tests/test_shells/bash.nodaemon.ok +++ b/tests/test_shells/bash.nodaemon.ok @@ -26,5 +26,16 @@ def   BRANCH  ⋯  shell  3rd  (echo)  cd ../'$(echo)'   BRANCH  ⋯  shell  3rd  $(echo)  cd ../'`echo`'   BRANCH  ⋯  shell  3rd  `echo`  cd ../'«Unicode!»' +  BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_LEFT" +  BRANCH  ⋯  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo + foo   +  BRANCH  ⋯  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR +  BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_FULL" +                                                                                                                                                                                                                                                                                                             +  BRANCH  ⋯  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo +                                                                                                                                                                                                                                                                                                       foo  +  BRANCH  ⋯  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR +                                                                                                                                                                                                                                                                                                             +  BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above   BRANCH  ⋯  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC   BRANCH $ABC⋯  shell  3rd  «Unicode!» $ABCfalse diff --git a/tests/test_shells/fish.ok b/tests/test_shells/fish.ok index c69abfc5..e1044450 100644 --- a/tests/test_shells/fish.ok +++ b/tests/test_shells/fish.ok @@ -19,6 +19,22 @@    BRANCH     BRANCH     BRANCH  + USER  ⋯  shell  3rd  «Unicode!»   +   BRANCH  +   BRANCH  +   BRANCH  + foo   + USER  ⋯  shell  3rd  «Unicode!»   +   BRANCH  +   BRANCH  +   BRANCH  +                                                                                                                                                                                                                                                                                                            +                                                                                                                                                                                                                                                                                                      foo  +                                                                                                                                                                                                                                                                                                            + USER  ⋯  shell  3rd  «Unicode!»   +   BRANCH  +   BRANCH  +   BRANCH   INSERT  USER  ⋯  shell  3rd  «Unicode!»      BRANCH   DEFAULT  USER  ⋯  shell  3rd  «Unicode!»   diff --git a/tests/test_shells/input.bash b/tests/test_shells/input.bash index 0a1c0165..bbd8d0bb 100644 --- a/tests/test_shells/input.bash +++ b/tests/test_shells/input.bash @@ -5,6 +5,30 @@ set_theme() { export POWERLINE_CONFIG_OVERRIDES="ext.shell.theme=$1" } set_theme_option default_leftonly.segment_data.hostname.args.only_if_ssh false +ABOVE_LEFT='[{ + "left": [ + { + "function": "powerline.segments.common.env.environment", + "args": {"variable": "DISPLAYED_ENV_VAR"} + } + ] +}]' +ABOVE_FULL='[{ + "left": [ + { + "type": "string", + "name": "background", + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "function": "powerline.segments.common.env.environment", + "args": {"variable": "DISPLAYED_ENV_VAR"} + } + ] +}]' set_theme default_leftonly export VIRTUAL_ENV= source powerline/bindings/bash/powerline.sh @@ -31,6 +55,13 @@ cd ../'(echo)' cd ../'$(echo)' cd ../'`echo`' cd ../'«Unicode!»' +set_theme_option default_leftonly.segments.above "$ABOVE_LEFT" +export DISPLAYED_ENV_VAR=foo +unset DISPLAYED_ENV_VAR +set_theme_option default_leftonly.segments.above "$ABOVE_FULL" +export DISPLAYED_ENV_VAR=foo +unset DISPLAYED_ENV_VAR +set_theme_option default_leftonly.segments.above set_theme_option default_leftonly.dividers.left.hard \$ABC false true is the last line diff --git a/tests/test_shells/input.fish b/tests/test_shells/input.fish index 968248f1..4be37c31 100644 --- a/tests/test_shells/input.fish +++ b/tests/test_shells/input.fish @@ -4,6 +4,31 @@ end function set_theme set -g -x POWERLINE_CONFIG_OVERRIDES "ext.shell.theme=$argv" end +set -g -x POWERLINE_ +set -g ABOVE_LEFT '[{ + "left": [ + { + "function": "powerline.segments.common.env.environment", + "args": {"variable": "DISPLAYED_ENV_VAR"} + } + ] +}]' +set -g ABOVE_FULL '[{ + "left": [ + { + "type": "string", + "name": "background", + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "function": "powerline.segments.common.env.environment", + "args": {"variable": "DISPLAYED_ENV_VAR"} + } + ] +}]' set_theme_option default_leftonly.segment_data.hostname.args.only_if_ssh false set_theme default_leftonly set fish_function_path "$PWD/powerline/bindings/fish" $fish_function_path @@ -30,6 +55,13 @@ cd ../'$(echo)' cd ../'`echo`' cd ../'«Unicode!»' set_theme default +set_theme_option default.segments.above "$ABOVE_LEFT" +set -g -x DISPLAYED_ENV_VAR foo +set -g -x -e DISPLAYED_ENV_VAR +set_theme_option default.segments.above "$ABOVE_FULL" +set -g -x DISPLAYED_ENV_VAR foo +set -g -x -e DISPLAYED_ENV_VAR +set_theme_option default.segments.above '' set -g fish_key_bindings fish_vi_key_bindings ii false diff --git a/tests/test_shells/input.zsh b/tests/test_shells/input.zsh index 7775ba97..ab71ee6c 100644 --- a/tests/test_shells/input.zsh +++ b/tests/test_shells/input.zsh @@ -16,6 +16,30 @@ fi source powerline/bindings/zsh/powerline.zsh set_theme_option default_leftonly.segment_data.hostname.args.only_if_ssh false set_theme_option default.segment_data.hostname.args.only_if_ssh false +ABOVE_LEFT='[{ + "left": [ + { + "function": "powerline.segments.common.env.environment", + "args": {"variable": "DISPLAYED_ENV_VAR"} + } + ] +}]' +ABOVE_FULL='[{ + "left": [ + { + "type": "string", + "name": "background", + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "function": "powerline.segments.common.env.environment", + "args": {"variable": "DISPLAYED_ENV_VAR"} + } + ] +}]' set_theme default_leftonly export VIRTUAL_ENV= cd tests/shell/3rd @@ -51,6 +75,13 @@ done 1 cd . cd . +set_theme_option default.segments.above "$ABOVE_LEFT" +export DISPLAYED_ENV_VAR=foo +unset DISPLAYED_ENV_VAR +set_theme_option default.segments.above "$ABOVE_FULL" +export DISPLAYED_ENV_VAR=foo +unset DISPLAYED_ENV_VAR +set_theme_option default.segments.above hash -d foo=$PWD:h ; cd . set_theme_option default.dividers.left.hard \$ABC true diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index d544bf83..0e7f0923 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -31,6 +31,10 @@ user = os.environ['USER'] REFS_RE = re.compile(r'^\[\d+ refs\]\n') IPYPY_DEANSI_RE = re.compile(r'\033(?:\[(?:\?\d+[lh]|[^a-zA-Z]+[a-ln-zA-Z])|[=>])') +start_str = 'cd tests/shell/3rd' +if shell == 'pdb': + start_str = 'class Foo(object):' + with codecs.open(fname, 'r', encoding='utf-8') as R: with codecs.open(new_fname, 'w', encoding='utf-8') as W: found_cd = False @@ -38,11 +42,7 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: for line in (R if shell != 'fish' else R.read().split('\n')): i += 1 if not found_cd: - found_cd = ( - 'class Foo(object):' in line - if shell == 'pdb' else - 'cd tests/shell/3rd' in line - ) + found_cd = (start_str in line) continue if 'true is the last line' in line: break @@ -70,7 +70,7 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: try: start = line.index('\033[0;') end = line.index(' ', start) - line = line[start:end] + '\033[0m\n' + line = line[start:end] + '\n' except ValueError: line = '' elif shell == 'mksh': @@ -118,4 +118,8 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: line = '' elif line == '-> self.quitting = 1\n': line = '-> self.quitting = True\n' + elif line == '\n': + line = '' + if line == '-> self.quitting = True\n': + break W.write(line) diff --git a/tests/test_shells/run_script.py b/tests/test_shells/run_script.py new file mode 100755 index 00000000..3d5eddd5 --- /dev/null +++ b/tests/test_shells/run_script.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import argparse +import os +import re + +from time import sleep +from subprocess import check_call +from io import BytesIO + +import pexpect + + +def get_argparser(ArgumentParser=argparse.ArgumentParser): + parser = ArgumentParser(description='Run powerline shell test using pexpect') + parser.add_argument('--wait-for-echo', action='store_true', help='Wait until the input is echoed back.') + parser.add_argument('--type', metavar='TYPE', help='Test type (daemon, nodaemon, …).') + parser.add_argument('--client', metavar='CLIENT', help='Type of the client used (C, shell, zpython, …).') + parser.add_argument('--shell', metavar='SHELL', help='Shell name.') + parser.add_argument('command', nargs=argparse.REMAINDER, metavar='COMMAND', + help='Command to run and its argument.') + return parser + + +def main(): + parser = get_argparser() + args = parser.parse_args() + + shell = args.shell or args.command[0] + test_type = args.type or shell + test_client = args.client or test_type + + log_file_base = '{0}.{1}.{2}'.format(shell, test_type, test_client) + full_log_file_name = os.path.join('tests', 'shell', '{0}.full.log'.format(log_file_base)) + # postproc_log_file_name = os.path.join('tests', 'shell', '{0}.log'.format(log_file_base)) + + local_paths = [ + os.path.abspath(os.path.join('tests', 'shell', 'path')), + os.path.abspath('scripts'), + ] + + if test_type == 'fish': + local_paths += ['/usr/bin', '/bin'] + + python_paths = os.environ.get('PYTHONPATH', '') + if python_paths: + python_paths = ':' + python_paths + python_paths = os.path.abspath('.') + python_paths + + environ = { + 'LANG': 'en_US.UTF-8', + 'PATH': os.pathsep.join(local_paths), + 'TERM': 'screen-256color', + 'DIR1': os.environ['DIR1'], + 'DIR2': os.environ['DIR2'], + 'XDG_CONFIG_HOME': os.path.abspath(os.path.join('tests', 'shell', 'fish_home')), + 'IPYTHONDIR': os.path.abspath(os.path.join('tests', 'shell', 'ipython_home')), + 'PYTHONPATH': python_paths, + 'POWERLINE_CONFIG_OVERRIDES': os.environ.get('POWERLINE_CONFIG_OVERRIDES', ''), + 'POWERLINE_THEME_OVERRIDES': os.environ.get('POWERLINE_THEME_OVERRIDES', ''), + 'POWERLINE_CONFIG_PATHS': os.path.abspath(os.path.join('powerline', 'config_files')), + 'POWERLINE_COMMAND_ARGS': os.environ.get('POWERLINE_COMMAND_ARGS', ''), + 'POWERLINE_COMMAND': os.environ.get('POWERLINE_COMMAND', ''), + 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH', ''), + } + + os.environ['PATH'] = environ['PATH'] + + if test_type == 'daemon': + environ['POWERLINE_SHELL_CONTINUATION'] = '1' + environ['POWERLINE_SHELL_SELECT'] = '1' + + if test_type != 'zpython' and shell == 'zsh': + environ['POWERLINE_NO_ZSH_ZPYTHON'] = '1' + + sio = BytesIO() + + child = pexpect.spawn( + args.command[0], + args.command[1:], + env=environ, + logfile=sio, + timeout=30, + ) + child.expect(re.compile(b'.*')) + sleep(0.5) + child.setwinsize(1, 300) + + with open(os.path.join('tests', 'test_shells', 'input.{0}'.format(shell)), 'rb') as F: + if not args.wait_for_echo: + child.send(F.read()) + else: + for line in F: + child.send(line) + sleep(1) + # TODO Implement something more smart + + with open(full_log_file_name, 'wb') as LF: + while True: + try: + s = child.read_nonblocking(1000) + except pexpect.TIMEOUT: + break + except pexpect.EOF: + break + else: + LF.write(s) + + child.close(force=True) + + check_call([ + os.path.join('tests', 'shell', 'path', 'python'), + os.path.join('tests', 'test_shells', 'postproc.py'), + test_type, test_client, shell + ]) + pidfile = os.path.join('tests', 'shell', '3rd', 'pid') + if os.path.exists(pidfile): + os.unlink(pidfile) + + +if __name__ == '__main__': + main() diff --git a/tests/test_shells/screenrc b/tests/test_shells/screenrc deleted file mode 100644 index ba25fd28..00000000 --- a/tests/test_shells/screenrc +++ /dev/null @@ -1,7 +0,0 @@ -width 1024 -height 1 -logfile "tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log" -# Having utf8 setting on causes screen to recode file passed to readreg. Adding -# `-e utf8` just after `readreg` causes screen to fail with `-X: copyreg: -# character, ^x, or (octal) \032 expected.` in place of fixing the issue. -defutf8 off diff --git a/tests/test_shells/tcsh.ok b/tests/test_shells/tcsh.ok index 03c2527c..fd5d5914 100644 --- a/tests/test_shells/tcsh.ok +++ b/tests/test_shells/tcsh.ok @@ -1,17 +1,17 @@ -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git   -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   -  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd   -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1   -  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`   -  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  «Unicode!»   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  .git   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER  ⓔ  some-virtual-environment   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd  1   +  HOSTNAME  USER   BRANCH  ⋯  tests  shell  3rd   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^[[32m   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  ^H   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  \[\]   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  %%   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  #[bold]   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  (echo)   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  $(echo)   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  `echo`   +  HOSTNAME  USER   BRANCH  ⋯  shell  3rd  «Unicode!»   diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 14430361..6c95b71f 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -47,95 +47,38 @@ check_screen_log() { fi } -run() { - TEST_TYPE="$1" - shift - TEST_CLIENT="$1" - shift - SH="$1" - shift - local local_path="$PWD/tests/shell/path:$PWD/scripts" - if test "x$SH" = "xfish" ; then - local_path="${local_path}:/usr/bin:/bin" - fi - if test $TEST_TYPE = daemon ; then - local additional_prompts=1 - else - local additional_prompts= - fi - env -i \ - LANG=en_US.UTF-8 \ - PATH="$local_path" \ - TERM="screen-256color" \ - COLUMNS="${COLUMNS}" \ - LINES="${LINES}" \ - TEST_TYPE="${TEST_TYPE}" \ - TEST_CLIENT="${TEST_CLIENT}" \ - SH="${SH}" \ - DIR1="${DIR1}" \ - POWERLINE_NO_ZSH_ZPYTHON="$(test $TEST_TYPE = zpython || echo 1)" \ - DIR2="${DIR2}" \ - XDG_CONFIG_HOME="$PWD/tests/shell/fish_home" \ - IPYTHONDIR="$PWD/tests/shell/ipython_home" \ - PYTHONPATH="${PWD}${PYTHONPATH:+:}$PYTHONPATH" \ - POWERLINE_CONFIG_OVERRIDES="${POWERLINE_CONFIG_OVERRIDES}" \ - POWERLINE_THEME_OVERRIDES="${POWERLINE_THEME_OVERRIDES}" \ - POWERLINE_SHELL_CONTINUATION=$additional_prompts \ - POWERLINE_SHELL_SELECT=$additional_prompts \ - POWERLINE_CONFIG_PATHS="$PWD/powerline/config_files" \ - POWERLINE_COMMAND_ARGS="${POWERLINE_COMMAND_ARGS}" \ - POWERLINE_COMMAND="${POWERLINE_COMMAND}" \ - "$@" -} - # HACK: get newline for use in strings given that "\n" and $'' do not work. NL="$(printf '\nE')" NL="${NL%E}" +print_full_output() { + TEST_TYPE="$1" + TEST_CLIENT="$2" + SH="$3" + echo "Full output:" + echo '============================================================' + cat tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log + echo + echo '____________________________________________________________' + if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then + echo "Full output (cat -v):" + echo '============================================================' + cat -v tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log + echo + echo '____________________________________________________________' + fi +} + do_run_test() { TEST_TYPE="$1" shift TEST_CLIENT="$1" shift SH="$1" - SESNAME="powerline-shell-test-${SH}-$$" - # Note: when running screen with setuid libc unsets LD_LIBRARY_PATH, so it - # cannot be added to the `env -i` call above. - run "${TEST_TYPE}" "${TEST_CLIENT}" "${SH}" \ - screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ - env LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" \ - "$@" - while ! screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH ; do - sleep 0.1s - done - # Wait for screen to initialize - sleep 1 - local attempts=100 - while ! screen -S "$SESNAME" -p 0 -X width 300 1 >/dev/null ; do - sleep 0.1s - attempts=$(( attempts - 1 )) - if test $attempts -eq 0 ; then - echo "Waiting for too long: assuming test failed" - echo "Failed ${SH}. Full output:" - echo '============================================================' - cat tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log - echo '____________________________________________________________' - if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then - echo "Full output (cat -v):" - echo '============================================================' - cat -v tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log - echo '____________________________________________________________' - fi - return 1 - fi - done + local wait_for_echo_arg= if ( \ test "x${SH}" = "xdash" \ - || ( \ - test "x${SH}" = "xipython" \ - && test "$PYTHON_IMPLEMENTATION" = "PyPy" \ - ) \ || ( \ test "x${SH}" = "xpdb" \ && ( \ @@ -148,28 +91,11 @@ do_run_test() { ) \ ) \ ) ; then - # If I do not use this hack for dash then output will look like - # - # command1 - # command2 - # … - # prompt1> prompt2> … - while read -r line ; do - if test "$(screen -S "$SESNAME" -p 0 -X stuff "$line$NL")" = "No screen session found." ; then - break - fi - sleep 1 - done < tests/test_shells/input.$SH - else - screen -S "$SESNAME" -p 0 -X paste a + wait_for_echo_arg="--wait-for-echo" fi - # Wait for screen to exit (sending command to non-existing screen session - # fails; when launched instance exits corresponding session is deleted) - while screen -S "$SESNAME" -X blankerprg "" > /dev/null ; do - sleep 0.1s - done - ${PYTHON} ./tests/test_shells/postproc.py ${TEST_TYPE} ${TEST_CLIENT} ${SH} - rm -f tests/shell/3rd/pid + "${PYTHON}" tests/test_shells/run_script.py \ + $wait_for_echo_arg --type=${TEST_TYPE} --client=${TEST_CLIENT} --shell=${SH} \ + "$@" if ! check_screen_log ${TEST_TYPE} ${TEST_CLIENT} ${SH} ; then echo '____________________________________________________________' if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then @@ -179,16 +105,8 @@ do_run_test() { check_screen_log ${TEST_TYPE} ${TEST_CLIENT} ${SH} | cat -v echo '____________________________________________________________' fi - echo "Failed ${SH}. Full output:" - echo '============================================================' - cat tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log - echo '____________________________________________________________' - if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then - echo "Full output (cat -v):" - echo '============================================================' - cat -v tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log - echo '____________________________________________________________' - fi + echo -n "Failed ${SH}. " + print_full_output ${TEST_TYPE} ${TEST_CLIENT} ${SH} case ${SH} in *ksh) ${SH} -c 'echo ${KSH_VERSION}' @@ -251,7 +169,6 @@ cp -r tests/test_shells/ipython_home tests/shell mkdir tests/shell/path ln -s "$(which "${PYTHON}")" tests/shell/path/python -ln -s "$(which screen)" tests/shell/path ln -s "$(which env)" tests/shell/path ln -s "$(which git)" tests/shell/path ln -s "$(which sleep)" tests/shell/path @@ -274,6 +191,7 @@ ln -s "$(which rm)" tests/shell/path ln -s "$(which uname)" tests/shell/path ln -s "$(which test)" tests/shell/path ln -s "$(which pwd)" tests/shell/path +ln -s "$(which hostname)" tests/shell/path ln -s ../../test_shells/bgscript.sh tests/shell/path ln -s ../../test_shells/waitpid.sh tests/shell/path if which socat ; then @@ -294,6 +212,8 @@ done ln -s python tests/shell/path/pdb PDB_PYTHON=pdb +ln -s python tests/shell/path/ipython +IPYTHON_PYTHON=ipython if test -z "$POWERLINE_RC_EXE" ; then if which rc-status >/dev/null ; then @@ -309,12 +229,42 @@ if which "$POWERLINE_RC_EXE" >/dev/null ; then ln -s "$(which $POWERLINE_RC_EXE)" tests/shell/path/rc fi -for exe in bash zsh busybox fish tcsh mksh dash ipython ; do +for exe in bash zsh busybox fish tcsh mksh dash ; do if which $exe >/dev/null ; then + if test "$exe" = "fish" ; then + fish_version="$(fish --version 2>&1)" + fish_version="${fish_version##* }" + fish_version_major="${fish_version%%.*}" + if test "$fish_version_major" != "$fish_version" ; then + # No dot is in development version compiled by bot-ci + fish_version_minor="${fish_version#*.}" + fish_version_patch="${fish_version_minor#*.}" + fish_version_dev="${fish_version_patch#*-}" + if test "$fish_version_dev" = "$fish_version_patch" ; then + fish_version_dev="" + fi + fish_version_minor="${fish_version_minor%%.*}" + fish_version_patch="${fish_version_patch%%-*}" + if test $fish_version_major -lt 2 || ( \ + test $fish_version_major -eq 2 && (\ + test $fish_version_minor -lt 1 || (\ + test $fish_version_minor -eq 1 && + test $fish_version_patch -lt 2 && \ + test -z "$fish_version_dev" + ) \ + ) \ + ) ; then + continue + fi + fi + fi ln -s "$(which $exe)" tests/shell/path fi done +mkdir tests/shell/home +export HOME="$PWD/tests/shell/home" + unset ENV export ADDRESS="powerline-ipc-test-$$" @@ -389,15 +339,14 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te continue fi fi - if test "$TEST_CLIENT" = "shell" && ! which socat >/dev/null ; then + if test "$TEST_CLIENT" = "shell" && ! test -x tests/shell/path/socat ; then continue fi if test "x$ONLY_TEST_CLIENT" != "x" && test "x$TEST_CLIENT" != "x$ONLY_TEST_CLIENT" ; then continue fi - POWERLINE_COMMAND_ARGS="--socket $ADDRESS" - POWERLINE_COMMAND="$POWERLINE_COMMAND" - export POWERLINE_COMMAND + export POWERLINE_COMMAND_ARGS="--socket $ADDRESS" + export POWERLINE_COMMAND="$POWERLINE_COMMAND" echo ">> powerline command is ${POWERLINE_COMMAND:-empty}" J=-1 for TEST_COMMAND in \ @@ -421,6 +370,9 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te if test x$FAST$SH = x1dash ; then continue fi + if test x$FAST$SH = x1fish ; then + continue + fi if test "x$ONLY_SHELL" != "x" && test "x$ONLY_SHELL" != "x$SH" ; then continue fi @@ -469,7 +421,7 @@ fi if ( test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xzsh" ) \ && ( test "x${ONLY_TEST_TYPE}" = "x" || test "x${ONLY_TEST_TYPE}" = "xzpython" ) \ - && zsh -f -c 'zmodload libzpython' 2>/dev/null; then + && zsh tests/test_shells/zsh_test_script.zsh 2>/dev/null; then echo "> zpython" if ! run_test zpython zpython zsh -f -i ; then FAILED=1 @@ -478,7 +430,7 @@ if ( test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xzsh" ) \ fi if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xpdb" ; then - if ! ( test "$PYTHON_IMPLEMENTATION" = "PyPy" && test "$PYTHON_VERSION_MAJOR" = 2 ) ; then + if test "$PYTHON_IMPLEMENTATION" != "PyPy" ; then if test "x${ONLY_TEST_TYPE}" = "x" || test "x${ONLY_TEST_TYPE}" = "xsubclass" ; then echo "> pdb subclass" if ! run_test subclass python $PDB_PYTHON "$PWD/tests/test_shells/pdb-main.py" ; then @@ -501,12 +453,12 @@ if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xpdb" ; then fi if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xipython" ; then - if which ipython >/dev/null ; then + if "${PYTHON}" -c "try: import IPython${NL}except ImportError: raise SystemExit(1)" ; then # Define some overrides which should be ignored by IPython. - POWERLINE_CONFIG_OVERRIDES='common.term_escape_style=fbterm' - POWERLINE_THEME_OVERRIDES='in.segments.left=[]' - echo "> $(which ipython)" - if ! run_test ipython ipython ipython ; then + export POWERLINE_CONFIG_OVERRIDES='common.term_escape_style=fbterm' + export POWERLINE_THEME_OVERRIDES='in.segments.left=[]' + echo "> ipython" + if ! run_test ipython ipython ${IPYTHON_PYTHON} -mIPython ; then FAILED=1 FAIL_SUMMARY="${FAIL_SUMMARY}${NL}T ipython" fi diff --git a/tests/test_shells/zsh.daemon.ok b/tests/test_shells/zsh.daemon.ok index 53ff5b4c..daf415dc 100644 --- a/tests/test_shells/zsh.daemon.ok +++ b/tests/test_shells/zsh.daemon.ok @@ -36,6 +36,17 @@ abc def  INSERT  ⋯  tests  shell  3rd  cd .  INSERT  ⋯  tests  shell  3rd  cd . + INSERT  ⋯  tests  shell  3rd  set_theme_option default.segments.above "$ABOVE_LEFT" + INSERT  ⋯  tests  shell  3rd  export DISPLAYED_ENV_VAR=foo + foo   + INSERT  ⋯  tests  shell  3rd  unset DISPLAYED_ENV_VAR + INSERT  ⋯  tests  shell  3rd  set_theme_option default.segments.above "$ABOVE_FULL" +                                                                                                                                                                                                                                                                                                            + INSERT  ⋯  tests  shell  3rd  export DISPLAYED_ENV_VAR=foo +                                                                                                                                                                                                                                                                                                      foo  + INSERT  ⋯  tests  shell  3rd  unset DISPLAYED_ENV_VAR +                                                                                                                                                                                                                                                                                                            + INSERT  ⋯  tests  shell  3rd  set_theme_option default.segments.above  INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd .  INSERT  ~foo  3rd  set_theme_option default.dividers.left.hard \$ABC  INSERT $ABC~foo  3rd $ABCtrue diff --git a/tests/test_shells/zsh.nodaemon.ok b/tests/test_shells/zsh.nodaemon.ok index 5883a780..6a813f93 100644 --- a/tests/test_shells/zsh.nodaemon.ok +++ b/tests/test_shells/zsh.nodaemon.ok @@ -36,6 +36,17 @@ abc def  INSERT  ⋯  tests  shell  3rd  cd .  INSERT  ⋯  tests  shell  3rd  cd . + INSERT  ⋯  tests  shell  3rd  set_theme_option default.segments.above "$ABOVE_LEFT" + INSERT  ⋯  tests  shell  3rd  export DISPLAYED_ENV_VAR=foo + foo   + INSERT  ⋯  tests  shell  3rd  unset DISPLAYED_ENV_VAR + INSERT  ⋯  tests  shell  3rd  set_theme_option default.segments.above "$ABOVE_FULL" +                                                                                                                                                                                                                                                                                                            + INSERT  ⋯  tests  shell  3rd  export DISPLAYED_ENV_VAR=foo +                                                                                                                                                                                                                                                                                                      foo  + INSERT  ⋯  tests  shell  3rd  unset DISPLAYED_ENV_VAR +                                                                                                                                                                                                                                                                                                            + INSERT  ⋯  tests  shell  3rd  set_theme_option default.segments.above  INSERT  ⋯  tests  shell  3rd  hash -d foo=$PWD:h ; cd .  INSERT  ~foo  3rd  set_theme_option default.dividers.left.hard \$ABC  INSERT $ABC~foo  3rd $ABCtrue diff --git a/tests/test_shells/zsh_test_script.zsh b/tests/test_shells/zsh_test_script.zsh new file mode 100644 index 00000000..f4b894ce --- /dev/null +++ b/tests/test_shells/zsh_test_script.zsh @@ -0,0 +1,9 @@ +set -e +. tests/bot-ci/scripts/common/main.sh +zmodload zpython +zpython 'import platform' +zpython 'zsh.setvalue("ZSH_PYTHON_VERSION", platform.python_version())' +zpython 'zsh.setvalue("ZSH_PYTHON_IMPLEMENTATION", platform.python_implementation())' + +[[ $ZSH_PYTHON_IMPLEMENTATION = $PYTHON_IMPLEMENTATION ]] +[[ $ZSH_PYTHON_VERSION = $PYTHON_VERSION ]]