diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index 8e3782d6..501b48aa 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -54,14 +54,12 @@ _powerline_set_prompt() { _powerline_setup_prompt() { VIRTUAL_ENV_DISABLE_PROMPT=1 if test -z "${POWERLINE_COMMAND}" ; then - if which powerline &>/dev/null ; then - export POWERLINE_COMMAND=powerline - elif which powerline-render &>/dev/null ; then - export POWERLINE_COMMAND=powerline-render + if which powerline-config &>/dev/null ; then + export POWERLINE_COMMAND="$(powerline-config shell command)" else # `$0` is set to `-bash` when using SSH so that won't work local powerline_dir="$(dirname "$BASH_SOURCE")/../../.." - export POWERLINE_COMMAND="$powerline_dir/scripts/powerline" + export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" fi fi test "x$PROMPT_COMMAND" != "x${PROMPT_COMMAND%_powerline_set_prompt*}" || diff --git a/powerline/bindings/config.py b/powerline/bindings/config.py index 3591903a..846c66f0 100644 --- a/powerline/bindings/config.py +++ b/powerline/bindings/config.py @@ -6,11 +6,12 @@ from collections import namedtuple import os import subprocess import re +import sys -from powerline.config import TMUX_CONFIG_DIRECTORY +from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY from powerline.lib.config import ConfigLoader from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config -from powerline.lib.shell import run_cmd +from powerline.lib.shell import run_cmd, which TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) @@ -119,3 +120,35 @@ def create_powerline_logger(args): common_config = finish_common_config(config['common']) logger = create_logger(common_config) return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config') + + +def check_command(cmd): + if which(cmd): + print(cmd) + sys.exit(0) + + +def shell_command(pl, args): + '''Deduce which command to use for ``powerline`` + + Candidates: + + * ``powerline``. Present only when installed system-wide. + * ``{powerline_root}/scripts/powerline``. Present after ``pip install -e`` + was run and C client was compiled (in this case ``pip`` does not install + binary file). + * ``{powerline_root}/client/powerline.sh``. Useful when ``sh``, ``sed`` and + ``socat`` are present, but ``pip`` or ``setup.py`` was not run. + * ``{powerline_root}/client/powerline.py``. Like above, but when one of + ``sh``, ``sed`` and ``socat`` was not present. + * ``powerline-render``. Should not really ever be used. + * ``{powerline_root}/scripts/powerline-render``. Same. + ''' + check_command('powerline') + check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline')) + if which('sh') and which('sed') and which('socat'): + check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.sh')) + check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.py')) + check_command('powerline-render') + check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline-render')) + sys.exit(1) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 32f50131..89fe9778 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -20,12 +20,11 @@ function powerline-setup if test -z "$POWERLINE_NO_FISH_PROMPT$POWERLINE_NO_SHELL_PROMPT" if test -z "$POWERLINE_COMMAND" - if which powerline >/dev/null - set -g -x POWERLINE_COMMAND powerline - else if which powerline-render >/dev/null - set -g -x POWERLINE_COMMAND powerline-render + if false ;and which powerline-config >/dev/null + set -g -x POWERLINE_COMMAND (powerline-config shell command) else - set -g -x POWERLINE_COMMAND (dirname (status -f))/../../../scripts/powerline + set -l powerline_dir (dirname (status -f))/../../.. + set -g -x POWERLINE_COMMAND (eval $powerline_dir/scripts/powerline-config shell command) end end function --on-variable POWERLINE_COMMAND _powerline_update diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index 6f45ffde..ae5c035f 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -1,3 +1,4 @@ +_POWERLINE_SOURCED="$_" _powerline_columns_fallback() { if which stty >/dev/null ; then # Ksh does not have “local” built-in @@ -97,13 +98,11 @@ _powerline_set_set_jobs() { _powerline_set_command() { if test -z "${POWERLINE_COMMAND}" ; then - if which powerline-client >/dev/null ; then - export POWERLINE_COMMAND=powerline-client - elif which powerline >/dev/null ; then - export POWERLINE_COMMAND=powerline + if which powerline-config &>/dev/null ; then + export POWERLINE_COMMAND="$(powerline-config shell command)" else - # `$0` is set to `-bash` when using SSH so that won't work - export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" + local powerline_dir="$(dirname "$POWERLINE_SOURCED")/../../.." + export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" fi fi } diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 1ff61cb2..69f34a2e 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -11,12 +11,10 @@ if ! ( $?POWERLINE_NO_TCSH_TMUX_SUPPORT || $?POWERLINE_NO_SHELL_TMUX_SUPPORT ) t endif if ! ( $?POWERLINE_NO_TCSH_PROMPT || $?POWERLINE_NO_SHELL_PROMPT ) then if ! $?POWERLINE_COMMAND then - if ( { which powerline > /dev/null } ) then - setenv POWERLINE_COMMAND powerline - else if ( { which powerline-render > /dev/null } ) then - setenv POWERLINE_COMMAND powerline-render + if ( { which powerline-config > /dev/null } ) then + setenv POWERLINE_COMMAND "`powerline-config shell command`" else - setenv POWERLINE_COMMAND $POWERLINE_SOURCED:h:h:h:h:q/scripts/powerline + setenv POWERLINE_COMMAND "`$POWERLINE_SOURCED[2]:h:h:h:h:q/scripts/powerline-config shell command`" endif endif diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index a9717749..5439167a 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -1,3 +1,5 @@ +_POWERLINE_SOURCED="$0:A" + _powerline_columns_fallback() { if which stty &>/dev/null ; then local cols="$(stty size 2>/dev/null)" @@ -124,12 +126,11 @@ _powerline_setup_prompt() { zpython 'del _powerline_setup' else if test -z "${POWERLINE_COMMAND}" ; then - if which powerline &>/dev/null ; then - export POWERLINE_COMMAND=powerline - elif which powerline-render &>/dev/null ; then - export POWERLINE_COMMAND=powerline-render + if which powerline-config &>/dev/null ; then + export POWERLINE_COMMAND="$(powerline-config shell command)" else - export POWERLINE_COMMAND="$0:A:h:h:h:h/scripts/powerline" + local powerline_dir="$POWERLINE_SOURCED:h:h:h:h" + export POWERLINE_COMMAND="$($powerline_dir/scripts/powerline-config shell command)" fi fi diff --git a/powerline/config.py b/powerline/config.py index 53e530f4..c895da5e 100644 --- a/powerline/config.py +++ b/powerline/config.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals, print_function import os -BINDINGS_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'bindings') +POWERLINE_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BINDINGS_DIRECTORY = os.path.join(POWERLINE_ROOT, 'powerline', 'bindings') TMUX_CONFIG_DIRECTORY = os.path.join(BINDINGS_DIRECTORY, 'tmux') DEFAULT_SYSTEM_CONFIG_DIR = None diff --git a/powerline/lib/shell.py b/powerline/lib/shell.py index ea777770..4418c3d9 100644 --- a/powerline/lib/shell.py +++ b/powerline/lib/shell.py @@ -6,6 +6,7 @@ from subprocess import Popen, PIPE from locale import getlocale, getdefaultlocale, LC_MESSAGES from functools import partial import sys +import os if sys.platform.startswith('win32'): @@ -60,3 +61,71 @@ def readlines(cmd, cwd): with p.stdout: for line in p.stdout: yield line[:-1].decode(encoding) + + +try: + from shutil import which +except ImportError: + # shutil.which was added in python-3.3. Here is what was added: + # Lib/shutil.py, commit 5abe28a9c8fe701ba19b1db5190863384e96c798 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): # NOQA + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None diff --git a/scripts/powerline-config b/scripts/powerline-config index bcece7e6..2e636437 100755 --- a/scripts/powerline-config +++ b/scripts/powerline-config @@ -18,6 +18,11 @@ TMUX_ACTIONS = { } +SHELL_ACTIONS = { + 'command': config.shell_command, +} + + if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) subparsers = parser.add_subparsers() @@ -30,6 +35,14 @@ if __name__ == '__main__': help='If action is "source" then version-specific tmux configuration files are sourced.' ) + shell_parser = subparsers.add_parser('shell', help='Shell-specific commands') + shell_parser.add_argument( + 'function', + choices=tuple(SHELL_ACTIONS.values()), + type=(lambda v: SHELL_ACTIONS.get(v)), + help='If action is "command" then preferred powerline command is output', + ) + args = parser.parse_args() pl = config.create_powerline_logger(args)