2

I'm on tmux 2.6 and am using a few different conda environments. Upon splitting panes/windows I would like tmux to activate the environment of the parent pane also in the child pane.

I know that I can add code to be executed in a key bind for the split-window command and that the name of the currently active conda env is stored in $CONDA_DEFAULT_ENV. However, whatever commands I tried failed.

For my attempts I had conda activate $CONDA_DEFAULT_ENV in /some_path/bla.sh and set the pane-splitting command with

bind \ split-window -h -c "#{pane_current_path}" '/some_path/bla.sh'

in my tmux config file, but the newly created pane vanishes immediately after the split.

However, even if it had not, I guess it would have just reactivated the base env because the $CONDA_DEFAULT_ENV env variable has changed with initiating the new shell.

I guess a working solution has to first store the old $CONDA_DEFAULT_ENV environment variable, carry it over to the new tmux pane and then use it to set the conda environment, but I don't know how to achieve this.

zeawoas
  • 446
  • 7
  • 18
  • Possible answer [here](https://stackoverflow.com/a/57897060/5008284) – meuh Oct 21 '19 at 09:03
  • 1
    @meuh I'm afraid it won't work. `update-environment` is triggered when a new **session** is created. But the questioner need to add the environment variable when a new **pane** is created. – Simba Oct 21 '19 at 11:11
  • can confirm that it sadly doesn't work – zeawoas Oct 21 '19 at 11:15
  • Also, when I just try to activate any environment from within `bla.sh` I get `CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'.` in the new pane although I can manually activate environments without problems in new panes. Are the commands added to `split-window` executed by a different shell than the one that ends up being the shell of the new pane? – zeawoas Oct 21 '19 at 11:21
  • @zeawoas About activating an environment in a script. https://stackoverflow.com/a/58081608/5101148 – Simba Oct 21 '19 at 13:01

1 Answers1

3

I have accomplished this using tmux 3.1b. I see that you were running tmux 2.6 when you posted the question but, if you can, please try to update to at least v3.0, as my answer relies on the use of the -e flag to new-window and split-window and that flag was only introduced from v3.0 according to their changelog. Assuming you can use the aforementioned -e flag, the steps are as follows:

Step 1 (with tmux/tmux.conf):

Pass -e "TMUX_PARENT_PANE_ID=#{pane_id}", in addition to any other flags you may already use, when running tmux new-window or tmux split-window. This creates in the environment of the new (child) pane/window a variable TMUX_PARENT_PANE_ID that holds a unique ID assigned by tmux to the initial (parent) pane.

If you use key bindings, then you need to (re)write them in a not-very-intuitive manner making use of tmux's run. For instance, in order to bind letter "c" to tmux new-window, the corresponding line in your tmux.conf file must look like the following:

bind c run 'tmux new-window YOUR_CURRENT_OPTIONS -e "TMUX_PARENT_PANE_ID=#{pane_id}"'

where YOUR_CURRENT_OPTIONS represent whatever options you may already have in place prior to adding the -e option. If you just use bind c new-window YOUR_CURRENT_OPTIONS -e "TMUX_PARENT_PANE_ID=#{pane_id}", then #{pane_id} is passed as a literal "#{pane_id}" and the next steps won't work.

Setp 2 [in your bashrc (or equivalent) file]:

Redefine the conda command so as to:

  1. Have a copy, in the local environment of each pane, of anaconda's CONDA_DEFAULT_ENV environment variable. In the code that follows, this copy will be named CONDA_DEFAULT_ENV_COPY. This will be useful for recalling the conda environment in a pane if bashrc (or your corresponding equivalent file) is sourced again.
  2. Keep track of changes in the conda environments of all panes of the tmux session. We do this by defining/updating a TMUX_SESSION_CONDA_ENVS tmux session environment variable. Contrary to variables defined in the local environments of each pane, tmux session variables are accessible by all panes in the session.

The code to accomplish what is described above is:

# Redefine conda command, part a: Keep copy of original conda command/function
eval "original_$(declare -f conda)" 2> /dev/null
if [ $? -ne 0 ]; then
    original_conda () {
      command conda "$@"
    }
fi
# Redefine conda command, part b: Add new functionality related to items (i) and (ii).
conda () {
  # Run the regular conda
  original_conda "$@"
  local CONDA_RTN_CODE=$?

  # Keep a copy of CONDA_DEFAULT_ENV to restore the environment if, e.g.,
  # 'source ~/.bashrc' is run
  CONDA_DEFAULT_ENV_COPY=$CONDA_DEFAULT_ENV

  # Stop and return original_conda's return code if it fails
  [ $CONDA_RTN_CODE -ne 0 ] && return $CONDA_RTN_CODE

  # Do tmux-related stuff, but only if tmux is running and "$@" contains substring "activate"
  if [[ -n "$TMUX" ]] && [[ "$@" =~ .*"activate".* ]]; then
    # Create/update the *tmux* session env var "TMUX_SESSION_CONDA_ENVS"
    local TMUX_SESSION_CONDA_ENVS=$(tmux showenv TMUX_SESSION_CONDA_ENVS 2>/dev/null)
    if [[ $? -eq 0 ]]; then
      # Get list of conda envs for all panes except the current one
      local OLD_VALUES=$(echo $TMUX_SESSION_CONDA_ENVS | sed "s/TMUX_SESSION_CONDA_ENVS=//")
      local CONDA_ENV_OTHER_PANES=$(echo $OLD_VALUES | sed "s/$TMUX_PANE:\w*[[:space:]]*//g")
    fi
    # Include current pane's conda env info
    tmux setenv TMUX_SESSION_CONDA_ENVS "$TMUX_PANE:$CONDA_DEFAULT_ENV $CONDA_ENV_OTHER_PANES"
  fi
}

Step 3 [also in your bashrc (or equivalent) file]:

Query the parent's pane conda environment at the time the child was created, and, finally, activate the conda environment in the child. The code I'm currently using is the following:

if [[ -n "$TMUX_PARENT_PANE_ID" ]]; then
    # Remember: "TMUX_SESSION_CONDA_ENVS", as per our redefined "conda" command, carries
    # info about changes in the the conda environments in all the session's panes.
    # TMUX_PARENT_PANE_ID makes it thus possible to query, from any child
    # pane, its parent's conda environment at the time the child was created.
    # This is exactly what will be done now.
    TMUX_SESSION_CONDA_ENVS=$(tmux showenv TMUX_SESSION_CONDA_ENVS 2>/dev/null)
    if [ $? -eq 0 ]; then
        PATT="(?<=${TMUX_PARENT_PANE_ID}:).*?(?=([[:space:]]|$))"
        PARENT_CONDA_ENV=$(echo $TMUX_SESSION_CONDA_ENVS | grep -oP "$PATT" | head -1)
        echo "Activate conda env '$PARENT_CONDA_ENV' of parent tmux pane '$TMUX_PARENT_PANE_ID'"
        conda activate $PARENT_CONDA_ENV
    fi
    # Clean up the pane's env (TMUX_SESSION_CONDA_ENVS remains in the tmux session env)
    unset TMUX_SESSION_CONDA_ENVS PATT PARENT_CONDA_ENV
    # Erase memory of parent tmux pane's ID so that the 'else' block below
    # is run if we re-source bashrc
    unset TMUX_PARENT_PANE_ID
else
    # Triger update of TMUX_SESSION_CONDA_ENVS and CONDA_DEFAULT_ENV_COPY
    # when the pane has no parent (very first pane or a pane where bashrc was
    # re-sourced after creation).
    [[ -n "$CONDA_DEFAULT_ENV_COPY" ]] && echo "Activate previous conda env '$CONDA_DEFAULT_ENV_COPY'"
    conda activate $CONDA_DEFAULT_ENV_COPY
fi

After following these steps, just close your terminals, open them again and relaunch tmux. I hope this helps.

some_guy
  • 46
  • 3