mirror of
https://github.com/tmux-plugins/tmux-resurrect.git
synced 2025-09-09 20:58:45 +00:00
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
9c63ea625b | |||
7258e29656 | |||
781b5104e4 | |||
f4f36a5af9 | |||
d4ec690610 | |||
d9ac38bef9 | |||
c5bc35932b | |||
a3dd99085a | |||
157bd5e38a | |||
890a413b9f | |||
9cabda7c71 | |||
66916085d9 | |||
30366a3cb4 | |||
ac86e025ad | |||
8b183f373f | |||
5c1105c579 | |||
3dd5441d42 | |||
703df121e2 | |||
2bc9bc0dd7 | |||
671699a054 | |||
b7e7669999 | |||
952e1f9784 | |||
abad85f03b | |||
e1b01ee4f9 | |||
708cd49d31 | |||
47db8198d3 | |||
c4375bf642 | |||
ddf9c5ef87 | |||
442b3ed244 | |||
b91e18cb0e | |||
869be6fa5a | |||
028e7b7c2e | |||
b502572a07 | |||
1c8efb034a | |||
95ec3c1d9b | |||
5dc22a4a9b | |||
bfc625e13f | |||
3244b809eb | |||
a9ac17a8f8 | |||
9eae48b87e | |||
e8401ba305 | |||
e770c87e10 | |||
601366be6d | |||
831fc5a458 | |||
059686ab6c | |||
488f086fa5 | |||
fcf7ca13f0 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
run_tests
|
||||
tests/run_tests_in_isolation
|
||||
tests/helpers/helpers.sh
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "lib/tmux-test"]
|
||||
path = lib/tmux-test
|
||||
url = https://github.com/tmux-plugins/tmux-test.git
|
14
.travis.yml
Normal file
14
.travis.yml
Normal file
@ -0,0 +1,14 @@
|
||||
# generic packages and latest Tmux 1.9a
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y git-core expect
|
||||
- sudo apt-get install -y python-software-properties software-properties-common
|
||||
- sudo add-apt-repository -y ppa:pi-rho/dev
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y tmux=1.9a-1~ppa1~p
|
||||
|
||||
install:
|
||||
- git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules
|
||||
- lib/tmux-test/setup
|
||||
|
||||
script: ./tests/run_tests_in_isolation
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -2,6 +2,41 @@
|
||||
|
||||
### master
|
||||
|
||||
### v2.4.0, 2015-02-23
|
||||
- add "tmux-test"
|
||||
- add test for "resurrect save" feature
|
||||
- add test for "resurrect restore" feature
|
||||
- make the tests work and pass on travis
|
||||
- add travis badge to the readme
|
||||
|
||||
### v2.3.0, 2015-02-12
|
||||
- Improve fetching proper window_layout for zoomed windows. In order to fetch
|
||||
proper value, window has to get unzoomed. This is now done faster so that
|
||||
"unzoom,fetch value,zoom" cycle is almost unnoticable to the user.
|
||||
|
||||
### v2.2.0, 2015-02-12
|
||||
- bugfix: zoomed windows related regression
|
||||
- export save and restore script paths so that 'tmux-resurrect-save' plugin can
|
||||
use them
|
||||
- enable "quiet" saving (used by 'tmux-resurrect-save' plugin)
|
||||
|
||||
### v2.1.0, 2015-02-12
|
||||
- if restore is started when there's only **1 pane in the whole tmux server**,
|
||||
assume the users wants the "full restore" and overrwrite that pane.
|
||||
|
||||
### 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.
|
||||
|
51
README.md
51
README.md
@ -1,5 +1,7 @@
|
||||
# Tmux Resurrect
|
||||
|
||||
[](https://travis-ci.org/tmux-plugins/tmux-resurrect)
|
||||
|
||||
Restore `tmux` environment after a system restart.
|
||||
|
||||
Tmux is great, except when you have to restart the computer. You lose all the
|
||||
@ -12,7 +14,10 @@ projects.
|
||||
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 even (optionally) [restores vim sessions](#restoring-vim-sessions)!
|
||||
It even (optionally) [restores vim and neovim sessions](#restoring-vim-and-neovim-sessions)!
|
||||
|
||||
Automatic restoring and continuous saving of tmux env is also possible with
|
||||
[tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) plugin.
|
||||
|
||||
### Screencast
|
||||
|
||||
@ -23,8 +28,6 @@ It even (optionally) [restores vim sessions](#restoring-vim-sessions)!
|
||||
- `prefix + Ctrl-s` - save
|
||||
- `prefix + Ctrl-r` - restore
|
||||
|
||||
`prefix + Alt-s` and `prefix + Alt-r` key bindings are now deprecated.
|
||||
|
||||
For custom key bindings, add to `.tmux.conf`:
|
||||
|
||||
set -g @resurrect-save 'S'
|
||||
@ -42,17 +45,21 @@ This plugin goes to great lengths to save and restore all the details from your
|
||||
- active and alternative window for each session
|
||||
- windows with focus
|
||||
- active pane for each window
|
||||
- "grouped sessions" (useful feature when using tmux with multiple monitors)
|
||||
- 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).
|
||||
- restoring bash history (optional, *experimental*). More details in
|
||||
- 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, `bash`.
|
||||
|
||||
`tmux-resurrect` is idempotent! It will not try to restore panes or windows that
|
||||
already exist.
|
||||
already exist.<br/>
|
||||
The single exception to this is when tmux is started with only 1 pane in order
|
||||
to restore previous tmux env. In this case only will this single pane be
|
||||
overwritten.
|
||||
|
||||
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
|
||||
|
||||
@ -88,7 +95,7 @@ You should now be able to use the plugin.
|
||||
Configuration is not required, but it enables extra features.
|
||||
|
||||
Only a conservative list of programs is restored by default:<br/>
|
||||
`vi vim emacs man less more tail top htop irssi`.
|
||||
`vi vim nvim emacs man less more tail top htop irssi`.
|
||||
|
||||
- Restore additional programs with the setting in `.tmux.conf`:
|
||||
|
||||
@ -115,15 +122,20 @@ Only a conservative list of programs is restored by default:<br/>
|
||||
|
||||
set -g @resurrect-processes ':all:'
|
||||
|
||||
#### Restoring vim sessions
|
||||
#### Restoring vim and neovim sessions
|
||||
|
||||
- save vim sessions. I recommend [tpope/vim-obsession](https://github.com/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`:
|
||||
|
||||
# for vim
|
||||
set -g @resurrect-strategy-vim 'session'
|
||||
# for neovim
|
||||
set -g @resurrect-strategy-nvim 'session'
|
||||
|
||||
`tmux-resurrect` 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
|
||||
|
||||
@ -136,11 +148,13 @@ Change this with:
|
||||
|
||||
In `.tmux.conf`:
|
||||
|
||||
set -g @resurrect-save-bash-history 'on'
|
||||
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 Bash running in
|
||||
foreground (as opposed to e.g. vi or top) when saving.
|
||||
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
|
||||
|
||||
@ -150,6 +164,11 @@ foreground (as opposed to e.g. vi or top) when saving.
|
||||
highlighted text to system clipboard
|
||||
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
|
||||
opening highlighted file or a url
|
||||
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
|
||||
restoring and continuous saving of tmux env
|
||||
|
||||
You might want to follow [@brunosutic](https://twitter.com/brunosutic) on
|
||||
twitter if you want to hear about new tmux plugins or feature updates.
|
||||
|
||||
### Reporting bugs and contributing
|
||||
|
||||
@ -159,7 +178,7 @@ 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
|
||||
|
||||
|
1
lib/tmux-test
Submodule
1
lib/tmux-test
Submodule
Submodule lib/tmux-test added at 1778f06b20
@ -25,9 +25,15 @@ set_default_strategies() {
|
||||
tmux set-option -g "${restore_process_strategy_option}irb" "default_strategy"
|
||||
}
|
||||
|
||||
set_script_path_options() {
|
||||
tmux set-option -g "$save_path_option" "$CURRENT_DIR/scripts/save.sh"
|
||||
tmux set-option -g "$restore_path_option" "$CURRENT_DIR/scripts/restore.sh"
|
||||
}
|
||||
|
||||
main() {
|
||||
set_save_bindings
|
||||
set_restore_bindings
|
||||
set_default_strategies
|
||||
set_script_path_options
|
||||
}
|
||||
main
|
||||
|
@ -13,6 +13,7 @@ exit_safely_if_empty_ppid() {
|
||||
ps_command_flags() {
|
||||
case $(uname -s) in
|
||||
FreeBSD) echo "-ao" ;;
|
||||
OpenBSD) echo "-ao" ;;
|
||||
*) echo "-eo" ;;
|
||||
esac
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ resurrect_dir_option="@resurrect-dir"
|
||||
|
||||
SUPPORTED_VERSION="1.9"
|
||||
|
||||
d=$'\t'
|
||||
|
||||
# helper functions
|
||||
get_tmux_option() {
|
||||
local option="$1"
|
||||
@ -54,6 +56,16 @@ save_bash_history_option_on() {
|
||||
[ "$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
|
||||
|
||||
resurrect_dir() {
|
||||
@ -73,10 +85,3 @@ 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=$'\t' read session_name window_number; do
|
||||
tmux resize-pane -t "${session_name}:${window_number}" -Z
|
||||
done
|
||||
}
|
||||
|
@ -7,12 +7,17 @@ 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=""
|
||||
|
||||
RESTORING_FROM_SCRATCH="false"
|
||||
|
||||
is_line_type() {
|
||||
local line_type="$1"
|
||||
local line="$2"
|
||||
@ -53,6 +58,14 @@ is_pane_registered_as_existing() {
|
||||
[[ "$EXISTING_PANES_VAR" =~ "$pane_custom_id" ]]
|
||||
}
|
||||
|
||||
restore_from_scratch_true() {
|
||||
RESTORING_FROM_SCRATCH="true"
|
||||
}
|
||||
|
||||
is_restoring_from_scratch() {
|
||||
[ "$RESTORING_FROM_SCRATCH" == "true" ]
|
||||
}
|
||||
|
||||
window_exists() {
|
||||
local session_name="$1"
|
||||
local window_number="$2"
|
||||
@ -106,14 +119,22 @@ new_pane() {
|
||||
|
||||
restore_pane() {
|
||||
local pane="$1"
|
||||
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
|
||||
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"
|
||||
if is_restoring_from_scratch; then
|
||||
# overwrite the pane
|
||||
# happens only for the first pane if it's the only registered pane for the whole tmux server
|
||||
local pane_id="$(tmux display-message -p -F "#{pane_id}" -t "$session_name:$window_number")"
|
||||
new_pane "$session_name" "$window_number" "$window_name" "$dir"
|
||||
tmux kill-pane -t "$pane_id"
|
||||
else
|
||||
# Pane exists, no need to create it!
|
||||
# Pane existence is registered. Later, its process also won't be restored.
|
||||
register_existing_pane "$session_name" "$window_number" "$pane_index"
|
||||
fi
|
||||
elif window_exists "$session_name" "$window_number"; then
|
||||
new_pane "$session_name" "$window_number" "$window_name" "$dir"
|
||||
elif session_exists "$session_name"; then
|
||||
@ -127,13 +148,54 @@ restore_pane() {
|
||||
restore_state() {
|
||||
local state="$1"
|
||||
echo "$state" |
|
||||
while IFS=$'\t' read line_type client_session client_last_session; do
|
||||
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_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
|
||||
}
|
||||
|
||||
never_ever_overwrite() {
|
||||
local overwrite_option_value="$(get_tmux_option "$overwrite_option" "")"
|
||||
[ -n "$overwrite_option_value" ]
|
||||
}
|
||||
|
||||
detect_if_restoring_from_scratch() {
|
||||
if never_ever_overwrite; then
|
||||
return
|
||||
fi
|
||||
local total_number_of_panes="$(tmux list-panes -a | wc -l | sed 's/ //g')"
|
||||
if [ "$total_number_of_panes" -eq 1 ]; then
|
||||
restore_from_scratch_true
|
||||
fi
|
||||
}
|
||||
|
||||
# functions called from main (ordered)
|
||||
|
||||
restore_all_panes() {
|
||||
detect_if_restoring_from_scratch
|
||||
while read line; do
|
||||
if is_line_type "pane" "$line"; then
|
||||
restore_pane "$line"
|
||||
@ -141,9 +203,16 @@ restore_all_panes() {
|
||||
done < $(last_resurrect_file)
|
||||
}
|
||||
|
||||
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_shell_history() {
|
||||
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ { print $2, $3, $7, $10; }' $(last_resurrect_file) |
|
||||
while IFS=$'\t' read session_name window_number pane_index pane_command; do
|
||||
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"
|
||||
@ -160,7 +229,7 @@ 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=$'\t' read session_name window_number pane_index dir pane_full_command; do
|
||||
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"
|
||||
@ -168,25 +237,34 @@ restore_all_pane_processes() {
|
||||
fi
|
||||
}
|
||||
|
||||
restore_pane_layout_for_each_window() {
|
||||
\grep '^window' $(last_resurrect_file) |
|
||||
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/ && $9 == 1 { print $2, $3, $7; }' $(last_resurrect_file) |
|
||||
while IFS=$'\t' read session_name window_number active_pane; do
|
||||
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_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
|
||||
}
|
||||
|
||||
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=$'\t' read session_name active_window window_number; do
|
||||
while IFS=$d read session_name active_window window_number; do
|
||||
tmux switch-client -t "${session_name}:${window_number}"
|
||||
done
|
||||
}
|
||||
@ -211,6 +289,7 @@ main() {
|
||||
# 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
|
||||
|
133
scripts/save.sh
133
scripts/save.sh
@ -6,8 +6,26 @@ source "$CURRENT_DIR/variables.sh"
|
||||
source "$CURRENT_DIR/helpers.sh"
|
||||
source "$CURRENT_DIR/spinner_helpers.sh"
|
||||
|
||||
# delimiters
|
||||
d=$'\t'
|
||||
delimiter=$'\t'
|
||||
|
||||
# if "quiet" script produces no output
|
||||
SCRIPT_OUTPUT="$1"
|
||||
|
||||
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 delimiter=$'\t'
|
||||
local format
|
||||
format+="pane"
|
||||
format+="${delimiter}"
|
||||
@ -34,7 +52,6 @@ pane_format() {
|
||||
}
|
||||
|
||||
window_format() {
|
||||
local delimiter=$'\t'
|
||||
local format
|
||||
format+="window"
|
||||
format+="${delimiter}"
|
||||
@ -51,7 +68,6 @@ window_format() {
|
||||
}
|
||||
|
||||
state_format() {
|
||||
local delimiter=$'\t'
|
||||
local format
|
||||
format+="state"
|
||||
format+="${delimiter}"
|
||||
@ -65,6 +81,15 @@ dump_panes_raw() {
|
||||
tmux list-panes -a -F "$(pane_format)"
|
||||
}
|
||||
|
||||
dump_windows_raw(){
|
||||
tmux list-windows -a -F "$(window_format)"
|
||||
}
|
||||
|
||||
toggle_window_zoom() {
|
||||
local target="$1"
|
||||
tmux resize-pane -Z -t "$target"
|
||||
}
|
||||
|
||||
_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"
|
||||
@ -86,7 +111,8 @@ pane_full_command() {
|
||||
save_shell_history() {
|
||||
local pane_id="$1"
|
||||
local pane_command="$2"
|
||||
if [ "$pane_command" = "bash" ]; then
|
||||
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")'"
|
||||
@ -96,16 +122,55 @@ save_shell_history() {
|
||||
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
|
||||
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
|
||||
# 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}"
|
||||
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
|
||||
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}"
|
||||
@ -113,7 +178,23 @@ dump_panes() {
|
||||
}
|
||||
|
||||
dump_windows() {
|
||||
tmux list-windows -a -F "$(window_format)"
|
||||
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
|
||||
# window_layout is not correct for zoomed windows
|
||||
if [[ "$window_flags" == *Z* ]]; then
|
||||
# unmaximize the window
|
||||
toggle_window_zoom "${session_name}:${window_index}"
|
||||
# get correct window layout
|
||||
window_layout="$(tmux display-message -p -t "${session_name}:${window_index}" -F "#{window_layout}")"
|
||||
# maximize window again
|
||||
toggle_window_zoom "${session_name}:${window_index}"
|
||||
fi
|
||||
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_active}${d}${window_flags}${d}${window_layout}"
|
||||
done
|
||||
}
|
||||
|
||||
dump_state() {
|
||||
@ -121,31 +202,39 @@ dump_state() {
|
||||
}
|
||||
|
||||
dump_bash_history() {
|
||||
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
|
||||
save_shell_history "$session_name:$window_number.$pane_index" "$pane_command"
|
||||
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)"
|
||||
dump_panes > $resurrect_file_path
|
||||
dump_windows >> $resurrect_file_path
|
||||
dump_state >> $resurrect_file_path
|
||||
ln -fs "$resurrect_file_path" "$(last_resurrect_file)"
|
||||
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
|
||||
}
|
||||
|
||||
show_output() {
|
||||
[ "$SCRIPT_OUTPUT" != "quiet" ]
|
||||
}
|
||||
|
||||
main() {
|
||||
if supported_tmux_version_ok; then
|
||||
start_spinner "Saving..." "Tmux environment saved!"
|
||||
if show_output; then
|
||||
start_spinner "Saving..." "Tmux environment saved!"
|
||||
fi
|
||||
save_all
|
||||
stop_spinner
|
||||
display_message "Tmux environment saved!"
|
||||
if show_output; then
|
||||
stop_spinner
|
||||
display_message "Tmux environment saved!"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
main
|
||||
|
@ -1,13 +1,15 @@
|
||||
# key bindings
|
||||
default_save_key="M-s C-s"
|
||||
default_save_key="C-s"
|
||||
save_option="@resurrect-save"
|
||||
save_path_option="@resurrect-save-script-path"
|
||||
|
||||
default_restore_key="M-r C-r"
|
||||
default_restore_key="C-r"
|
||||
restore_option="@resurrect-restore"
|
||||
restore_path_option="@resurrect-restore-script-path"
|
||||
|
||||
# default processes that are restored
|
||||
default_proc_list_option="@resurrect-default-processes"
|
||||
default_proc_list='vi vim emacs man less more tail top htop irssi'
|
||||
default_proc_list='vi vim nvim emacs man less more tail top htop irssi'
|
||||
|
||||
# User defined processes that are restored
|
||||
# 'false' - nothing is restored
|
||||
@ -28,3 +30,6 @@ save_command_strategy_option="@resurrect-save-command-strategy"
|
||||
default_save_command_strategy="ps"
|
||||
|
||||
bash_history_option="@resurrect-save-bash-history"
|
||||
|
||||
# set to 'on' to ensure panes are never ever overwritten
|
||||
overwrite_option="@resurrect-never-overwrite"
|
||||
|
30
strategies/nvim_session.sh
Executable file
30
strategies/nvim_session.sh
Executable 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
|
21
tests/fixtures/restore_file.txt
vendored
Normal file
21
tests/fixtures/restore_file.txt
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
pane 0 0 :bash 1 :* 0 :/tmp 1 bash :
|
||||
pane blue 0 :vim 0 : 0 :/tmp 1 vim :vim foo.txt
|
||||
pane blue 1 :man 0 :- 0 :/tmp 0 bash :
|
||||
pane blue 1 :man 0 :- 1 :/usr/share/man 1 man :man echo
|
||||
pane blue 2 :bash 1 :* 0 :/tmp 1 bash :
|
||||
pane red 0 :bash 0 : 0 :/tmp 1 bash :
|
||||
pane red 1 :bash 0 :- 0 :/tmp 0 bash :
|
||||
pane red 1 :bash 0 :- 1 :/tmp 0 bash :
|
||||
pane red 1 :bash 0 :- 2 :/tmp 1 bash :
|
||||
pane red 2 :bash 1 :* 0 :/tmp 0 bash :
|
||||
pane red 2 :bash 1 :* 1 :/tmp 1 bash :
|
||||
pane yellow 0 :bash 1 :* 0 :/tmp/bar 1 bash :
|
||||
window 0 0 1 :* ce9e,200x49,0,0,1
|
||||
window blue 0 0 : ce9f,200x49,0,0,2
|
||||
window blue 1 0 :- 178b,200x49,0,0{100x49,0,0,3,99x49,101,0,4}
|
||||
window blue 2 1 :* cea2,200x49,0,0,5
|
||||
window red 0 0 : cea3,200x49,0,0,6
|
||||
window red 1 0 :- 135b,200x49,0,0[200x24,0,0,7,200x24,0,25{100x24,0,25,8,99x24,101,25,9}]
|
||||
window red 2 1 :* db81,200x49,0,0[200x24,0,0,10,200x24,0,25,11]
|
||||
window yellow 0 1 :* 6781,200x49,0,0,12
|
||||
state yellow blue
|
21
tests/fixtures/save_file.txt
vendored
Normal file
21
tests/fixtures/save_file.txt
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
pane 0 0 :bash 1 :* 0 :/tmp 1 bash :
|
||||
pane blue 0 :vim 0 : 0 :/tmp 1 vim :vim foo.txt
|
||||
pane blue 1 :man 0 :- 0 :/tmp 0 bash :
|
||||
pane blue 1 :man 0 :- 1 :/usr/share/man 1 man :man echo
|
||||
pane blue 2 :bash 1 :* 0 :/tmp 1 bash :
|
||||
pane red 0 :bash 0 : 0 :/tmp 1 bash :
|
||||
pane red 1 :bash 0 :- 0 :/tmp 0 bash :
|
||||
pane red 1 :bash 0 :- 1 :/tmp 0 bash :
|
||||
pane red 1 :bash 0 :- 2 :/tmp 1 bash :
|
||||
pane red 2 :bash 1 :* 0 :/tmp 0 bash :
|
||||
pane red 2 :bash 1 :* 1 :/tmp 1 bash :
|
||||
pane yellow 0 :bash 1 :* 0 :/tmp/bar 1 bash :
|
||||
window 0 0 1 :* ce9d,200x49,0,0,0
|
||||
window blue 0 0 : cea4,200x49,0,0,7
|
||||
window blue 1 0 :- 9797,200x49,0,0{100x49,0,0,8,99x49,101,0,9}
|
||||
window blue 2 1 :* 677f,200x49,0,0,10
|
||||
window red 0 0 : ce9e,200x49,0,0,1
|
||||
window red 1 0 :- 52b7,200x49,0,0[200x24,0,0,2,200x24,0,25{100x24,0,25,3,99x24,101,25,4}]
|
||||
window red 2 1 :* bd68,200x49,0,0[200x24,0,0,5,200x24,0,25,6]
|
||||
window yellow 0 1 :* 6780,200x49,0,0,11
|
||||
state yellow blue
|
41
tests/helpers/create_and_save_tmux_test_environment.exp
Executable file
41
tests/helpers/create_and_save_tmux_test_environment.exp
Executable file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env expect
|
||||
|
||||
source "./tests/helpers/expect_helpers.exp"
|
||||
|
||||
expect_setup
|
||||
|
||||
spawn tmux
|
||||
# delay with sleep to compensate for tmux starting time
|
||||
sleep 1
|
||||
|
||||
run_shell_command "cd /tmp"
|
||||
|
||||
# session red
|
||||
new_tmux_session "red"
|
||||
|
||||
new_tmux_window
|
||||
horizontal_split
|
||||
vertical_split
|
||||
|
||||
new_tmux_window
|
||||
horizontal_split
|
||||
|
||||
# session blue
|
||||
new_tmux_session "blue"
|
||||
|
||||
run_shell_command "touch foo.txt"
|
||||
run_shell_command "vim foo.txt"
|
||||
|
||||
new_tmux_window
|
||||
vertical_split
|
||||
run_shell_command "man echo"
|
||||
|
||||
new_tmux_window
|
||||
|
||||
# session yellow
|
||||
new_tmux_session "yellow"
|
||||
run_shell_command "cd /tmp/bar"
|
||||
|
||||
start_resurrect_save
|
||||
|
||||
run_shell_command "tmux kill-server"
|
65
tests/helpers/expect_helpers.exp
Normal file
65
tests/helpers/expect_helpers.exp
Normal file
@ -0,0 +1,65 @@
|
||||
# a set of expect helpers
|
||||
|
||||
# basic setup for each script
|
||||
proc expect_setup {} {
|
||||
# disables script output
|
||||
log_user 0
|
||||
# standard timeout
|
||||
set timeout 5
|
||||
}
|
||||
|
||||
proc new_tmux_window {} {
|
||||
send "c"
|
||||
send "cd /tmp\r"
|
||||
sleep 0.2
|
||||
}
|
||||
|
||||
proc rename_current_session {name} {
|
||||
send "$"
|
||||
# delete existing name with ctrl-u
|
||||
send ""
|
||||
send "$name\r"
|
||||
sleep 0.2
|
||||
}
|
||||
|
||||
proc new_tmux_session {name} {
|
||||
send "TMUX='' tmux new -d -s $name\r"
|
||||
sleep 1
|
||||
send "tmux switch-client -t $name\r"
|
||||
send "cd /tmp\r"
|
||||
sleep 0.5
|
||||
}
|
||||
|
||||
proc horizontal_split {} {
|
||||
send "\""
|
||||
sleep 0.2
|
||||
send "cd /tmp\r"
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
proc vertical_split {} {
|
||||
send "%"
|
||||
sleep 0.2
|
||||
send "cd /tmp\r"
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
proc run_shell_command {command} {
|
||||
send "$command\r"
|
||||
sleep 1
|
||||
}
|
||||
|
||||
proc start_resurrect_save {} {
|
||||
send ""
|
||||
sleep 5
|
||||
}
|
||||
|
||||
proc start_resurrect_restore {} {
|
||||
send ""
|
||||
sleep 10
|
||||
}
|
||||
|
||||
proc clear_screen_for_window {target} {
|
||||
send "tmux send-keys -t $target C-l\r"
|
||||
sleep 0.2
|
||||
}
|
1
tests/helpers/helpers.sh
Symbolic link
1
tests/helpers/helpers.sh
Symbolic link
@ -0,0 +1 @@
|
||||
../../lib/tmux-test/tests/helpers/helpers.sh
|
18
tests/helpers/restore_and_save_tmux_test_environment.exp
Executable file
18
tests/helpers/restore_and_save_tmux_test_environment.exp
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env expect
|
||||
|
||||
source "./tests/helpers/expect_helpers.exp"
|
||||
|
||||
expect_setup
|
||||
|
||||
spawn tmux
|
||||
# delay with sleep to compensate for tmux starting time
|
||||
sleep 1
|
||||
|
||||
start_resurrect_restore
|
||||
|
||||
# delete all existing resurrect save files
|
||||
run_shell_command "rm ~/.tmux/resurrect/*"
|
||||
|
||||
start_resurrect_save
|
||||
|
||||
run_shell_command "tmux kill-server"
|
11
tests/helpers/resurrect_helpers.sh
Normal file
11
tests/helpers/resurrect_helpers.sh
Normal file
@ -0,0 +1,11 @@
|
||||
# we want "fixed" dimensions no matter the size of real display
|
||||
set_screen_dimensions_helper() {
|
||||
stty cols 200
|
||||
stty rows 50
|
||||
}
|
||||
|
||||
last_save_file_differs_helper() {
|
||||
local original_file="$1"
|
||||
diff "$original_file" "${HOME}/.tmux/resurrect/last"
|
||||
[ $? -ne 0 ]
|
||||
}
|
1
tests/run_tests_in_isolation
Symbolic link
1
tests/run_tests_in_isolation
Symbolic link
@ -0,0 +1 @@
|
||||
../lib/tmux-test/tests/run_tests_in_isolation
|
33
tests/test_resurrect_restore.sh
Executable file
33
tests/test_resurrect_restore.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source $CURRENT_DIR/helpers/helpers.sh
|
||||
source $CURRENT_DIR/helpers/resurrect_helpers.sh
|
||||
|
||||
setup_before_restore() {
|
||||
# setup restore file
|
||||
mkdir -p ~/.tmux/resurrect/
|
||||
cp tests/fixtures/restore_file.txt "${HOME}/.tmux/resurrect/restore_file.txt"
|
||||
ln -sf restore_file.txt "${HOME}/.tmux/resurrect/last"
|
||||
|
||||
# directory used in restored tmux session
|
||||
mkdir -p /tmp/bar
|
||||
}
|
||||
|
||||
restore_tmux_environment_and_save_again() {
|
||||
set_screen_dimensions_helper
|
||||
$CURRENT_DIR/helpers/restore_and_save_tmux_test_environment.exp
|
||||
}
|
||||
|
||||
main() {
|
||||
install_tmux_plugin_under_test_helper
|
||||
setup_before_restore
|
||||
restore_tmux_environment_and_save_again
|
||||
|
||||
if last_save_file_differs_helper "tests/fixtures/restore_file.txt"; then
|
||||
fail_helper "Saved file not correct after restore"
|
||||
fi
|
||||
exit_helper
|
||||
}
|
||||
main
|
23
tests/test_resurrect_save.sh
Executable file
23
tests/test_resurrect_save.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source $CURRENT_DIR/helpers/helpers.sh
|
||||
source $CURRENT_DIR/helpers/resurrect_helpers.sh
|
||||
|
||||
create_tmux_test_environment_and_save() {
|
||||
set_screen_dimensions_helper
|
||||
$CURRENT_DIR/helpers/create_and_save_tmux_test_environment.exp
|
||||
}
|
||||
|
||||
main() {
|
||||
install_tmux_plugin_under_test_helper
|
||||
mkdir -p /tmp/bar # setup required dirs
|
||||
create_tmux_test_environment_and_save
|
||||
|
||||
if last_save_file_differs_helper "tests/fixtures/save_file.txt"; then
|
||||
fail_helper "Saved file not correct (initial save)"
|
||||
fi
|
||||
exit_helper
|
||||
}
|
||||
main
|
Reference in New Issue
Block a user