├── scripts ├── variables.sh ├── restore-snapshot.sh ├── save-snapshot.sh └── helpers.sh ├── LICENSE ├── named-snapshot.tmux └── README.md /scripts/variables.sh: -------------------------------------------------------------------------------- 1 | resurrect_save_path_option="@resurrect-save-script-path" 2 | resurrect_restore_path_option="@resurrect-restore-script-path" 3 | 4 | resurrect_dir_option="@resurrect-dir" 5 | 6 | snapshot_dir_option="@named-snapshot-dir" 7 | named_save_option="@named-snapshot-save" 8 | default_save_option="C-m:manual M:*" 9 | named_restore_option="@named-snapshot-restore" 10 | default_restore_option="C-n:manual N:*" 11 | switch_client_option="@named-snapshot-switch-client" 12 | default_switch_client_option="" 13 | -------------------------------------------------------------------------------- /scripts/restore-snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | 5 | source "$CURRENT_DIR/helpers.sh" 6 | source "$CURRENT_DIR/variables.sh" 7 | 8 | main() { 9 | local name="$1" 10 | local resurrect_restore_script_path="$(get_tmux_option "$resurrect_restore_path_option" "")" 11 | if [ -n "$resurrect_restore_script_path" ] && [ -n "$name" ]; then 12 | tmux display-message "Restoring snapshot '$name'..." 13 | local last_file="$(last_resurrect_file)" 14 | local original_path="$(last_resurrect_path)" 15 | local name_path="$(snapshot_dir)/$name" 16 | 17 | if [[ -L "$name_path" ]]; then 18 | local last_snapshot="$(readlink "$name_path")" 19 | ln -fs "$last_snapshot" "$last_file" 20 | "$resurrect_restore_script_path" "quiet" >/dev/null 2>&1 21 | ln -fs "$original_path" "$last_file" 22 | else 23 | tmux display-message "Snapshot '$name' not found" 24 | fi 25 | fi 26 | } 27 | 28 | main "$@" 29 | -------------------------------------------------------------------------------- /scripts/save-snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | 5 | source "$CURRENT_DIR/helpers.sh" 6 | source "$CURRENT_DIR/variables.sh" 7 | 8 | main() { 9 | local name="$1" 10 | local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "")" 11 | if [ -n "$resurrect_save_script_path" ] && [ -n "$name" ]; then 12 | tmux display-message "Saving snapshot '$name'..." 13 | local last_file="$(last_resurrect_file)" 14 | local original_path="$(last_resurrect_path)" 15 | local name_path="$(snapshot_dir)/$name" 16 | 17 | "$resurrect_save_script_path" "quiet" >/dev/null 2>&1 18 | local last_snapshot="$(last_resurrect_path)" 19 | 20 | if files_differ "$name_path" "$last_snapshot"; then 21 | ln -fs "$last_snapshot" "$name_path" 22 | fi 23 | 24 | if [ -n "$original_path" ]; then 25 | ln -fs "$original_path" "$last_file" 26 | fi 27 | 28 | tmux display-message "Snapshot '$name' saved" 29 | fi 30 | } 31 | 32 | main "$@" 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sirisak Lueangsaksri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/helpers.sh: -------------------------------------------------------------------------------- 1 | if [ -d "$HOME/.tmux/resurrect" ]; then 2 | default_resurrect_dir="$HOME/.tmux/resurrect" 3 | else 4 | default_resurrect_dir="${XDG_DATA_HOME:-$HOME/.local/share}"/tmux/resurrect 5 | fi 6 | 7 | set_tmux_option() { 8 | local option=$1 9 | local value=$2 10 | tmux set-option -gq "$option" "$value" 11 | } 12 | 13 | get_tmux_option() { 14 | local option="$1" 15 | local default_value="$2" 16 | local option_value="$(tmux show-option -qv "$option")" 17 | if [ -z "$option_value" ]; then 18 | option_value="$(tmux show-option -gqv "$option")" 19 | fi 20 | if [ -z "$option_value" ]; then 21 | echo "$default_value" 22 | else 23 | echo "$option_value" 24 | fi 25 | } 26 | 27 | resurrect_dir() { 28 | if [ -z "$_RESURRECT_DIR" ]; then 29 | local path="$(get_tmux_option "$resurrect_dir_option" "$default_resurrect_dir")" 30 | # expands tilde, $HOME and $HOSTNAME if used in $resurrect-dir 31 | echo "$path" | sed "s,\$HOME,$HOME,g; s,\$HOSTNAME,$(hostname),g; s,\~,$HOME,g" 32 | else 33 | echo "$_RESURRECT_DIR" 34 | fi 35 | } 36 | 37 | snapshot_dir() { 38 | local path="$(get_tmux_option "$snapshot_dir_option" "$(resurrect_dir)")" 39 | echo "$path" | sed "s,\$HOME,$HOME,g; s,\$HOSTNAME,$(hostname),g; s,\~,$HOME,g" 40 | } 41 | 42 | last_resurrect_file() { 43 | echo "$(resurrect_dir)/last" 44 | } 45 | 46 | last_resurrect_path() { 47 | readlink "$(last_resurrect_file)" 48 | } 49 | 50 | check_last_snapshot_exists() { 51 | local resurrect_file=$(last_resurrect_file) 52 | if [ ! -f "$resurrect_file" ]; then 53 | return 1 54 | fi 55 | } 56 | 57 | files_differ() { 58 | ! cmp -s "$1" "$2" 59 | } 60 | -------------------------------------------------------------------------------- /named-snapshot.tmux: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" 4 | 5 | source "$CURRENT_DIR/scripts/helpers.sh" 6 | source "$CURRENT_DIR/scripts/variables.sh" 7 | 8 | bind_prompt_key() { 9 | local action="$1" 10 | local key="$2" 11 | local switch_client="$4" 12 | local client_name="" 13 | 14 | if [[ "$action" == "save" ]]; then 15 | local prompt='Save Snapshot as:' 16 | else 17 | local prompt='Restore Snapshot:' 18 | fi 19 | if [ -n "$switch_client" ]; then 20 | client_name="$(echo "$switch_client" | cut -d':' -f2)" 21 | tmux bind-key -T "$client_name" "$key" command-prompt -p "$prompt" "run-shell '$CURRENT_DIR/scripts/$action-snapshot.sh %1'" 22 | else 23 | tmux bind-key "$key" command-prompt -p "$prompt" "run-shell '$CURRENT_DIR/scripts/$action-snapshot.sh %1'" 24 | fi 25 | } 26 | 27 | bind_key() { 28 | local action="$1" 29 | local key="$2" 30 | local name="$3" 31 | local switch_client="$4" 32 | 33 | if test "$name" = "*"; then 34 | bind_prompt_key "$@" 35 | else 36 | if [ -n "$switch_client" ]; then 37 | local client_name="$(echo "$switch_client" | cut -d':' -f2)" 38 | tmux bind-key -T "$client_name" "$key" run-shell "$CURRENT_DIR/scripts/$action-snapshot.sh $name" 39 | else 40 | tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/$action-snapshot.sh $name" 41 | fi 42 | fi 43 | } 44 | 45 | set_switch_client_table() { 46 | local switch_client="$1" 47 | local key="$(echo "$switch_client" | cut -d':' -f1)" 48 | local name="$(echo "$switch_client" | cut -d':' -f2)" 49 | 50 | tmux bind-key "$key" "switch-client -T $name; display-message 'Named Snapshot Mode'" 51 | } 52 | 53 | set_save_bindings() { 54 | local switch_client="$1" 55 | local key_bindings="$(get_tmux_option "$named_save_option" "$default_save_option")" 56 | local keymap 57 | for keymap in $key_bindings; do 58 | local key="$(echo "$keymap" | cut -d':' -f1)" 59 | local name="$(echo "$keymap" | cut -d':' -f2)" 60 | bind_key save "$key" "$name" "$switch_client" 61 | done 62 | } 63 | 64 | set_restore_bindings() { 65 | local switch_client="$1" 66 | local key_bindings="$(get_tmux_option "$named_restore_option" "$default_restore_option")" 67 | local keymap 68 | for keymap in $key_bindings; do 69 | local key="$(echo "$keymap" | cut -d':' -f1)" 70 | local name="$(echo "$keymap" | cut -d':' -f2)" 71 | bind_key restore "$key" "$name" "$switch_client" 72 | done 73 | } 74 | 75 | main() { 76 | local switch_client="$(get_tmux_option "$switch_client_option" "$default_switch_client_option")" 77 | if [ -n "$switch_client" ]; then 78 | set_switch_client_table "$switch_client" 79 | fi 80 | 81 | set_save_bindings "$switch_client" 82 | set_restore_bindings "$switch_client" 83 | } 84 | 85 | main 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tmux-named-snapshot 2 | 3 | This plugin will allow you to save and restore 4 | [tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) snapshots 5 | into its own separate snapshot, making it easy to keep track of tmux session setup. 6 | 7 | ## Getting Started 8 | 9 | This plugin is shipped with these key bindings 10 | 11 | - `Prefix + Ctrl-m`: Save 'manual' snapshot 12 | - `Prefix + M`: Prompt for a name and save the snapshot under that name 13 | - `Prefix + Ctrl-n`: Restore 'manual' snapshot 14 | - `Prefix + N`: Prompt for a name and restore the snapshot by that name 15 | 16 | Check out [Configurations](#configurations) section below to customize the 17 | key bindings and any additional options. 18 | 19 | ## Configurations 20 | 21 | - `@named-snapshot-save` 22 | Description: A list of key mapping to be bound to save command 23 | Default: `C-m:manual M:*` 24 | Values: a space separated keymap, in which consists of colon separated strings 25 | - `@named-snapshot-restore` 26 | Description: A list of key mapping to be bound to restore command 27 | Default: `C-n:manual N:*` 28 | Values: a space separated keymap, in which consists of colon separated strings 29 | 30 | Each mapping should consists of key and its corresponding snapshot name. So 31 | a mapping of `C-m:manual` will map a `manual` snapshot to `C-m` key binding. 32 | 33 | A special snapshot name `*` will prompt for a snapshot name before 34 | performing the action. 35 | 36 | You can always map multiple key bindings to the same snapshot name. 37 | 38 | - `@named-snapshot-switch-client` 39 | Description: A specification of an optional 40 | [switch-client]() 41 | keybinding to define all specified save/restore bindings in a separate "namespace". 42 | Default: _Empty_ (not used by default) 43 | Values: a colon separated strings with "key:table_name" 44 | 45 | - `@named-snapshot-dir` 46 | Description: A path (without a trailing slash) to the directory for storing 47 | named snapshots (missing directory will **NOT** be created automatically) 48 | Default: _Empty_ (default to `@resurrect_dir` option) 49 | Value: a string to be used as a path 50 | 51 | ### Examples 52 | 53 | To setup the key bindings, the configuration should be put in `.tmux.conf` 54 | file. 55 | 56 | For example, 57 | 58 | ``` 59 | set -g @named-snapshot-save 'C-m:manual M:* C-d:dev' 60 | set -g @named-snapshot-restore 'C-n:manual N:* D:dev' 61 | ``` 62 | 63 | will setup the following key bindings 64 | 65 | - `Prefix + Ctrl-m`: Save 'manual' snapshot 66 | - `Prefix + M`: Prompt for a name and save the snapshot under that name 67 | - `Prefix + Ctrl-d`: Save 'dev' snapshot 68 | - `Prefix + Ctrl-n`: Restore 'manual' snapshot 69 | - `Prefix + N`: Prompt for a name and restore the snapshot by that name 70 | - `Prefix + D`: Restore 'dev' snapshot 71 | 72 | You can also define a separate "namespace" for all save/restore bindings using `switch-client` 73 | feature in `tmux`. 74 | 75 | To do this, define the `@named-snapshot-switch-client` parameter with a value of type `N:tns`, 76 | where `N` (stands for `Named`) is the preferred key to enter "Named Snapshot Mode" and `tns` 77 | (stands for `tmux-named-snapshot`) is the name of the key-table for the switch-client. 78 | 79 | For example, 80 | 81 | ``` 82 | set -g @named-snapshot-switch-client 'N:tns' 83 | set -g @named-snapshot-save 'm:manual p:* d:dev' 84 | set -g @named-snapshot-restore 'M:manual P:* D:dev' 85 | ``` 86 | 87 | will setup the following key bindings 88 | 89 | - `Prefix + N`: To enter "Named Snapshot Mode" 90 | 91 | While in this mode: 92 | 93 | - `m`: Save 'manual' snapshot 94 | - `p`: Prompt for a name and save the snapshot under that name 95 | - `d`: Save 'dev' snapshot 96 | - `M`: Restore 'manual' snapshot 97 | - `P`: Prompt for a name and restore the snapshot by that name 98 | - `D`: Restore 'dev' snapshot 99 | 100 | ## Installation 101 | 102 | ### Requirements 103 | 104 | Please note that this plugin utilize multiple unix tools to deliver its 105 | functionalities (most of these tools should be already installed on most unix systems) 106 | 107 | - `sed` 108 | - `cut` 109 | - `readlink` 110 | - `cmp` 111 | 112 | ### Using [TPM](https://github.com/tmux-plugins/tpm) 113 | 114 | ``` 115 | set -g @plugin 'spywhere/tmux-named-snapshot' 116 | ``` 117 | 118 | ### Manual 119 | 120 | Clone the repo 121 | 122 | ``` 123 | $ git clone https://github.com/spywhere/tmux-named-snapshot ~/target/path 124 | ``` 125 | 126 | Then add this line into your `.tmux.conf` 127 | 128 | ``` 129 | run-shell ~/target/path/named-snapshot.tmux 130 | ``` 131 | 132 | Once you reloaded your tmux configuration, all the format strings in the status 133 | bar should be updated automatically. 134 | 135 | ## License 136 | 137 | MIT 138 | --------------------------------------------------------------------------------