├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md └── session-sauce.plugin.zsh /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.3.2 4 | * Fix error messages getting added to session lists. 5 | 6 | ## 1.3.1 7 | * Speed up listing sessions. 8 | * Fix bug when selecting manually created sessions which aren't directories from `SESS_PROJECT_ROOT` 9 | 10 | ## 1.3.1 11 | * Speed up listing sessions. 12 | * Fix bug when selecting manually created sessions which aren't directories from `SESS_PROJECT_ROOT` 13 | 14 | ## 1.3.0 15 | * Allow selecting multiple sessions for `sess kill`. 16 | 17 | ## 1.2.0 18 | 19 | * Add 'sess -' which switches you back to your last-accessed session. 20 | 21 | ## 1.1.0 22 | 23 | * Add support for multiple project roots in `SESS_PROJECT_ROOT` using `:` separator. 24 | 25 | ## 1.0.0 26 | 27 | * Initial Release 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | RUN apk add bash fzf tmux 4 | 5 | COPY ./session-sauce.plugin.zsh /src/ 6 | 7 | WORKDIR src 8 | ENV SESS_PROJECT_ROOT=/ 9 | ENTRYPOINT bash 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Chris Penner 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Session Sauce 2 | 3 | 4 | 5 | * [Compatibility](#compatibility) 6 | * [Dependencies:](#dependencies) 7 | * [Configuration](#configuration) 8 | * [`SESS_PROJECT_ROOT`](#sess_project_root) 9 | * [tmux bindings](#tmux-bindings) 10 | * [Usage](#usage) 11 | * [Smart session switch](#smart-session-switch) 12 | * [Explicitly create a new session](#explicitly-create-a-new-session) 13 | * [List all active sessions](#list-all-active-sessions) 14 | * [Choose an active session](#choose-an-active-session) 15 | * [Kill an active session](#kill-an-active-session) 16 | * [Version](#version) 17 | * [Help](#help) 18 | 19 | 20 | 21 | Session Sauce exposes the `sess` command for quick and easy tmux session management. 22 | 23 | [![asciicast](https://asciinema.org/a/tVPETaazkExhMjFH6RWdCmUSm.svg)](https://asciinema.org/a/tVPETaazkExhMjFH6RWdCmUSm) 24 | 25 | It allows you as the user to stop caring about which sessions currently exist 26 | by being declarative about which project you want to switch to. If a session 27 | exists you'll be switched there. If it doesn't, it will be created, then you'll 28 | be switched there, detaching any current session (if one exists). 29 | 30 | `sess` also handles your `TMUX` context for you, opening a tmux client if you're 31 | outside of one, and switching sessions of the current client if you're already 32 | inside tmux. No more `sessions should be nested with care, unset $TMUX to force` 33 | messages! 34 | 35 | Note: All subcommand names can optionally be shortened to their first letter. 36 | E.g. `sess s` is equivalent to `sess switch` 37 | 38 | ## Compatibility 39 | 40 | session-sauce should work on most linux and mac machines, and works with most bash-compatible shells. 41 | 42 | Installation options: 43 | 44 | * copy-paste `session-sauce.plugin.zsh` onto your machine and source it from your shell's `rc` file. (E.g. `~/.bashrc` or `~/.zshrc`) 45 | * Add to your ZSH plugin manager as `ChrisPenner/session-sauce` 46 | 47 | Although the script has a `zsh` suffix it should be fully compatible with all shells that can interpret `bash`. 48 | 49 | ## Dependencies: 50 | 51 | **fzf**: 52 | 53 | Find installation instructions here: https://github.com/junegunn/fzf 54 | Or `brew install fzf` on a Mac 55 | 56 | **tmux**: 57 | 58 | Find installation instructions here: https://github.com/tmux/tmux 59 | Or `brew install tmux` on a Mac 60 | 61 | ## Configuration 62 | 63 | ### `SESS_PROJECT_ROOT` 64 | 65 | Export this variable from your zshrc or bashrc file. 66 | This should contain a list of `:` separated absolute paths to directories 67 | where you keep your projects. 68 | Projects in this directory will be used as options 69 | for the `sess switch` command. 70 | Periods in project directory names will be replaced with underscores in the 71 | corresponding session names due to tmux session naming restrictions 72 | (tmux/tmux@9ee93b3). 73 | 74 | ### tmux bindings 75 | 76 | You can set up tmux bindings to pop a side pane for switching sessions so you don't have to type an explicit shell command. 77 | This is handy when you're running things like vim and don't want to jump out to a prompt in order to switch sessions. 78 | 79 | Throw these into your `.tmux.conf` and customize the binding to whatever you like. 80 | 81 | ``` 82 | bind-key C-s split-window -v "zsh -ic 'sess switch'" 83 | bind-key C-l split-window -v "zsh -ic 'sess choose'" 84 | ``` 85 | 86 | This runs `zsh` in script mode using an interactive shell so that it loads your ZSH plugins (like session-sauce). 87 | Otherwise it's likely that `sess` won't be on your path, or that your `SESS_PROJECT_ROOT` won't be set. 88 | 89 | If your login shell is quite slow to start up, you may want to use simply `zsh -c` and explicitly source only the `session-sauce` script, but I'll leave that up to you. 90 | 91 | ## Usage 92 | 93 | ### Smart session switch 94 | 95 | ```sh 96 | $ sess switch [query] 97 | ``` 98 | 99 | Interactively select a session from a list of your project directories 100 | (configured by `SESS_PROJECT_ROOT`) as well as all existing sessions. 101 | 102 | This is the most versatile and useful command. 103 | Running simply `sess` will be expanded to `sess switch` 104 | 105 | An optional query can be provided to pre-fill the fzf window. 106 | If there is only one match for the query the result 107 | will be selected automatically. 108 | 109 | ### Explicitly create a new session 110 | 111 | ```sh 112 | $ sess new [session-name] 113 | ``` 114 | 115 | Normally you'll just use `sess switch` to create sessions, 116 | but `sess new` can be used to explicitly create new session 117 | in the current directory. 118 | 119 | This can be useful for creating sessions for projects 120 | outside of `SESS_PROJECT_ROOT`. 121 | 122 | If no session name is provided the directory name will be used, with periods 123 | replaced with underscores due to tmux session naming restrictions 124 | (tmux/tmux@9ee93b3). 125 | 126 | ### List all active sessions 127 | 128 | ```sh 129 | $ sess list 130 | ``` 131 | 132 | List all the active sessions. 133 | 134 | 135 | ### Choose an active session 136 | 137 | ```sh 138 | $ sess choose [query] 139 | ``` 140 | 141 | Interactively select a session from a list of all active sessions. 142 | Unlike 'sess switch' this does NOT include projects from `SESS_PROJECT_ROOT`. 143 | 144 | An optional query can be provided to pre-fill the fzf window. 145 | If there is only one match for the query the result 146 | will be selected automatically. 147 | 148 | 149 | ### Kill an active session 150 | 151 | ```sh 152 | $ sess kill [query] 153 | ``` 154 | 155 | Interactively select a session from a list of all active sessions to kill. 156 | 157 | An optional query can be provided to pre-fill the fzf window. 158 | 159 | Use Tab to select multiple sessions to kill. 160 | 161 | ### Version 162 | 163 | ```sh 164 | $ sess version 165 | ``` 166 | 167 | Display the currently installed version of sess. 168 | 169 | ### Help 170 | ```sh 171 | $ sess help 172 | ``` 173 | 174 | Displays this usage info. 175 | -------------------------------------------------------------------------------- /session-sauce.plugin.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sess() { 4 | # Dependencies: fzf & tmux 5 | if ! command -V "fzf" 2>&1 >/dev/null ; then 6 | echo "sesh requires fzf, but couldn't find it on your path" >&2 7 | echo "Find installation instructions here: https://github.com/junegunn/fzf" >&2 8 | echo "Or 'brew install fzf' on a Mac" >&2 9 | return 1 10 | fi 11 | 12 | if ! command -V "tmux" 2>&1 >/dev/null ; then 13 | echo "sesh requires tmux, but couldn't find it on your path" >&2 14 | echo "Find installation instructions here: https://github.com/tmux/tmux" >&2 15 | echo "Or 'brew install tmux' on a Mac" 16 | return 1 17 | fi 18 | 19 | if [[ -z "$TMUX" ]]; then 20 | local attach_cmd=attach-session 21 | else 22 | local attach_cmd=switch-client 23 | fi 24 | 25 | _sess_ensure_session() { 26 | local session="$1" 27 | local dir="$2" 28 | # Create new session 29 | # Fail silently if it already exists 30 | tmux new-session -d -s "$session" -c "$dir" >/dev/null 2>&1 || true 31 | } 32 | 33 | _sess_switch_session() { 34 | session="$1" 35 | dir="$2" 36 | # Before switching, stash the current session so we can switch back later. 37 | local current_session=$(tmux display-message -p '#S') 38 | if [[ -n "$current_session" ]] ; then 39 | # Set a global tmux env var with the current session 40 | tmux set-environment -g SESS_LAST_SESSION "$current_session" 41 | fi 42 | if [[ -n "$dir" ]]; then 43 | _sess_ensure_session "$session" "$dir" 44 | fi 45 | # Attach to or switch to session 46 | tmux "$attach_cmd" -t "$session" 47 | } 48 | 49 | _sess_list_sessions() { 50 | tmux list-sessions -F "#{session_name}" 51 | } 52 | 53 | _sess_split_name_from_dir() { 54 | # This is a performance bottleneck, run 8 tasks at a time. 55 | xargs -P8 -I '{}' bash -c 'echo -e "{}\t$(basename {} | tr . _)"' 56 | } 57 | 58 | _sess_pick() { 59 | sort -k2 | uniq -i -f1 | fzf $2 --with-nth=2 -q "$1" 60 | } 61 | 62 | _sess_switch() { 63 | # Switch to a chosen session 64 | local session_and_dir="$1" 65 | if [[ -z "$session_and_dir" ]]; then 66 | # no session chosen 67 | return 0 68 | fi 69 | local dir=$(echo "$session_and_dir" | cut -f1) 70 | local session=$(echo "$session_and_dir" | cut -f2) 71 | _sess_switch_session "$session" "$dir" 72 | } 73 | 74 | _sess_kill() { 75 | # Kill the chosen session 76 | local sessions_and_dirs="$1" 77 | local sessions=$(echo "$session_and_dir" | cut -f2) 78 | if [[ -z "$sessions" ]]; then 79 | # no session chosen 80 | return 0 81 | fi 82 | echo "$sessions" | xargs -n1 tmux kill-session -t 83 | echo "$sessions" | xargs -n1 echo "Successfully killed" 84 | } 85 | 86 | _sess_usage() { 87 | cat >&2 < /dev/null | sed "s:^.*=::") 218 | if [[ -z "$last_session" ]]; then 219 | echo "No session stashed. Try switching sessions first." 220 | return 1 221 | fi 222 | _sess_switch_session "$last_session" 223 | ;; 224 | 225 | # switch 226 | *) 227 | if [[ -z "$SESS_PROJECT_ROOT" ]]; then 228 | echo "The default 'sess' command uses the SESS_PROJECT_ROOT environment variable" >&2 229 | echo "to discover your projects. Set this in your shell's rc file." >&2 230 | echo "E.g. 'export SESS_PROJECT_ROOT=~/projects'" >&2 231 | echo >&2 232 | echo "Run 'sess help' for more information" >&2 233 | return 1 234 | fi 235 | 236 | # sed is used to introduce a 'dummy' filepath for sessions which already exist. 237 | # It is ignored when switching to the session since it already exists. 238 | local session_and_dir=$( \ 239 | (for root in $(tr ":" "\n" <<< "$SESS_PROJECT_ROOT"); do 240 | ls -d "$root"/* || echo "No projects found in $root." >&2 241 | done | _sess_split_name_from_dir; _sess_list_sessions 2>/dev/null | sed "s/^/. /") | 242 | _sess_pick "$2" --select-1) 243 | _sess_switch "$session_and_dir" 244 | ;; 245 | esac 246 | 247 | 248 | # If we're in zsh we can unbind these functions so we don't pollute the global 249 | # namespace 250 | if command -V unfunction >/dev/null 2>&1 ; then 251 | unfunction _sess_pick 252 | unfunction _sess_switch 253 | unfunction _sess_kill 254 | unfunction _sess_list_sessions 255 | unfunction _sess_switch_session 256 | unfunction _sess_ensure_session 257 | unfunction _sess_usage 258 | fi 259 | } 260 | --------------------------------------------------------------------------------