80 Commits

Author SHA1 Message Date
442b3ed244 v2.0.0 2015-02-10 12:57:24 +01:00
b91e18cb0e Remove deprecated and key bindings 2015-02-10 12:56:23 +01:00
869be6fa5a Update readme
Fixes #71
2015-02-10 12:48:53 +01:00
028e7b7c2e Refactoring: prefer using variable with tab character 2015-02-10 12:48:53 +01:00
b502572a07 Restore active and alternate windows only if they are present in the 'last' file 2015-02-10 12:48:53 +01:00
1c8efb034a If there are no grouped sessions, do not output empty line 2015-02-10 12:48:53 +01:00
95ec3c1d9b Save and restore active and alternate windows for grouped sessions 2015-02-10 12:48:53 +01:00
5dc22a4a9b Restore grouped sessions 2015-02-10 12:48:53 +01:00
bfc625e13f Save grouped sessions
Grouped sessions are session started with the command
`tmux new-session -t <existing-session-name>`

Those sessions share the panes and windows with the existing, original
session.
We're making sure to identify and save grouped sessions.
On the other hand, we don't want to save panes and windows twice
2015-02-10 12:48:53 +01:00
3244b809eb Merge pull request #67 from SilentGob/master
Correct ps flag for OpenBSD
2015-01-16 00:25:50 +01:00
a9ac17a8f8 Correct ps flag for OpenBSD
Same flag as FreeBSD is needed
2015-01-15 23:20:29 +01:00
9eae48b87e Update README 2014-11-21 14:47:57 +01:00
e8401ba305 Merge pull request #56 from cunha/master
allow relative paths in @resurrect-dir
2014-11-09 20:25:26 +01:00
e770c87e10 v1.5.0 2014-11-09 16:43:44 +01:00
601366be6d Support for restoring neovim sessions 2014-11-09 16:42:53 +01:00
831fc5a458 allow relative paths in @resurrect-dir
this has been tested to work with set -g @resurrect-dir '.tmux/resu rrect'
2014-11-07 11:52:21 -02:00
059686ab6c Merge pull request #55 from rburny/master
Fixes to #51 and #52
2014-10-26 16:59:41 +01:00
488f086fa5 Add a note about HISTCONTROL to README. 2014-10-26 02:17:12 +02:00
fcf7ca13f0 Only save pane history if its not running any program (other than Bash
shell). Fixes a bug where 'history -w' was sent to pane running Bash
script.
2014-10-26 01:30:00 +02:00
e38eed7dae v1.4.0 2014-10-25 18:46:15 +02:00
a35d5f9b90 Update changelog & readme for the zoomed windows feature
Closes #47, #54
2014-10-25 18:43:56 +02:00
0b496dd228 Removing the last_resized variable
Instead resizing only if pane_active is set so we are sure the resizep
is called only once
2014-10-25 18:29:52 +02:00
ad52ade4bf Preserving layout of zoomed windows across restores
The problem is that tmux list-window shows only the current pane layout
if a pane is maximized. This is a bug in tmux. In order to avoid this
bug we unzoom the window when saving and zoom in again after saving.
This implies that the Z flag is no longer set in list-windows, and so it
can't be used when restoring. Instead we use the Z flag of the panes
(which still have it) to restore the zoom.
2014-10-25 18:29:52 +02:00
3ba092459a Merge pull request #49 from tmux-plugins/bash_history
Bash save and restore history feature
2014-10-20 23:17:46 +02:00
6a6d65b98a Update the readme 2014-10-20 23:16:56 +02:00
f3fe4acc39 Document bash restore history feature 2014-10-17 22:40:15 +02:00
8684d4592b Flag gate the bash history restore feature 2014-10-17 22:33:29 +02:00
8328de41d8 Update changelog 2014-10-17 17:47:10 +02:00
94985fc500 Extract save_shell_history to a separate function 2014-10-17 17:45:49 +02:00
81982b5114 Add bash history saving and restoring (first version).
This does not yet have flag to turn the feature off.
2014-10-17 17:14:33 +02:00
a73c465e47 Add issue video screenshot 2014-10-07 13:55:23 +02:00
4ba0e398b9 Document tmux env save dir 2014-10-06 12:59:14 +02:00
8fd38588c0 Fix ps command flags for FreeBSD
This was reported by @duck in #45
2014-09-29 13:39:41 +02:00
a7fe9dcac3 Command line script that fully restores tmux environment 2014-09-24 14:33:46 +02:00
ec9f68cad5 Quote arguments in tmux display-message 2014-09-24 14:30:05 +02:00
94594efdb0 Small bugfix: text command arguments 2014-09-24 14:25:30 +02:00
1b79eb2f63 Rename default strategy to ps 2014-09-21 15:12:35 +02:00
8ebda79f68 Implement save command strategy gdb
@danschumann originally came up with this strategy in #44
2014-09-21 00:08:41 +02:00
ae9083e695 Implement save command strategy: pgrep 2014-09-20 23:55:19 +02:00
9f7050aaae Use a strategy when fetching pane full command 2014-09-20 23:47:15 +02:00
99abfa5f13 Small readme update 2014-09-20 22:45:37 +02:00
95303946b2 v1.3.0 2014-09-20 22:36:42 +02:00
1d09f07d2b Remove dependency on pgrep; use ps to get process names
Fixes #43
2014-09-20 22:33:14 +02:00
618769b62f Update readme 2014-09-20 13:23:42 +02:00
fac377bf8c Add bash to the dependency list 2014-09-10 21:53:38 +02:00
dc7561df74 Small readme tweak 2014-09-10 13:26:14 +02:00
eb2cd31d4b Update readme
Do not invite people to negotiate default program restore list.
2014-09-07 10:19:03 +02:00
e242ea6d8d v1.2.1 2014-09-02 22:48:50 +02:00
a0a3f2fd56 When a pane is not restored, don't restore it's program 2014-09-02 22:47:38 +02:00
d606106f1c Fix: command prompt not ideal after a restore
Fixes #36
2014-09-02 22:34:00 +02:00
d5598d1c61 Update readme 2014-09-01 21:18:15 +02:00
db05b2133b v1.2.0 2014-09-01 20:32:54 +02:00
8368355240 Enable inline strategies when restoring programs 2014-09-01 20:32:27 +02:00
20c5fc40cc v1.1.0 2014-08-31 11:37:14 +02:00
af3cb5db2e ctrl key mappings; deprecate alt keys 2014-08-31 11:35:58 +02:00
deb3e9fdce Add a reference to other plugins in the readme 2014-08-31 01:38:51 +02:00
3682cf6170 Bugfix: sourcing variables file 2014-08-30 21:43:08 +02:00
6255154190 Readme tweak 2014-08-30 17:30:30 +02:00
a6eb17f8fd Fix a link to tpope/vim-obsession 2014-08-30 17:11:14 +02:00
27b9b41e21 Merge pull request #27 from michaelmior/patch-1
Minor README fixes
2014-08-30 16:25:38 +02:00
4d5557d599 Minor README fixes 2014-08-30 10:24:54 -04:00
f5cfa2daa7 Update readme 2014-08-30 14:41:26 +02:00
f2533ec0ef Mention alternative in the readme 2014-08-30 14:40:38 +02:00
d0f6f6ca30 v1.0.0 2014-08-30 11:18:26 +02:00
34a1b4647d Update screencast image in the readme 2014-08-30 10:51:59 +02:00
a68a786a73 Add screencast link to the readme 2014-08-30 10:48:59 +02:00
19c981545e Make the default program running list even more conservative 2014-08-30 00:15:15 +02:00
571bcb8173 Add screencast script 2014-08-30 00:11:51 +02:00
2b259cf11a Show spinner during the env save process 2014-08-29 19:51:47 +02:00
f9ad59900a Update readme 2014-08-29 19:12:28 +02:00
beb54b62fb v0.4.0 2014-08-29 19:11:21 +02:00
bd095e739d Change plugin name and all the variables 2014-08-29 18:59:14 +02:00
e2e55c6faa v0.3.0 2014-08-29 17:21:27 +02:00
cedd1292c1 Restore window zoom state
Close #25
2014-08-29 17:04:00 +02:00
05cf790493 Enable restoring more panes per window
Closes #24
2014-08-29 16:16:21 +02:00
f9ef86d604 Do not restore processes within existing panes
Closes #23
2014-08-29 15:50:18 +02:00
9a6e4a1a2c Make pane restorations idempotent 2014-08-29 12:49:06 +02:00
bd13c9bae8 Clean out comments 2014-08-29 01:04:50 +02:00
87b2d75794 Update readme 2014-08-29 01:04:03 +02:00
54f47a4015 Properly restore pane with top program
Fixes #17
2014-08-29 00:42:11 +02:00
22 changed files with 996 additions and 345 deletions

