From a8da67e2aabf367687d548b7d15e718012f43c9f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 20 Jul 2014 00:24:12 +0400 Subject: [PATCH] Add various other shell implementations support Tested in busybox, mksh, ksh and dash. Ksh has issues described in troubleshooting. --- .../installation/troubleshooting-common.rst | 7 + docs/source/overview.rst | 32 ++++ powerline/bindings/shell/powerline.sh | 181 ++++++++++++++++++ powerline/renderers/ksh_prompt.py | 20 ++ 4 files changed, 240 insertions(+) create mode 100644 powerline/bindings/shell/powerline.sh create mode 100644 powerline/renderers/ksh_prompt.py diff --git a/docs/source/installation/troubleshooting-common.rst b/docs/source/installation/troubleshooting-common.rst index 98abdd54..bb688d7e 100644 --- a/docs/source/installation/troubleshooting-common.rst +++ b/docs/source/installation/troubleshooting-common.rst @@ -132,3 +132,10 @@ To get rid of these lags there currently are two options: the future. * Compile and install ``libzpython`` module that lives in https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. + +Prompt is spoiled after completing files in ksh +----------------------------------------------- + +This is exactly why powerline has official mksh support, but not official ksh +support. If you know the solution feel free to share it in `powerline bug +tracker `_. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 23b16942..3b44268d 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -166,6 +166,38 @@ is the absolute path to your Powerline installation directory: .. _tmux-statusline: +Busybox (ash), mksh and dash prompt +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +After launching busybox run the following command: + +.. code-block:: bash + + . {repository_root}/powerline/bindings/shell/powerline.sh + +Mksh users may put this line into ``~/.mkshrc`` file. Dash users may use the +following in ``~/.profile``: + +.. code-block:: bash + + if test "x$0" != "x${0#dash}" ; then + export ENV={repository_root}/powerline/bindings/shell/powerline.sh + fi + +.. note:: + Dash users that already have ``$ENV`` defined should either put the ``. + …/shell/powerline.sh`` line in the ``$ENV`` file or create a new file which + will source (using ``.`` command) both former ``$ENV`` file and + :file:`powerline.sh` files and set ``$ENV`` to the path of this new file. + +.. warning:: + Job count is using some weird hack that uses signals and temporary files for + interprocess communication. It may be wrong sometimes. Not the case in mksh. + +.. warning:: + Busybox has two shells: ``ash`` and ``hush``. Second is known to segfault in + busybox 1.22.1 when using :file:`powerline.sh` script. + Tmux statusline --------------- diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh new file mode 100644 index 00000000..6f45ffde --- /dev/null +++ b/powerline/bindings/shell/powerline.sh @@ -0,0 +1,181 @@ +_powerline_columns_fallback() { + if which stty >/dev/null ; then + # Ksh does not have “local” built-in + _powerline_cols="$(stty size 2>/dev/null)" + if ! test -z "$_powerline_cols" ; then + echo "${_powerline_cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + +_powerline_has_jobs_in_subshell() { + if test -n "$_POWERLINE_HAS_JOBS_IN_SUBSHELL" ; then + return $_POWERLINE_HAS_JOBS_IN_SUBSHELL + elif test -z "$1" ; then + sleep 1 & + # Check whether shell outputs anything in a subshell when using jobs + # built-in. Shells like dash will not output anything meaning that + # I have to bother with temporary files. + test "$(jobs -p|wc -l)" -gt 0 + else + case "$1" in + dash|bb|ash) return 1 ;; + mksh|ksh|bash) return 0 ;; + *) _powerline_has_jobs_in_subshell ;; + esac + fi + _POWERLINE_HAS_JOBS_IN_SUBSHELL=$? + return $_POWERLINE_HAS_JOBS_IN_SUBSHELL +} + +_powerline_set_append_trap() { + if _powerline_has_jobs_in_subshell "$@" ; then + _powerline_append_trap() { + # Arguments: command, signal + # Ksh does not have “local” built-in + _powerline_traps="$(trap)" + if echo "$_powerline_traps" | grep -cm1 $2'$' >/dev/null ; then + _powerline_traps="$(echo "$_powerline_traps" | sed "s/ $2/'\\n$1' $2/")" + eval "$_powerline_traps" + fi + } + else + _powerline_append_trap() { + # Arguments: command, signal + _powerline_create_temp + trap > $_POWERLINE_TEMP + if grep -cm1 $2'$' $_POWERLINE_TEMP >/dev/null ; then + sed -i -e "s/ $2/'\\n$1' $2/" + . $_POWERLINE_TEMP + else + trap "$1" $2 + fi + echo -n > $_POWERLINE_TEMP + } + fi + _powerline_set_append_trap() { + return 0 + } +} + +_powerline_create_temp() { + if test -z "$_POWERLINE_TEMP" || ! test -e "$_POWERLINE_TEMP" ; then + _POWERLINE_TEMP="$(mktemp)" + _powerline_append_trap 'rm $_POWERLINE_TEMP' EXIT + fi +} + +_powerline_set_set_jobs() { + if _powerline_has_jobs_in_subshell "$@" ; then + _powerline_set_jobs() { + _POWERLINE_JOBS="$(jobs -p|wc -l)" + } + else + _powerline_set_append_trap "$@" + _POWERLINE_PID=$$ + _powerline_append_trap '_powerline_do_set_jobs' USR1 + _powerline_do_set_jobs() { + _powerline_create_temp + jobs -p > $_POWERLINE_TEMP + } + # This command will always be launched from a subshell, thus a hack is + # needed to run `jobs -p` outside of the subshell. + _powerline_set_jobs() { + kill -USR1 $_POWERLINE_PID + # Note: most likely this will read data from the previous run. Tests + # show that it is OK for some reasons. + _POWERLINE_JOBS="$(wc -l < $_POWERLINE_TEMP)" + } + fi + _powerline_set_set_jobs() { + return 0 + } +} + +_powerline_set_command() { + if test -z "${POWERLINE_COMMAND}" ; then + if which powerline-client >/dev/null ; then + export POWERLINE_COMMAND=powerline-client + elif which powerline >/dev/null ; then + export POWERLINE_COMMAND=powerline + else + # `$0` is set to `-bash` when using SSH so that won't work + export POWERLINE_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline" + fi + fi +} + +_powerline_tmux_setenv() { + TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`tmux display -p "#D" | tr -d %` "$2" + TMUX="$_POWERLINE_TMUX" tmux refresh -S +} + +_powerline_tmux_set_pwd() { + if test "x$_POWERLINE_SAVED_PWD" != "x$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" +} + +_powerline_set_renderer_arg() { + case "$1" in + bb|ash) _POWERLINE_RENDERER_ARG="-rbash_prompt" ;; + mksh|ksh) _POWERLINE_RENDERER_ARG="-rksh_prompt" ;; + bash|dash) _POWERLINE_RENDERER_ARG= ;; + esac +} + +_powerline_set_jobs() { + _powerline_set_set_jobs + _powerline_set_jobs +} + +_powerline_prompt() { + # Arguments: side, exit_code + _powerline_set_jobs + $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" $_POWERLINE_RENDERER_ARG --last_exit_code=$2 --jobnum=$_POWERLINE_JOBS +} + +_powerline_setup_prompt() { + VIRTUAL_ENV_DISABLE_PROMPT=1 + _powerline_set_append_trap "$@" + _powerline_set_set_jobs "$@" + _powerline_set_command "$@" + _powerline_set_renderer_arg "$@" + PS1='$(_powerline_prompt aboveleft $?)' +} + +_powerline_init_tmux_support() { + # Dash does not have &>/dev/null + if test -n "$TMUX" && tmux refresh -S >/dev/null 2>/dev/null ; then + # TMUX variable may be unset to create new tmux session inside this one + _POWERLINE_TMUX="$TMUX" + + _powerline_set_append_trap "$@" + + # If _powerline_tmux_set_pwd is used before _powerline_prompt it sets $? + # to zero in ksh. + PS1="$PS1"'$(_powerline_tmux_set_pwd)' + _powerline_append_trap '_powerline_tmux_set_columns' WINCH + _powerline_tmux_set_columns + fi +} + +# Strips the leading `-`: it may be present when shell is a login shell +_POWERLINE_USED_SHELL=${0#-} +_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/usr} +_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL#/bin/} + +if test -z "$POWERLINE_NO_BB_PROMPT$POWERLINE_NO_SHELL_PROMPT" ; then + _powerline_setup_prompt $_POWERLINE_USED_SHELL +fi +if test -z "$POWERLINE_NO_BB_TMUX_SUPPORT$POWERLINE_NO_SHELL_TMUX_SUPPORT" ; then + _powerline_init_tmux_support $_POWERLINE_USED_SHELL +fi diff --git a/powerline/renderers/ksh_prompt.py b/powerline/renderers/ksh_prompt.py new file mode 100644 index 00000000..3b4387a4 --- /dev/null +++ b/powerline/renderers/ksh_prompt.py @@ -0,0 +1,20 @@ +# vim:fileencoding=utf-8:noet + +from __future__ import absolute_import, unicode_literals + +from powerline.renderers.shell import ShellRenderer + + +ESCAPE_CHAR = '\001' + + +class KshPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + escape_hl_start = '\001' + escape_hl_end = '\001' + + def render(self, *args, **kwargs): + return '\001\r' + super(KshPromptRenderer, self).render(*args, **kwargs) + + +renderer = KshPromptRenderer