├── .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 |
--------------------------------------------------------------------------------