View File

@ -2,6 +2,65 @@
### master
### v2.0.0, 2015-02-10
- add link to the wiki page for "first pane/window issue" to the README as well
as other tweaks
- save and restore grouped sessions (used with multi-monitor workflow)
- save and restore active and alternate windows in grouped sessions
- if there are no grouped sessions, do not output empty line to "last" file
- restore active and alternate windows only if they are present in the "last" file
- refactoring: prefer using variable with tab character
- remove deprecated `M-s` and `M-r` key bindings (breaking change)
### v1.5.0, 2014-11-09
- add support for restoring neovim sessions
### v1.4.0, 2014-10-25
- plugin now uses strategies when fetching pane full command. Implemented
'default' strategy.
- save command strategy: 'pgrep'. It's here only if fallback is needed.
- save command strategy: 'gdb'
- rename default strategy name to 'ps'
- create `expect` script that can fully restore tmux environment
- fix default save command strategy `ps` command flags. Flags are different for
FreeBSD.
- add bash history saving and restoring (@rburny)
- preserving layout of zoomed windows across restores (@Azrael3000)
### v1.3.0, 2014-09-20
- remove dependency on `pgrep` command. Use `ps` for fetching process names.
### v1.2.1, 2014-09-02
- tweak 'new_pane' creation strategy to fix #36
- when running multiple tmux server and for a large number of panes (120 +) when
doing a restore, some panes might not be created. When that is the case also
don't restore programs for those panes.
### v1.2.0, 2014-09-01
- new feature: inline strategies when restoring a program
### v1.1.0, 2014-08-31
- bugfix: sourcing `variables.sh` file in save script
- add `Ctrl` key mappings, deprecate `Alt` keys mappings.
### v1.0.0, 2014-08-30
- show spinner during the save process
- add screencast script
- make default program running list even more conservative
### v0.4.0, 2014-08-29
- change plugin name to `tmux-resurrect`. Change all the variable names.
### v0.3.0, 2014-08-29
- bugfix: when top is running the pane `$PWD` can't be saved. This was causing
issues during the restore and is now fixed.
- restoring sessions multiple times messes up the whole environment - new panes
are all around. This is now fixed - pane restorations are now idempotent.
- if pane exists from before session restore - do not restore the process within
it. This makes the restoration process even more idempotent.
- more panes within a window can now be restored
- restore window zoom state
### v0.2.0, 2014-08-29
- bugfix: with vim 'session' strategy, if the session file does not exist - make
sure vim does not contain `-S` flag

View File

@ -6,7 +6,7 @@ Code contributions are welcome!
If you find a bug please report it in the issues. When reporting a bug please
attach:
- a file symlinked to `~/.tmux/sessions/last`.
- a file symlinked to `~/.tmux/resurrect/last`.
- your `.tmux.conf`
- if you're getting an error paste it to a [gist](https://gist.github.com/) and
link it in the issue

138
README.md
View File

