├── .gitignore ├── README.md ├── TODO.md ├── install.sh ├── texas.bash ├── texas.py ├── texas.zsh └── texas_init.zsh /.gitignore: -------------------------------------------------------------------------------- 1 | /texas 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | texas 2 | ===== 3 | 4 | `texas` creates a shell session synchronized with a [ranger][1] 5 | session. Whenever one's current directory changes, so does the 6 | other. It was created as an alternative to a Midnight Commander 7 | feature known as `ShowCommandLine` (which is bound to 8 | ctrl-o). 9 | 10 | [1]: https://github.com/hut/ranger 11 | 12 | INSTALLATION 13 | ------------ 14 | 15 | TL;DR: Run `./install.sh`. You don't need to read any further if 16 | you're not interested what does it actually do. 17 | 18 | Currently there is no support for the plugin managers such as 19 | [antigen](https://github.com/zsh-users/antigen) due to a bit tricky 20 | loading process. If you use them, be sure to load `texas` after 21 | loading the plugins managed by the manager. I've encountered some 22 | strange hard to track issues otherwise. 23 | 24 | **INSTALLATION DETAILS** 25 | 26 | The installation is comprised of two steps: installing a `ranger` 27 | plugin and installing a shell plugin. Both are mandatory. 28 | 29 | An unattended installation may be performed like this: 30 | 31 | $ yes | ./install.sh 32 | 33 | **Step 1: Install ranger plugin** 34 | 35 | Copy `texas.py` to `~/.config/ranger/plugins`. 36 | 37 | **Step 2: Install shell plugin** 38 | 39 | **bash** 40 | 41 | Source the `texas.bash` file by adding the line `source 42 | /path/to/texas.bash` to your `.bashrc`. 43 | 44 | **zsh** 45 | 46 | First copy the file `texas.zsh` to your `$fpath` and rename it to just 47 | `texas` (remove the `.zsh` suffix). After that, source the 48 | `texas_init.zsh` file by adding the line `source 49 | /path/to/texas_init.zsh` to your `.zshrc`. 50 | 51 | USAGE 52 | ----- 53 | 54 | `texas` uses [tmux][2] internally. Although knowledge of how to use 55 | `tmux` is not necessary, the user will certainly benefit from it. 56 | 57 | [2]: http://tmux.github.io/ 58 | 59 | **Startup** 60 | 61 | `texas` may be started either from inside an existing `tmux` session 62 | or from a regular shell session. In the first case, it will use the 63 | current `tmux` window and in the second case it will create a new 64 | `tmux` session **in a separate tmux daemon automatically named 65 | "texas"**. 66 | 67 | If you launch `texas` from an existing `tmux` session, you may quit 68 | `ranger` and the shell should still be running just as before 69 | launching `texas`. 70 | 71 | If you launch `texas` outside of an existing `tmux` session, `ranger` 72 | and the shell are bound together: closing one will close the other and 73 | in that regard they may be considered as a single application. If you 74 | open any more `tmux` windows (which `texas` by all means does not 75 | discourage), they will *not* be closed. Only `ranger` and the 76 | associated shell will close leaving all the other `tmux` windows 77 | intact. 78 | 79 | **Switching windows** 80 | 81 | If you run `texas` in a new `tmux` session (see the previous 82 | paragraph), you may use ctrl-o to switch between windows, 83 | like in Midnight Commander. The regular `tmux` keys for switching 84 | windows will work too (please refer to the `tmux` manual). 85 | ctrl-o has one advantage though (other than being shorter): 86 | it will work even if you move one of the `tmux` panes to a separate 87 | `tmux` window (for example with the `:break-pane` `tmux` command) as 88 | it intelligently switches either to a second split or a second window. 89 | 90 | Before `v1.0` it was supported only in `zsh`. Since `v1.0` it works in 91 | `bash` too. 92 | 93 | Since `v1.1` ctrl-o is bound in `tmux` itself. Before that 94 | it was handled by `bash`/`zsh` and `ranger`. 95 | 96 | Since `v1.2` ctrl-o is bound only when run in a new 97 | `tmux` daemon to prevent contaminating the all the other tmux sessions 98 | with this keybinding. 99 | 100 | CONFIGURATION 101 | ------------- 102 | 103 | The followind environmental variables may be used to customize `texas`: 104 | 105 | - **TEXAS_CONFIG_NOSWAP** — display `ranger` below the shell instead 106 | of on top of it (set to "1" to enable) 107 | - **TEXAS_CONFIG_SIZE** — customize the size of the `ranger` pane, 108 | as percentage (default: 70). 109 | - **TEXAS_CONFIG_TMUX_CONFIG** — an *additional* `tmux` config to 110 | source after starting `texas`. Only used in a dedicated `tmux` 111 | session, i.e. when `texas` is started from outside of an already 112 | running `tmux`. 113 | - **TEXAS_CONFIG_SWITCH_KEY** — a `tmux` key to be bound in a 114 | dedicated `tmux` session for the pane switching (default: 115 | C-o) 116 | - **TEXAS_CONFIG_HORIZONTAL** - start tmux with `-h` flag thus 117 | making split horizontal instead of vertical (default). Set to 118 | "1" to enable. 119 | 120 | DEPENDENCIES 121 | ------------ 122 | 123 | - `ranger` 124 | - `tmux` 125 | - `bash` or `zsh` 126 | 127 | KNOWN ISSUES 128 | ------------ 129 | 130 | If used with `bash`, each time `ranger` changes its current directory 131 | a new prompt line will be shown in `bash`. It will erase the contents 132 | of the command line and may be seen as ugly. 133 | 134 | In `tmux 2.1` with `TEXAS_CONFIG_NOSWAP=0` the wrong pane is being 135 | focused. It can be fixed by manually adding `-d` to the `tmux 136 | swap-pane` call. I've detected this behavior only in `tmux 2.1`. In 137 | both `2.0` and `2.2` `-d` causes the exactly opposite effect, so it 138 | seems to be a bug in `tmux 2.1`. 139 | 140 | SEE ALSO 141 | -------- 142 | 143 | `ranger(1)`, `tmux(1)` 144 | 145 | AUTHOR 146 | ------ 147 | 148 | Wojciech 'vifon' Siewierski < wojciech dot siewierski at onet dot pl > 149 | 150 | COPYRIGHT 151 | --------- 152 | 153 | Copyright (C) 2015-2016 Wojciech Siewierski 154 | 155 | This program is free software: you can redistribute it and/or modify 156 | it under the terms of the GNU General Public License as published by 157 | the Free Software Foundation, either version 3 of the License, or 158 | (at your option) any later version. 159 | 160 | This program is distributed in the hope that it will be useful, 161 | but WITHOUT ANY WARRANTY; without even the implied warranty of 162 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 163 | GNU General Public License for more details. 164 | 165 | You should have received a copy of the GNU General Public License 166 | along with this program. If not, see . 167 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - [X] Bash: update prompt after changing cwd 2 | - [ ] support older versions of tmux 3 | - [X] `split-window -b` is only available since tmux 2.0 4 | - [ ] `split-window -F` is not available in tmux 1.6 5 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | check-dependency() { 4 | if ! command -v "$1" &> /dev/null; then 5 | cat <> "$CONFIG" 99 | fpath=("$TEXAS_DIR" \$fpath) 100 | source "$TEXAS_DIR/texas_init.zsh" 101 | EOF 102 | else 103 | echo "Installing the bash plugin..." 104 | cat <> "$CONFIG" 105 | source "$TEXAS_DIR/texas.bash" 106 | EOF 107 | fi 108 | 109 | echo "Installing the ranger plugin..." 110 | mkdir -p "$HOME/.config/ranger/plugins" 111 | ln -sf "$TEXAS_DIR/texas.py" -t "$HOME/.config/ranger/plugins" 112 | 113 | if ask "Do you want to bind alt+z to texas?"; then 114 | echo "Installing the keybind..." 115 | if [ "$MODE" = "zsh" ]; then 116 | cat <<'EOF' >> "$CONFIG" 117 | bindkey -s '\ez' "\eq texas\n" 118 | EOF 119 | else 120 | cat <<'EOF' >> "$CONFIG" 121 | bind -x '"\ez":"texas"' 122 | EOF 123 | fi 124 | fi 125 | 126 | cat < /dev/null || ! command -v tmux &> /dev/null; then 4 | return 1 5 | fi 6 | 7 | texas() 8 | { 9 | # Start a new tmux session with texas inside. 10 | if [ -z "$TMUX" ]; then 11 | tmux -L texas new-session 'LAUNCH_TEXAS=1 bash' 12 | return 13 | fi 14 | 15 | [ -z "$TEXAS_CONFIG_SIZE" ] && TEXAS_CONFIG_SIZE=70 16 | 17 | local TEXAS_ADDITIONAL_FLAGS=() 18 | [ "$TEXAS_CONFIG_HORIZONTAL" = 1 ] && TEXAS_ADDITIONAL_FLAGS+=("-h") 19 | 20 | if [ "$TEXAS_CONFIG_NOSWAP" = 1 ]; then 21 | TEXAS_RANGER_PID=$(tmux split-window $TEXAS_ADDITIONAL_FLAGS -p "$TEXAS_CONFIG_SIZE" -P -F '#{pane_pid}' "TEXAS_BASH=1 LAUNCH_TEXAS=$LAUNCH_TEXAS TEXAS_SHELL_PID=$$ ranger") 22 | else 23 | TEXAS_CONFIG_SIZE=$((100 - TEXAS_CONFIG_SIZE)) 24 | TEXAS_RANGER_PID=$(tmux split-window $TEXAS_ADDITIONAL_FLAGS -p "$TEXAS_CONFIG_SIZE" -P -F '#{pane_pid}' "TEXAS_BASH=1 LAUNCH_TEXAS=$LAUNCH_TEXAS TEXAS_SHELL_PID=$$ ranger") 25 | tmux swap-pane -D 26 | fi 27 | 28 | # Do not bind a key in a non-dedicated tmux daemon. Tmux binds are 29 | # global per daemon so that would contaminate the user environment. 30 | if [ "$LAUNCH_TEXAS" = 1 ]; then 31 | local TEXAS_SWITCH_COMMAND 32 | TEXAS_SWITCH_COMMAND=$(cat <<'EOF' 33 | if [ "$(tmux display-message -p '#{window_panes}')" -gt 1 ]; then 34 | tmux select-pane -t :.+ 35 | else 36 | tmux next-window 37 | fi 38 | EOF 39 | ) 40 | [ -z "$TEXAS_CONFIG_SWITCH_KEY" ] && TEXAS_CONFIG_SWITCH_KEY="C-o" 41 | tmux bind -n "$TEXAS_CONFIG_SWITCH_KEY" run -b "$TEXAS_SWITCH_COMMAND" 42 | 43 | [ -f "$TEXAS_CONFIG_TMUX_CONFIG" ] && tmux source "$TEXAS_CONFIG_TMUX_CONFIG" 44 | fi 45 | 46 | # Unset the variable only here because the ranger plugin reacts to it. 47 | unset LAUNCH_TEXAS 48 | 49 | cd() 50 | { 51 | builtin cd "$@" 52 | if ! kill -USR1 $TEXAS_RANGER_PID 2> /dev/null; then 53 | # ranger is no longer running, let's clean up the bash state. 54 | 55 | # The ranger's PID is no longer needed. 56 | unset TEXAS_RANGER_PID 57 | 58 | # Remove the hook because there is no ranger to communicate with. 59 | unset -f cd 60 | fi 61 | } 62 | 63 | texas--exit-cleanup() { 64 | kill -HUP $TEXAS_RANGER_PID 65 | } 66 | trap texas--exit-cleanup EXIT 67 | 68 | texas--ranger-to-sh-sync() { 69 | # Needs to be immediately followed by SIGINT to update the 70 | # prompt. It's handled in the ranger plugin, controlled by the 71 | # TEXAS_BASH env variable. 72 | builtin cd -P /proc/$TEXAS_RANGER_PID/cwd 73 | } 74 | trap texas--ranger-to-sh-sync USR1 75 | 76 | 77 | texas--switch-to-ranger() { 78 | if [ "$(tmux display-message -p '#{window_panes}')" -gt 1 ]; then 79 | tmux select-pane -t :.+ 80 | else 81 | tmux next-window 82 | fi 83 | } 84 | } 85 | 86 | if [ -n "$LAUNCH_TEXAS" ]; then 87 | texas 88 | fi 89 | -------------------------------------------------------------------------------- /texas.py: -------------------------------------------------------------------------------- 1 | import ranger.api 2 | 3 | import os 4 | import os.path 5 | import signal 6 | 7 | old_hook_init = ranger.api.hook_init 8 | def hook_init(fm): 9 | try: 10 | # Get the PID of the associated shell. 11 | texas_shell_pid = int(os.environ['TEXAS_SHELL_PID']) 12 | 13 | # Bind the 'cd' signal to a function sending the ranger's cwd 14 | # to the associated shell. 15 | def ranger_to_sh_sync(sig): 16 | try: 17 | os.kill(texas_shell_pid, signal.SIGUSR1) 18 | if 'TEXAS_BASH' in os.environ: 19 | # Force Bash to refresh the prompt. 20 | # See: https://github.com/Vifon/texas/issues/1 21 | os.kill(texas_shell_pid, signal.SIGINT) 22 | except OSError: 23 | exit(0) 24 | fm.signal_bind( 25 | 'cd', 26 | ranger_to_sh_sync) 27 | 28 | # Handle the SIGUSR1 signal. 29 | def sh_to_ranger_sync(sig, frame): 30 | cwd = "/proc/{}/cwd".format(texas_shell_pid) 31 | fm.cd(os.path.realpath(cwd)) 32 | signal.signal( 33 | signal.SIGUSR1, 34 | sh_to_ranger_sync) 35 | 36 | # If a new tmux session needed to be created i.e. texas wasn't 37 | # started from inside of tmux. 38 | if int(os.environ['LAUNCH_TEXAS']): 39 | # Close the associated shell along with the whole texas on 40 | # ranger exit unless it was started inside of an existing 41 | # tmux session. 42 | import atexit 43 | def texas_cleanup(): 44 | os.kill(texas_shell_pid, signal.SIGHUP) 45 | atexit.register(texas_cleanup) 46 | # Prevent the child processes from inheriting this env var so 47 | # that the spawned shells will not start nested texases 48 | # immediately. 49 | del os.environ['LAUNCH_TEXAS'] 50 | except KeyError: 51 | # The texas shell is not running. 52 | pass 53 | finally: 54 | return old_hook_init(fm) 55 | 56 | ranger.api.hook_init = hook_init 57 | -------------------------------------------------------------------------------- /texas.zsh: -------------------------------------------------------------------------------- 1 | # -*- sh -*- 2 | 3 | # Start a new tmux session with texas inside. 4 | if [ -z "$TMUX" ]; then 5 | tmux -L texas new-session 'LAUNCH_TEXAS=1 zsh' 6 | return 7 | fi 8 | 9 | [ -z "$TEXAS_CONFIG_SIZE" ] && TEXAS_CONFIG_SIZE=70 10 | 11 | local TEXAS_ADDITIONAL_FLAGS=() 12 | [ "$TEXAS_CONFIG_HORIZONTAL" = 1 ] && TEXAS_ADDITIONAL_FLAGS+=("-h") 13 | 14 | if [ "$TEXAS_CONFIG_NOSWAP" = 1 ]; then 15 | TEXAS_RANGER_PID=$(tmux split-window $TEXAS_ADDITIONAL_FLAGS -p "$TEXAS_CONFIG_SIZE" -P -F '#{pane_pid}' "LAUNCH_TEXAS=$LAUNCH_TEXAS TEXAS_SHELL_PID=$$ ranger") 16 | else 17 | TEXAS_RANGER_PID=$(tmux split-window $TEXAS_ADDITIONAL_FLAGS -p "$((100 - TEXAS_CONFIG_SIZE))" -P -F '#{pane_pid}' "LAUNCH_TEXAS=$LAUNCH_TEXAS TEXAS_SHELL_PID=$$ ranger") 18 | tmux swap-pane -D 19 | fi 20 | 21 | # Do not bind a key in a non-dedicated tmux daemon. Tmux binds are 22 | # global per daemon so that would contaminate the user environment. 23 | if [ "$LAUNCH_TEXAS" = 1 ]; then 24 | local TEXAS_SWITCH_COMMAND 25 | TEXAS_SWITCH_COMMAND=$(cat <<'EOF' 26 | if [ "$(tmux display-message -p '#{window_panes}')" -gt 1 ]; then 27 | tmux select-pane -t :.+ 28 | else 29 | tmux next-window 30 | fi 31 | EOF 32 | ) 33 | [ -z "$TEXAS_CONFIG_SWITCH_KEY" ] && TEXAS_CONFIG_SWITCH_KEY="C-o" 34 | tmux bind -n "$TEXAS_CONFIG_SWITCH_KEY" run -b "$TEXAS_SWITCH_COMMAND" 35 | 36 | [ -f "$TEXAS_CONFIG_TMUX_CONFIG" ] && tmux source "$TEXAS_CONFIG_TMUX_CONFIG" 37 | fi 38 | 39 | # Unset the variable only here because the ranger plugin reacts to it. 40 | unset LAUNCH_TEXAS 41 | 42 | autoload -U add-zsh-hook 43 | 44 | texas--sh-to-ranger-sync() { 45 | if ! kill -USR1 $TEXAS_RANGER_PID 2> /dev/null; then 46 | # ranger is no longer running, let's clean up the zsh state. 47 | 48 | # The ranger's PID is no longer needed. 49 | unset TEXAS_RANGER_PID 50 | 51 | # Remove the hook because there is no ranger to communicate with. 52 | chpwd_functions=${chpwd_functions:#texas--sh-to-ranger-sync} 53 | fi 54 | } 55 | add-zsh-hook chpwd texas--sh-to-ranger-sync 56 | 57 | texas--exit-cleanup() { 58 | kill -HUP $TEXAS_RANGER_PID 59 | } 60 | add-zsh-hook zshexit texas--exit-cleanup 61 | 62 | texas--ranger-to-sh-sync() { 63 | # This trap may be called either during a command execution or 64 | # not. If it's the latter, zle will be active and we can change 65 | # the cwd safely. If a command is being executed, do nothing. The 66 | # cwd will be updated on the next signal from ranger after the 67 | # command finishes its execution. 68 | if zle; then 69 | cd -qP /proc/$TEXAS_RANGER_PID/cwd 70 | zle reset-prompt 71 | fi 72 | } 73 | trap texas--ranger-to-sh-sync USR1 74 | -------------------------------------------------------------------------------- /texas_init.zsh: -------------------------------------------------------------------------------- 1 | # -*- sh -*- 2 | 3 | if ! (( $+commands[ranger] && $+commands[tmux] )); then 4 | return 1 5 | fi 6 | 7 | autoload -U texas 8 | if [ -n "$LAUNCH_TEXAS" ]; then 9 | texas 10 | fi 11 | --------------------------------------------------------------------------------