chruby icon indicating copy to clipboard operation
chruby copied to clipboard

.ruby-version auto-switch ignored within subshells

Open aprescott opened this issue 11 years ago • 11 comments

I'm not sure if this is the same as / similar to #168, but subshells appear to not use auto-switching.

~$ cd /tmp
/tmp$ chruby
 * ruby-2.0.0-p353
   ruby-2.1.0

/tmp$ mkdir foo; echo 2.0.0-p353 > foo/.ruby-version
/tmp$ mkdir bar; echo 2.1.0 > bar/.ruby-version

/tmp$ cd foo/
/tmp/foo$ chruby
 * ruby-2.0.0-p353    # correct
   ruby-2.1.0

/tmp/foo$ cd ../bar/
/tmp/bar$ chruby
   ruby-2.0.0-p353
 * ruby-2.1.0         # correct

/tmp/bar$ (cd ../foo && chruby)
   ruby-2.0.0-p353
 * ruby-2.1.0         # wrong
$ bash --version
GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
Copyright (C) 2007 Free Software Foundation, Inc.

OS X 10.9, chruby 0.3.8.

aprescott avatar Jan 21 '14 15:01 aprescott

from man bash:

  set [--abefhkmnptuvxBCEHPT] [-o option-name] [arg ...]
  set [+abefhkmnptuvxBCEHPT] [+o option-name] [arg ...]

... -T If set, any traps on DEBUG and RETURN are inherited by shell functions, command substitutions, and commands executed in a subshell environment. The DEBUG and RETURN traps are nor- mally not inherited in such cases.

I would try set -T around: https://github.com/postmodern/chruby/blob/master/share/chruby/auto.sh#L30

mpapis avatar Jan 22 '14 10:01 mpapis

Any objections to enabling set -T by default?

postmodern avatar Jan 22 '14 20:01 postmodern

I was going to say no objections, but I just tried this and it's messing with my PS1 for some reason.

aprescott avatar Jan 22 '14 20:01 aprescott

Narrowed down the problem: I have a DEBUG trap in my .bashrc as per #227. So the end of my auto.sh is actually:

function chruby_trap() {
    [[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto
}

if [[ -n "$ZSH_VERSION" ]]; then
    if [[ ! "$preexec_functions" == *chruby_auto* ]]; then
        preexec_functions+=("chruby_auto")
    fi
elif [[ -n "$BASH_VERSION" ]]; then
    set -T
    trap chruby_trap DEBUG
fi

This is almost identical to the existing auto.sh provided by chruby but I expose chruby_trap. Then my .bashrc uses this to reset colouring:

# Reset color for command output and call chruby's trap function
trap 'echo -ne "\033[0m" && chruby_trap' DEBUG

Since my PS1 uses __git_ps1

PS1="some stuff \$(__git_ps1 '::%s') other stuff"

the set -T forces echo -ne to fire since now the DEBUG trap is inherited. The end result being that a PS1 of

PS1="abcdefghijklmnop\$(__git_ps1 '::%s')1234567890"

will cause a new terminal to appear as

1234567890[k]lmnop

where [k] indicates the cursor is over the k character, and typing will type over the klmnop portion.

So basically:

  • __git_ps1 in PS1 with other colour codes.
  • Existing DEBUG trap outside of chruby which inserts \033[0m to reset colouring for stdout.
  • Inheriting trap causes existing DEBUG to fire when __git_ps1 is executed.

I suppose an argument is that this is simply not something chruby can work around and my desire to have certain colouring in my PS1 + reset colouring for output through echo -ne "\033[0m" in a DEBUG trap, but it would be nice to have them coexist.

aprescott avatar Jan 22 '14 20:01 aprescott

I suppose there might be a way to call set +T and then set -T around my PS1 to bypass it for the __git_ps1 call?

aprescott avatar Jan 22 '14 20:01 aprescott

you could try to use a wrapper that checks what function is called and do reset color only in some cases, or ignoring the __git_ps1 function:

[[ " ${FUNCNAME[*]} " == *" __git_ps1 "* ]]

mpapis avatar Jan 22 '14 21:01 mpapis

@mpapis that's awesome, thanks!

I'm :+1: on set -T, even before I had that workaround, since I don't think this will apply to many people.

aprescott avatar Jan 22 '14 21:01 aprescott

set -T seems to only fix Bash. I have a test in my fork's branch that fails for zsh:

SHELL=`which bash` ./test/runner
>>> Running tests under /bin/bash in interactive-mode ...
>>> Running ./test/chruby_auto_test.sh ...
[...]
test_chruby_auto_within_subshells
[...]

Ran 13 tests.

OK
SHELL=`which zsh`  ./test/runner
>>> Running tests under /usr/bin/zsh in interactive-mode ...
>>> Running ./test/chruby_auto_test.sh ...
[...]
test_chruby_auto_within_subshells
ASSERT:did not switch Ruby when subshell entered versioned directory expected:</home/travis/build/aprescott/chruby/test/rubies/ruby-1.9.3-p429> but was:<>
[...]

Ran 13 tests.
FAILED (failures=1)

I don't know zsh enough to get this passing, but if someone lets me know then I can update my branch and PR a fix for this bug.

aprescott avatar Jan 29 '14 02:01 aprescott

that might be unsetopt localtraps try:

if [[ -n "${ZSH_VERSION:-}" ]]
then unsetopt localtraps
else set -T
fi

an important point about Zsh is that it has to be run in top scope, not in a function that gets called from shell

mpapis avatar Jan 29 '14 21:01 mpapis

After doing some testing, the trap DEBUG and preexec_functions hook are being inherited by the sub-shell.

bash

$ trap -p DEBUG
trap -- '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto' DEBUG
$ (trap -p DEBUG)
trap -- '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto' DEBUG

zsh

% eval 'echo ${preexec_functions[@]}' 
chruby_auto
% (eval 'echo ${preexec_functions[@]}')
chruby_auto

I believe the reason why the chruby_auto function isn't working, is because the sub-shell command is being treated as one single command, not individual commands which chruby_auto runs before.

postmodern avatar Jan 30 '14 05:01 postmodern

FWIW adding set -T before line 30 in auto.sh fixed my non-functioning auto-switching problem.

djbender avatar Jun 25 '14 12:06 djbender