@ -1,22 +1,32 @@
# Tmux Session Saver
# Tmux Resurrect
Persists `tmux` environment across system restarts.
Restore `tmux` environment after a system restart.
Tmux is great, except when you have to restart your computer. You loose all the
Tmux is great, except when you have to restart the computer. You lose all the
running programs, working directories, pane layouts etc.
There are helpful management tools out there, but they require initial
configuration and continuous updates as your workflow evolves or you start new
projects.
Enter `tmux-session-saver`: tmux persistence without configuration so there are
no interruptions in your workflow.
`tmux-resurrect` saves all the little details from your tmux environment so it
can be completely restored after a system restart (or when you feel like it).
No configuration is required. You should feel like you never quit tmux.
It will even (optionally) [restore vim sessions](#restoring-vim-sessions)!
It even (optionally) [restores vim and neovim sessions](#restoring-vim-and-neovim-sessions)!
### Screencast
[![screencast screenshot](/video/screencast_img.png)](https://vimeo.com/104763018)
### Key bindings
- `prefix + Alt-s` - save
- `prefix + Alt-r` - restore
- `prefix + Ctrl-s` - save
- `prefix + Ctrl-r` - restore
For custom key bindings, add to `.tmux.conf`:
set -g @resurrect-save 'S'
set -g @resurrect-restore 'R'
### About
@ -25,24 +35,40 @@ This plugin goes to great lengths to save and restore all the details from your
- all sessions, windows, panes and their order
- current working directory for each pane
- **exact pane layouts** within windows
- **exact pane layouts** within windows (even when zoomed)
- active and alternative session
- active and alternative window for each session
- windows with focus
- active pane for each window
- programs running within a pane! More details in the [configuration section](#configuration).
- restoring vim sessions (optional). More details in
[restoring vim sessions](#restoring-vim-sessions).
- "grouped sessions" (useful feature when using tmux with multiple monitors)
- programs running within a pane! More details in the
[configuration section](#configuration).
- restoring vim/neovim sessions (optional). More details in
[restoring vim and neovim sessions](#restoring-vim-and-neovim-sessions).
- restoring bash history (optional, \*experimental*). More details in
[restoring bash history](#restoring-bash-history-experimental).
Requirements / dependencies: `tmux 1.9` or higher, `pgrep`
Requirements / dependencies: `tmux 1.9` or higher, `bash`.
`tmux-resurrect` is idempotent! It will not try to restore panes or windows that
already exist.
### FAQ
> I have a problem: first pane/window is not restoring!
Check out
[this wiki page](https://github.com/tmux-plugins/tmux-resurrect/wiki/Help:-issues-with-the-first-window)
for the explanation and problem solution.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @tpm_plugins " \
tmux-plugins/tpm \
tmux-plugins/tmux-session-saver \
"
set -g @tpm_plugins ' \
tmux-plugins/tpm \
tmux-plugins/tmux-resurrect \
'
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin.
@ -51,11 +77,11 @@ use the plugin.
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-session-saver ~/clone/path
$ git clone https://github.com/tmux-plugins/tmux-resurrect ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/session_saver.tmux
run-shell ~/clone/path/resurrect.tmux
Reload TMUX environment:
@ -66,39 +92,78 @@ You should now be able to use the plugin.
### Configuration
Only a conservative list of programs is restored by default:
`vi vim emacs man less more tail top htop irssi irb pry "~rails console"`.
Open a github issue if you think some other program should be on the default list.
Configuration is not required, but it enables extra features.
Only a conservative list of programs is restored by default:<br/>
`vi vim nvim emacs man less more tail top htop irssi`.
- Restore additional programs with the setting in `.tmux.conf`:
set -g @session-saver-processes 'ssh psql mysql sqlite3'
set -g @resurrect-processes 'ssh psql mysql sqlite3'
- Programs with arguments should be double quoted:
set -g @session-saver-processes 'some_program "git log"'
set -g @resurrect-processes 'some_program "git log"'
- Start with tilde to restore a program whose process contains target name:
set -g @session-saver-processes 'some_program "~rails server"'
set -g @resurrect-processes 'irb pry "~rails server" "~rails console"'
- Use `->` to specify a command to be used when restoring a program (useful if
the default restore command fails ):
set -g @resurrect-processes 'some_program "grunt->grunt development"'
- Don't restore any programs:
set -g @session-saver-processes 'false'
set -g @resurrect-processes 'false'
- Restore **all** programs (be careful with this!):
set -g @session-saver-processes ':all:'
set -g @resurrect-processes ':all:'
#### Restoring vim sessions
#### Restoring vim and neovim sessions
- save vim sessions - I recommend [tpope/vim-obsession](tpope/vim-obsession)
- save vim/neovim sessions. I recommend
[tpope/vim-obsession](https://github.com/tpope/vim-obsession) (as almost every
plugin, it works for both vim and neovim).
- in `.tmux.conf`:
set -g @session-saver-strategy-vim "session"
# for vim
set -g @resurrect-strategy-vim 'session'
# for neovim
set -g @resurrect-strategy-nvim 'session'
`tmux-session-saver` will now restore vim sessions if `Sessions.vim` file is
present.
`tmux-resurrect` will now restore vim and neovim sessions if `Sessions.vim` file
is present.
#### Resurrect save dir
By default Tmux environment is saved to a file in `~/.tmux/resurrect` dir.
Change this with:
set -g @resurrect-dir '/some/path'
#### Restoring bash history (experimental)
In `.tmux.conf`:
set -g @resurrect-save-bash-history 'on'
Bash `history` for individual panes will now be saved and restored. Due to
technical limitations, this only works for panes which have no program running in
foreground when saving. `tmux-resurrect` will send history write command
to each such pane. To prevent these commands from being added to history themselves,
add `HISTCONTROL=ignoreboth` to your `.bashrc` (this is set by default in Ubuntu).
### Other goodies
- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for
regex searches in tmux and fast match selection
- [tmux-yank](https://github.com/tmux-plugins/tmux-yank) - enables copying
highlighted text to system clipboard
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
opening highlighted file or a url
### Reporting bugs and contributing
@ -108,7 +173,14 @@ Both contributing and bug reports are welcome. Please check out
### Credits
[Mislav Marohnić](https://github.com/mislav) - the idea for the plugin came from his
[tmux-session script](https://github.com/mislav/dotfiles/blob/master/bin/tmux-session).
[tmux-session script](https://github.com/mislav/dotfiles/blob/2036b5e03fb430bbcbc340689d63328abaa28876/bin/tmux-session).
### Other
Here's another script that tries to solve the same problem:
[link](http://brainscraps.wikia.com/wiki/Resurrecting_tmux_Sessions_After_Reboot).
It even has the same name, even though I discovered it only after publishing
`v1.0` of this plugin.
### License
[MIT](LICENSE.md)

View File

@ -9,7 +9,7 @@ set_save_bindings() {
local key_bindings=$(get_tmux_option "$save_option" "$default_save_key")
local key
for key in $key_bindings; do
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/session_saver.sh"
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/save.sh"
done
}
@ -17,7 +17,7 @@ set_restore_bindings() {
local key_bindings=$(get_tmux_option "$restore_option" "$default_restore_key")
local key
for key in $key_bindings; do
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/session_restorer.sh"
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/restore.sh"
done
}

22
save_command_strategies/gdb.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
gdb -batch --eval "attach $PANE_PID" --eval "call write_history(\"/tmp/bash_history-${PANE_PID}.txt\")" --eval 'detach' --eval 'q' >/dev/null 2>&1
\tail -1 "/tmp/bash_history-${PANE_PID}.txt"
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
\pgrep -lf -P "$PANE_PID" |
cut -d' ' -f2-
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

32
save_command_strategies/ps.sh Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
ps_command_flags() {
case $(uname -s) in
FreeBSD) echo "-ao" ;;
OpenBSD) echo "-ao" ;;
*) echo "-eo" ;;
esac
}
full_command() {
ps "$(ps_command_flags)" "ppid command" |
sed "s/^ *//" |
grep "^${PANE_PID}" |
cut -d' ' -f2-
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -1,9 +1,10 @@
# configurable constants
default_sessions_dir="$HOME/.tmux/sessions"
sessions_dir_option="@session-saver-dir"
default_resurrect_dir="$HOME/.tmux/resurrect"
resurrect_dir_option="@resurrect-dir"
SUPPORTED_VERSION="1.9"
d=$'\t'
# helper functions
get_tmux_option() {
local option="$1"
@ -50,17 +51,44 @@ remove_first_char() {
echo "$1" | cut -c2-
}
save_bash_history_option_on() {
local option="$(get_tmux_option "$bash_history_option" "off")"
[ "$option" == "on" ]
}
get_grouped_sessions() {
local grouped_sessions_dump="$1"
export GROUPED_SESSIONS="${d}$(echo "$grouped_sessions_dump" | cut -f2 -d"$d" | tr "\\n" "$d")"
}
is_session_grouped() {
local session_name="$1"
[[ "$GROUPED_SESSIONS" == *"${d}${session_name}${d}"* ]]
}
# path helpers
sessions_dir() {
echo $(get_tmux_option "$sessions_dir_option" "$default_sessions_dir")
resurrect_dir() {
echo $(get_tmux_option "$resurrect_dir_option" "$default_resurrect_dir")
}
session_path() {
resurrect_file_path() {
local timestamp="$(date +"%Y-%m-%dT%H:%M:%S")"
echo "$(sessions_dir)/tmux_session_${timestamp}.txt"
echo "$(resurrect_dir)/tmux_resurrect_${timestamp}.txt"
}
last_session_path() {
echo "$(sessions_dir)/last"
last_resurrect_file() {
echo "$(resurrect_dir)/last"
}
resurrect_history_file() {
local pane_id="$1"
echo "$(resurrect_dir)/bash_history-${pane_id}"
}
restore_zoomed_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $6 ~ /Z/ && $9 == 1 { print $2, $3; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number; do
tmux resize-pane -t "${session_name}:${window_number}" -Z
done
}

View File

@ -13,16 +13,18 @@ restore_pane_process() {
local window_number="$3"
local pane_index="$4"
local dir="$5"
if _process_should_be_restored "$pane_full_command"; then
if _process_should_be_restored "$pane_full_command" "$session_name" "$window_number" "$pane_index"; then
tmux switch-client -t "${session_name}:${window_number}"
tmux select-pane -t "$pane_index"
if _strategy_exists "$pane_full_command"; then
local inline_strategy="$(_get_inline_strategy "$pane_full_command")" # might not be defined
if [ -n "$inline_strategy" ]; then
# inline strategy exists
tmux send-keys "$inline_strategy" "C-m"
elif _strategy_exists "$pane_full_command"; then
local strategy_file="$(_get_strategy_file "$pane_full_command")"
local strategy_command="$($strategy_file "$pane_full_command" "$dir")"
tmux send-keys "$strategy_command" "C-m"
# tmux send-keys "Strategy! $pane_full_command $strategy_file"
# tmux send-keys "Strategy! $strategy_command"
else
# just invoke the command
tmux send-keys "$pane_full_command" "C-m"
@ -34,7 +36,17 @@ restore_pane_process() {
_process_should_be_restored() {
local pane_full_command="$1"
if _restore_all_processes; then
local session_name="$2"
local window_number="$3"
local pane_index="$4"
if is_pane_registered_as_existing "$session_name" "$window_number" "$pane_index"; then
# Scenario where pane existed before restoration, so we're not
# restoring the proces either.
return 1
elif ! pane_exists "$session_name" "$window_number" "$pane_index"; then
# pane number limit exceeded, pane does not exist
return 1
elif _restore_all_processes; then
return 0
elif _process_on_the_restore_list "$pane_full_command"; then
return 0
@ -45,7 +57,7 @@ _process_should_be_restored() {
_restore_all_processes() {
local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
if [ $restore_processes == ":all:" ]; then
if [ "$restore_processes" == ":all:" ]; then
return 0
else
return 1
@ -57,27 +69,46 @@ _process_on_the_restore_list() {
# TODO: make this work without eval
eval set $(_restore_list)
local proc
local match
for proc in "$@"; do
if _proc_starts_with_tildae "$proc"; then
proc="$(remove_first_char "$proc")"
# regex matching the command makes sure `$proc` string is somewhere the command string
if [[ "$pane_full_command" =~ ($proc) ]]; then
return 0
fi
else
# regex matching the command makes sure process is a "word"
if [[ "$pane_full_command" =~ (^${proc} ) ]] || [[ "$pane_full_command" =~ (^${proc}$) ]]; then
return 0
fi
match="$(_get_proc_match_element "$proc")"
if _proc_matches_full_command "$pane_full_command" "$match"; then
return 0
fi
done
return 1
}
_proc_matches_full_command() {
local pane_full_command="$1"
local match="$2"
if _proc_starts_with_tildae "$match"; then
match="$(remove_first_char "$match")"
# regex matching the command makes sure `$match` string is somewhere in the command string
if [[ "$pane_full_command" =~ ($match) ]]; then
return 0
fi
else
# regex matching the command makes sure process is a "word"
if [[ "$pane_full_command" =~ (^${match} ) ]] || [[ "$pane_full_command" =~ (^${match}$) ]]; then
return 0
fi
fi
return 1
}
_get_proc_match_element() {
echo "$1" | sed "s/${inline_strategy_token}.*//"
}
_get_proc_restore_element() {
echo "$1" | sed "s/.*${inline_strategy_token}//"
}
_restore_list() {
local user_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
local default_processes="$(get_tmux_option "$default_proc_list_option" "$default_proc_list")"
if [ -z $user_processes ]; then
if [ -z "$user_processes" ]; then
# user didn't define any processes
echo "$default_processes"
else
@ -89,6 +120,22 @@ _proc_starts_with_tildae() {
[[ "$1" =~ (^~) ]]
}
_get_inline_strategy() {
local pane_full_command="$1"
# TODO: make this work without eval
eval set $(_restore_list)
local proc
local match
for proc in "$@"; do
if [[ "$proc" =~ "$inline_strategy_token" ]]; then
match="$(_get_proc_match_element "$proc")"
if _proc_matches_full_command "$pane_full_command" "$match"; then
echo "$(_get_proc_restore_element "$proc")"
fi
fi
done
}
_strategy_exists() {
local pane_full_command="$1"
local strategy="$(_get_command_strategy "$pane_full_command")"

14
scripts/restore.exp Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env expect
# start tmux
spawn tmux -S/tmp/foo
# delay with sleep to compensate for tmux starting time
sleep 2
# run restore script directly
send "~/.tmux/plugins/tmux-resurrect/scripts/restore.sh\r"
# long wait until tmux restore is complete
# (things get messed up if expect client isn't attached)
sleep 100

256
scripts/restore.sh Executable file
View File

@ -0,0 +1,256 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/process_restore_helpers.sh"
source "$CURRENT_DIR/spinner_helpers.sh"
# delimiter
d=$'\t'
# Global variable.
# Used during the restore: if a pane already exists from before, it is
# saved in the array in this variable. Later, process running in existing pane
# is also not restored. That makes the restoration process more idempotent.
EXISTING_PANES_VAR=""
is_line_type() {
local line_type="$1"
local line="$2"
echo "$line" |
\grep -q "^$line_type"
}
check_saved_session_exists() {
local resurrect_file="$(last_resurrect_file)"
if [ ! -f $resurrect_file ]; then
display_message "Tmux resurrect file not found!"
return 1
fi
}
pane_exists() {
local session_name="$1"
local window_number="$2"
local pane_index="$3"
tmux list-panes -t "${session_name}:${window_number}" -F "#{pane_index}" 2>/dev/null |
\grep -q "^$pane_index$"
}
register_existing_pane() {
local session_name="$1"
local window_number="$2"
local pane_index="$3"
local pane_custom_id="${session_name}:${window_number}:${pane_index}"
local delimiter=$'\t'
EXISTING_PANES_VAR="${EXISTING_PANES_VAR}${delimiter}${pane_custom_id}"
}
is_pane_registered_as_existing() {
local session_name="$1"
local window_number="$2"
local pane_index="$3"
local pane_custom_id="${session_name}:${window_number}:${pane_index}"
[[ "$EXISTING_PANES_VAR" =~ "$pane_custom_id" ]]
}
window_exists() {
local session_name="$1"
local window_number="$2"
tmux list-windows -t "$session_name" -F "#{window_index}" 2>/dev/null |
\grep -q "^$window_number$"
}
session_exists() {
local session_name="$1"
tmux has-session -t "$session_name" 2>/dev/null
}
first_window_num() {
tmux show -gv base-index
}
tmux_socket() {
echo $TMUX | cut -d',' -f1
}
new_window() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
tmux new-window -d -t "${session_name}:${window_number}" -n "$window_name" -c "$dir"
}
new_session() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name" -n "$window_name" -c "$dir"
# change first window number if necessary
local created_window_num="$(first_window_num)"
if [ $created_window_num -ne $window_number ]; then
tmux move-window -s "${session_name}:${created_window_num}" -t "${session_name}:${window_number}"
fi
}
new_pane() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
tmux split-window -t "${session_name}:${window_number}" -c "$dir"
# minimize window so more panes can fit
tmux resize-pane -t "${session_name}:${window_number}" -U "999"
}
restore_pane() {
local pane="$1"
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_full_command; do
dir="$(remove_first_char "$dir")"
window_name="$(remove_first_char "$window_name")"
pane_full_command="$(remove_first_char "$pane_full_command")"
if pane_exists "$session_name" "$window_number" "$pane_index"; then
# Pane exists, no need to create it!
# Pane existence is registered. Later, it's process also isn't restored.
register_existing_pane "$session_name" "$window_number" "$pane_index"
elif window_exists "$session_name" "$window_number"; then
new_pane "$session_name" "$window_number" "$window_name" "$dir"
elif session_exists "$session_name"; then
new_window "$session_name" "$window_number" "$window_name" "$dir"
else
new_session "$session_name" "$window_number" "$window_name" "$dir"
fi
done < <(echo "$pane")
}
restore_state() {
local state="$1"
echo "$state" |
while IFS=$d read line_type client_session client_last_session; do
tmux switch-client -t "$client_last_session"
tmux switch-client -t "$client_session"
done
}
restore_all_panes() {
while read line; do
if is_line_type "pane" "$line"; then
restore_pane "$line"
fi
done < $(last_resurrect_file)
}
restore_shell_history() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ { print $2, $3, $7, $10; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number pane_index pane_command; do
if ! is_pane_registered_as_existing "$session_name" "$window_number" "$pane_index"; then
if [ "$pane_command" = "bash" ]; then
local pane_id="$session_name:$window_number.$pane_index"
# tmux send-keys has -R option that should reset the terminal.
# However, appending 'clear' to the command seems to work more reliably.
local read_command="history -r '$(resurrect_history_file "$pane_id")'; clear"
tmux send-keys -t "$pane_id" "$read_command" C-m
fi
fi
done
}
restore_all_pane_processes() {
if restore_pane_processes_enabled; then
local pane_full_command
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $11 !~ "^:$" { print $2, $3, $7, $8, $11; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number pane_index dir pane_full_command; do
dir="$(remove_first_char "$dir")"
pane_full_command="$(remove_first_char "$pane_full_command")"
restore_pane_process "$pane_full_command" "$session_name" "$window_number" "$pane_index" "$dir"
done
fi
}
restore_pane_layout_for_each_window() {
\grep '^window' $(last_resurrect_file) |
while IFS=$d read line_type session_name window_number window_active window_flags window_layout; do
tmux select-layout -t "${session_name}:${window_number}" "$window_layout"
done
}
restore_active_pane_for_each_window() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $9 == 1 { print $2, $3, $7; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number active_pane; do
tmux switch-client -t "${session_name}:${window_number}"
tmux select-pane -t "$active_pane"
done
}
restore_grouped_session() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window active_window; do
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$grouped_session" -t "$original_session"
done
}
restore_active_and_alternate_windows_for_grouped_sessions() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window_index active_window_index; do
alternate_window_index="$(remove_first_char "$alternate_window_index")"
active_window_index="$(remove_first_char "$active_window_index")"
if [ -n "$alternate_window_index" ]; then
tmux switch-client -t "${grouped_session}:${alternate_window_index}"
fi
if [ -n "$active_window_index" ]; then
tmux switch-client -t "${grouped_session}:${active_window_index}"
fi
done
}
restore_grouped_sessions() {
while read line; do
if is_line_type "grouped_session" "$line"; then
restore_grouped_session "$line"
restore_active_and_alternate_windows_for_grouped_sessions "$line"
fi
done < $(last_resurrect_file)
}
restore_active_and_alternate_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^window/ && $5 ~ /[*-]/ { print $2, $4, $3; }' $(last_resurrect_file) |
sort -u |
while IFS=$d read session_name active_window window_number; do
tmux switch-client -t "${session_name}:${window_number}"
done
}
restore_active_and_alternate_sessions() {
while read line; do
if is_line_type "state" "$line"; then
restore_state "$line"
fi
done < $(last_resurrect_file)
}
main() {
if supported_tmux_version_ok && check_saved_session_exists; then
start_spinner "Restoring..." "Tmux restore complete!"
restore_all_panes
restore_pane_layout_for_each_window >/dev/null 2>&1
if save_bash_history_option_on; then
restore_shell_history
fi
restore_all_pane_processes
# below functions restore exact cursor positions
restore_active_pane_for_each_window
restore_zoomed_windows
restore_grouped_sessions # also restores active and alt windows for grouped sessions
restore_active_and_alternate_windows
restore_active_and_alternate_sessions
stop_spinner
display_message "Tmux restore complete!"
fi
}
main

221
scripts/save.sh Executable file
View File

@ -0,0 +1,221 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/spinner_helpers.sh"
# delimiters
d=$'\t'
delimiter=$'\t'
grouped_sessions_format() {
local format
format+="#{session_grouped}"
format+="${delimiter}"
format+="#{session_group}"
format+="${delimiter}"
format+="#{session_id}"
format+="${delimiter}"
format+="#{session_name}"
echo "$format"
}
pane_format() {
local format
format+="pane"
format+="${delimiter}"
format+="#{session_name}"
format+="${delimiter}"
format+="#{window_index}"
format+="${delimiter}"
format+=":#{window_name}"
format+="${delimiter}"
format+="#{window_active}"
format+="${delimiter}"
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{pane_index}"
format+="${delimiter}"
format+=":#{pane_current_path}"
format+="${delimiter}"
format+="#{pane_active}"
format+="${delimiter}"
format+="#{pane_current_command}"
format+="${delimiter}"
format+="#{pane_pid}"
echo "$format"
}
window_format() {
local format
format+="window"
format+="${delimiter}"
format+="#{session_name}"
format+="${delimiter}"
format+="#{window_index}"
format+="${delimiter}"
format+="#{window_active}"
format+="${delimiter}"
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{window_layout}"
echo "$format"
}
state_format() {
local format
format+="state"
format+="${delimiter}"
format+="#{client_session}"
format+="${delimiter}"
format+="#{client_last_session}"
echo "$format"
}
dump_panes_raw() {
tmux list-panes -a -F "$(pane_format)"
}
dump_windows_raw(){
tmux list-windows -a -F "$(window_format)"
}
_save_command_strategy_file() {
local save_command_strategy="$(get_tmux_option "$save_command_strategy_option" "$default_save_command_strategy")"
local strategy_file="$CURRENT_DIR/../save_command_strategies/${save_command_strategy}.sh"
local default_strategy_file="$CURRENT_DIR/../save_command_strategies/${default_save_command_strategy}.sh"
if [ -e "$strategy_file" ]; then # strategy file exists?
echo "$strategy_file"
else
echo "$default_strategy_file"
fi
}
pane_full_command() {
local pane_pid="$1"
local strategy_file="$(_save_command_strategy_file)"
# execute strategy script to get pane full command
$strategy_file "$pane_pid"
}
save_shell_history() {
local pane_id="$1"
local pane_command="$2"
local full_command="$3"
if [ "$pane_command" = "bash" ] && [ "$full_command" = ":" ]; then
# leading space prevents the command from being saved to history
# (assuming default HISTCONTROL settings)
local write_command=" history -w '$(resurrect_history_file "$pane_id")'"
# C-e C-u is a Bash shortcut sequence to clear whole line. It is necessary to
# delete any pending input so it does not interfere with our history command.
tmux send-keys -t "$pane_id" C-e C-u "$write_command" C-m
fi
}
get_active_window_index() {
local session_name="$1"
tmux list-windows -t "$session_name" -F "#{window_flags} #{window_index}" |
awk '$1 ~ /\*/ { print $2; }'
}
get_alternate_window_index() {
local session_name="$1"
tmux list-windows -t "$session_name" -F "#{window_flags} #{window_index}" |
awk '$1 ~ /-/ { print $2; }'
}
dump_grouped_sessions() {
local current_session_group=""
local original_session
tmux list-sessions -F "$(grouped_sessions_format)" |
grep "^1" |
cut -c 3- |
sort |
while IFS=$d read session_group session_id session_name; do
if [ "$session_group" != "$current_session_group" ]; then
# this session is the original/first session in the group
original_session="$session_name"
current_session_group="$session_group"
else
# this session "points" to the original session
active_window_index="$(get_active_window_index "$session_name")"
alternate_window_index="$(get_alternate_window_index "$session_name")"
echo "grouped_session${d}${session_name}${d}${original_session}${d}:${alternate_window_index}${d}:${active_window_index}"
fi
done
}
fetch_and_dump_grouped_sessions(){
local grouped_sessions_dump="$(dump_grouped_sessions)"
get_grouped_sessions "$grouped_sessions_dump"
if [ -n "$grouped_sessions_dump" ]; then
echo "$grouped_sessions_dump"
fi
}
# translates pane pid to process command running inside a pane
dump_panes() {
local full_command
dump_panes_raw |
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid; do
# not saving panes from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
# check if current pane is part of a maximized window and if the pane is active
if [[ "${window_flags}" == *Z* ]] && [[ ${pane_active} == 1 ]]; then
# unmaximize the pane
tmux resize-pane -Z -t "${session_name}:${window_number}"
fi
full_command="$(pane_full_command $pane_pid)"
echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}"
done
}
dump_windows() {
dump_windows_raw |
while IFS=$d read line_type session_name window_index window_active window_flags window_layout; do
# not saving windows from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_active}${d}${window_flags}${d}${window_layout}"
done
}
dump_state() {
tmux display-message -p "$(state_format)"
}
dump_bash_history() {
dump_panes |
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command full_command; do
save_shell_history "$session_name:$window_number.$pane_index" "$pane_command" "$full_command"
done
}
save_all() {
local resurrect_file_path="$(resurrect_file_path)"
mkdir -p "$(resurrect_dir)"
fetch_and_dump_grouped_sessions > "$resurrect_file_path"
dump_panes >> "$resurrect_file_path"
dump_windows >> "$resurrect_file_path"
dump_state >> "$resurrect_file_path"
ln -fs "$(basename "$resurrect_file_path")" "$(last_resurrect_file)"
if save_bash_history_option_on; then
dump_bash_history
fi
restore_zoomed_windows
}
main() {
if supported_tmux_version_ok; then
start_spinner "Saving..." "Tmux environment saved!"
save_all
stop_spinner
display_message "Tmux environment saved!"
fi
}
main

View File

@ -1,163 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/process_restore_helpers.sh"
source "$CURRENT_DIR/spinner_helpers.sh"
is_line_type() {
local line_type="$1"
local line="$2"
echo "$line" |
\grep -q "^$line_type"
}
check_saved_session_exists() {
local saved_session="$(last_session_path)"
if [ ! -f $saved_session ]; then
display_message "Saved tmux session not found!"
return 1
fi
}
window_exists() {
local session_name="$1"
local window_number="$2"
tmux list-windows -t "$session_name" -F "#{window_index}" 2>/dev/null |
\grep -q "^$window_number$"
}
session_exists() {
local session_name="$1"
tmux has-session -t "$session_name" 2>/dev/null
}
first_window_num() {
tmux show -gv base-index
}
tmux_socket() {
echo $TMUX | cut -d',' -f1
}
new_window() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
tmux new-window -d -t "${session_name}:${window_number}" -n "$window_name" -c "$dir"
}
new_session() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name" -n "$window_name" -c "$dir"
# change first window number if necessary
local created_window_num="$(first_window_num)"
if [ $created_window_num -ne $window_number ]; then
tmux move-window -s "${session_name}:${created_window_num}" -t "${session_name}:${window_number}"
fi
}
new_pane() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
tmux split-window -t "${session_name}:${window_number}" -c "$dir"
}
restore_pane() {
local pane="$1"
echo "$pane" |
while IFS=$'\t' read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_full_command; do
window_name="$(remove_first_char "$window_name")"
pane_full_command="$(remove_first_char "$pane_full_command")"
if window_exists "$session_name" "$window_number"; then
new_pane "$session_name" "$window_number" "$window_name" "$dir"
elif session_exists "$session_name"; then
new_window "$session_name" "$window_number" "$window_name" "$dir"
else
new_session "$session_name" "$window_number" "$window_name" "$dir"
fi
done
}
restore_state() {
local state="$1"
echo "$state" |
while IFS=$'\t' read line_type client_session client_last_session; do
tmux switch-client -t "$client_last_session"
tmux switch-client -t "$client_session"
done
}
restore_all_sessions() {
while read line; do
if is_line_type "pane" "$line"; then
restore_pane "$line"
fi
done < $(last_session_path)
}
restore_all_pane_processes() {
if restore_pane_processes_enabled; then
local pane_full_command
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $11 !~ "^:$" { print $2, $3, $7, $8, $11; }' $(last_session_path) |
while IFS=$'\t' read session_name window_number pane_index dir pane_full_command; do
pane_full_command="$(remove_first_char "$pane_full_command")"
restore_pane_process "$pane_full_command" "$session_name" "$window_number" "$pane_index" "$dir"
done
fi
}
restore_pane_layout_for_each_window() {
\grep '^window' $(last_session_path) |
while IFS=$'\t' read line_type session_name window_number window_active window_flags window_layout; do
tmux select-layout -t "${session_name}:${window_number}" "$window_layout"
done
}
restore_active_pane_for_each_window() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $7 != 0 && $9 == 1 { print $2, $3, $7; }' $(last_session_path) |
while IFS=$'\t' read session_name window_number active_pane; do
tmux switch-client -t "${session_name}:${window_number}"
tmux select-pane -t "$active_pane"
done
}
restore_active_and_alternate_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^window/ && $5 ~ /[*-]/ { print $2, $4, $3; }' $(last_session_path) |
sort -u |
while IFS=$'\t' read session_name active_window window_number; do
tmux switch-client -t "${session_name}:${window_number}"
done
}
restore_active_and_alternate_sessions() {
while read line; do
if is_line_type "state" "$line"; then
restore_state "$line"
fi
done < $(last_session_path)
}
main() {
if supported_tmux_version_ok && check_saved_session_exists; then
start_spinner
restore_all_sessions
restore_all_pane_processes
restore_pane_layout_for_each_window >/dev/null 2>&1
# below functions restore exact cursor positions
restore_active_pane_for_each_window
restore_active_and_alternate_windows
restore_active_and_alternate_sessions
stop_spinner
display_message "Restored all Tmux sessions!"
fi
}
main

View File

@ -1,106 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
pane_format() {
local delimiter=$'\t'
local format
format+="pane"
format+="${delimiter}"
format+="#{session_name}"
format+="${delimiter}"
format+="#{window_index}"
format+="${delimiter}"
format+=":#{window_name}"
format+="${delimiter}"
format+="#{window_active}"
format+="${delimiter}"
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{pane_index}"
format+="${delimiter}"
format+="#{pane_current_path}"
format+="${delimiter}"
format+="#{pane_active}"
format+="${delimiter}"
format+="#{pane_current_command}"
format+="${delimiter}"
format+="#{pane_pid}"
echo "$format"
}
window_format() {
local delimiter=$'\t'
local format
format+="window"
format+="${delimiter}"
format+="#{session_name}"
format+="${delimiter}"
format+="#{window_index}"
format+="${delimiter}"
format+="#{window_active}"
format+="${delimiter}"
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{window_layout}"
echo "$format"
}
state_format() {
local delimiter=$'\t'
local format
format+="state"
format+="${delimiter}"
format+="#{client_session}"
format+="${delimiter}"
format+="#{client_last_session}"
echo "$format"
}
dump_panes_raw() {
tmux list-panes -a -F "$(pane_format)"
}
pane_full_command() {
pane_pid="$1"
\pgrep -lf -P "$pane_pid" |
cut -d' ' -f2-
}
# translates pane pid to process command running inside a pane
dump_panes() {
local full_command
local d=$'\t' # delimiter
dump_panes_raw |
while IFS=$'\t' read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid; do
full_command="$(pane_full_command $pane_pid)"
echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}"
done
}
dump_windows() {
tmux list-windows -a -F "$(window_format)"
}
dump_state() {
tmux display-message -p "$(state_format)"
}
save_all_sessions() {
local session_path="$(session_path)"
mkdir -p "$(sessions_dir)"
dump_panes > $session_path
dump_windows >> $session_path
dump_state >> $session_path
ln -fs "$session_path" "$(last_session_path)"
display_message "Saved all Tmux sessions!"
}
main() {
if supported_tmux_version_ok; then
save_all_sessions
fi
}
main

View File

@ -1,5 +1,5 @@
start_spinner() {
$CURRENT_DIR/tmux_spinner.sh "Restoring sessions..." "Restored all Tmux sessions!" &
$CURRENT_DIR/tmux_spinner.sh "$1" "$2" &
export SPINNER_PID=$!
}

View File

@ -16,7 +16,7 @@ MESSAGE="$1"
END_MESSAGE="$2"
SPIN='-\|/'
trap "tmux display-message $END_MESSAGE; exit" SIGINT SIGTERM
trap "tmux display-message '$END_MESSAGE'; exit" SIGINT SIGTERM
main() {
local i=0

View File

@ -1,13 +1,13 @@
# key bindings
default_save_key="M-s"
save_option="@session-saver-save"
default_save_key="C-s"
save_option="@resurrect-save"
default_restore_key="M-r"
restore_option="@session-saver-restore"
default_restore_key="C-r"
restore_option="@resurrect-restore"
# default processes that are restored
default_proc_list_option="@session-saver-default-processes"
default_proc_list='vi vim emacs man less more tail top htop irssi irb pry "~rails console"'
default_proc_list_option="@resurrect-default-processes"
default_proc_list='vi vim nvim emacs man less more tail top htop irssi'
# User defined processes that are restored
# 'false' - nothing is restored
@ -15,9 +15,16 @@ default_proc_list='vi vim emacs man less more tail top htop irssi irb pry "~rail
#
# user defined list of programs that are restored:
# 'my_program foo another_program'
restore_processes_option="@session-saver-processes"
restore_processes_option="@resurrect-processes"
restore_processes=""
# Defines part of the user variable. Example usage:
# set -g @session-saver-strategy-vim "session"
restore_process_strategy_option="@session-saver-strategy-"
# set -g @resurrect-strategy-vim "session"
restore_process_strategy_option="@resurrect-strategy-"
inline_strategy_token="->"
save_command_strategy_option="@resurrect-save-command-strategy"
default_save_command_strategy="ps"
bash_history_option="@resurrect-save-bash-history"

30
strategies/nvim_session.sh Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# "nvim session strategy"
#
# Same as vim strategy, see file 'vim_session.sh'
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
nvim_session_file_exists() {
[ -e "${DIRECTORY}/Session.vim" ]
}
original_command_contains_session_flag() {
[[ "$ORIGINAL_COMMAND" =~ "-S" ]]
}
main() {
if nvim_session_file_exists; then
echo "nvim -S"
elif original_command_contains_session_flag; then
# Session file does not exist, yet the original nvim command contains
# session flag `-S`. This will cause an error, so we're falling back to
# starting plain nvim.
echo "nvim"
else
echo "$ORIGINAL_COMMAND"
fi
}
main

View File

@ -4,7 +4,7 @@
#
# Restores a vim session from 'Session.vim' file, if it exists.
# If 'Session.vim' does not exist, it falls back to invoking the original
# command (withouth the `-S` flag).
# command (without the `-S` flag).
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
@ -21,7 +21,7 @@ main() {
if vim_session_file_exists; then
echo "vim -S"
elif original_command_contains_session_flag; then
# Session file does not exist, yet the orignal vim command contains
# Session file does not exist, yet the original vim command contains
# session flag `-S`. This will cause an error, so we're falling back to
# starting plain vim.
echo "vim"

BIN
video/issue_vid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
video/screencast_img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

110
video/script.md Normal file
View File

@ -0,0 +1,110 @@
# Screencast script
1. Intro
========
Let's demo tmux resurrect plugin.
Tmux resurrect enables persisting tmux sessions, so it can survive the dreaded
system restarts.
The benefit is uninterrupted workflow with no configuration required.
2. Working session
==================
Script
------
Let me show you what I have in this tmux demo session.
First of all, I have vim open and it has a couple files loaded.
Then there's a tmux window with a couple splits in various directories across
the system.
Next window contains tmux man page,
and then there's `htop` program.
And this is just one of many projects I'm currently running.
Actions
-------
- blank tmux window
- vim
- `ls` to show open files
- multiple pane windows (3)
- man tmux
- htop
- psql
- show a list of session
3. Saving the environment
=========================
Script
------
With vanilla tmux, when I restart the computer this whole environment will be
lost and I'll have to invest time to restore it.
tmux resurrect gives you the ability to persist everything with
prefix plus alt-s.
Now tmux environment is saved and I can safely shut down tmux with a
kill server command.
Actions
-------
- prefix + M-s
- :kill-server
4. Restoring the environment
============================
Script
------
At this point restoring everything back is easy.
I'll fire up tmux again. Notice it's completely empty.
Now, I'll press prefix plus alt-r and everything will restore.
Let's see how things look now.
First of all, I'm back to the exact same window I was in when the environment
was saved. Second - you can see the `htop` program was restored.
Going back there's tmux man page
a window with multiple panes with the exact same layout as before
and vim.
tmux resurrect takes special care of vim. By leveraging vim's sessions, it
preserves vim's split windows, open files, even the list of files edited before.
Check out the project readme for more details about special treatment for vim.
That was just one of the restored tmux sessions. If I open tmux session list you
can see all the other projects are restored as well.
When you see all these programs running you might be concerned that this plugin
started a lot of potentially destructive processes.
For example, when you restore tmux you don't want to accidentally start backups,
resource intensive or sensitive programs.
There's no need to be worried though. By default, this plugin starts only a
conservative list of programs like vim, less, tail, htop and similar.
This list of programs restored by default is in the project readme. Also, you
can easily add more programs to it.
If you feel paranoid, there's an option that prevents restoring any program.
Actions
-------
- tmux
- prefix + M-r
- open previous windows
- in vim hit :ls
- prefix + s for a list of panes
5. Outro
========
That's it for this demo. I hope you'll find tmux resurrect useful.