Search code examples
tmux

TMUX pane tabs, or 'locking/freezing' pane splits in place


When using tmux, I generally have an editor open in a split on top, and a shell at the bottom. Right now, I'm writing a python script, and on the bottom half of the split I can't find a way to toggle back and forth between ipython and bash: the best I can do is create a nested split between those two, sharing the bottom half of the screen. Right now this looks something like the following.

Is there something like tabs within a pane, so I can switch back and forth between bash/ipython while keeping the editor locked/frozen in place at the top? It's not ideal right now to have to choose between switching to a new tab for one of the shell prompts or using these tiny splits. Tmux is so flexible it seems like there'd be a way to do this straightforward pane-lock. Thanks bottom split


Solution

  • Resolved using nested tmux sessions. I based my solution on a much-stripped down version of nested-tmux. I launch a nested session and then use tabs within that nested session. To launch a nested session, I use the following script tmux-nested.sh (adapted from the nested-tmux repo):

    #!/bin/sh
    
    TMUX_PARENT=$(basename "$TMUX")
    TMUX_PARENT="${TMUX_PARENT%%,*}"
    export TMUX_PARENT
    _SOCK="r$RANDOM"
    tmux -L "$_SOCK" new-session -s "$_SOCK"
    

    This creates a nested session with socket given a random filename (a hardcoded value wouldn't permit multiple nested sessions, say in different tabs in the parent session).

    In my .tmux.conf file, I add the following line on startup to change the tmux prefix for nested sessions:

    # nested session logic
    if-shell '! [ -z "$TMUX_PARENT" ]' 'set-option -g prefix C-b'
    

    (where my normal prefix is a backtick)

    EDIT: I expanded this function into a much more robust implementation, which can be found below:

    # Nest tmux sessions
    tmux_nested() {
        local usagestring='usage: tmux-nested [-n | -a nested-session-# | -l]'
        if [[ -z $TMUX ]]; then
            # ensure invoked within active session
            echo "'tmux_nested' should be invoked inside an active tmux session"
            return 1
        fi
    
        # read flags and check for number of existing nested sessions
        local nested session_name
        nested=$(tmux list-sessions -F '#{session_name}' | \
                 ggrep -P '^nested[\d]+' --color=never | sort -V)
        while getopts ':lna:' opt; do
            case $opt in
                l)
                    [[ -z $nested ]] && nested="No nested sessions running"
                    echo "$nested" && return 0;;
                n)
                    # if new session requested: create, set <C-b> tmux prefix, and attach
                    for (( i = 1;; i++ )); do
                        # get lowest available numeric value for new session name
                        if ! tmux has-session -t "nested$i" 2> /dev/null; then
                            session_name="nested$i" && break
                        fi
                    done
                    tmux new-session -ds "$session_name"
                    tmux send-keys -t "$session_name" \
                                      "tmux set prefix C-b" ENTER "clear" ENTER
                    env TMUX='' tmux attach -t "$session_name"
                    return $?;;
                a)
                    # attach to specified session if requested
                    session_name="nested${OPTARG}"
                    if ! env TMUX='' tmux attach -t "$session_name"; then
                        echo "Try 'tmux_nested -l'?" && return 1
                    fi;;
                *) echo "$usagestring" && return 1;;
            esac
        done
    
        # incorrect invocation, report incorrect invocation and exit
        echo "$usagestring" && return 1
    }