Compare commits
288 Commits
Author | SHA1 | Date | |
---|---|---|---|
cff343cf9e | |||
4c1c0dcf85 | |||
6df04051fe | |||
299c4aa8ce | |||
b8ff2ea08b | |||
4941cdb074 | |||
a2ddfb96b9 | |||
dd36a4561b | |||
88297b4c3a | |||
45aa8feef6 | |||
75458f91c8 | |||
1431ba6fbe | |||
3606e4f602 | |||
7f5fa4bed2 | |||
74d9112314 | |||
8101d98358 | |||
ca6468e2de | |||
6050d2d8d8 | |||
e87d7d592c | |||
5b5e6ca7b1 | |||
1ad109d3a8 | |||
027960ad25 | |||
dc6252d950 | |||
c3d0599a6e | |||
6be2f34b5f | |||
4234ba99aa | |||
003a7cddbb | |||
3e8fbdf7aa | |||
1b63a940a0 | |||
02a7f1f9d6 | |||
6c9322aa99 | |||
80adb917c1 | |||
716b958145 | |||
abbc4fbfc4 | |||
dff7b2d2ae | |||
1c0eae57c8 | |||
afcddf8b9e | |||
4a36ded50f | |||
76656c510b | |||
e4825055c9 | |||
b6cbf652c4 | |||
123d83c668 | |||
e815c50f13 | |||
5f5f9d8fd5 | |||
78d67e4dcb | |||
da1a755802 | |||
e4e21f3f48 | |||
bd671b83dc | |||
327c0481ad | |||
905abba3c3 | |||
2382467b8e | |||
17cf30a69c | |||
1160c1da71 | |||
c66b791fff | |||
0f0ec12e92 | |||
d7825683d1 | |||
e3f05dd34f | |||
8fcb4ab40b | |||
a876ad6648 | |||
ac8a446667 | |||
c3ffe89e52 | |||
e99f1977b6 | |||
913f693f80 | |||
15cabbb930 | |||
edd8132bef | |||
9187f8b377 | |||
b020b2481e | |||
e5cbe54c75 | |||
71b93407f2 | |||
9dc3f8c639 | |||
8b504d4b06 | |||
6feae65a83 | |||
8aa999c591 | |||
0133c7a96a | |||
42f77b303c | |||
2f5394bfed | |||
fee390d1b5 | |||
6d10ee01b6 | |||
8f3f8d7cb9 | |||
7c77c70483 | |||
195a16f35f | |||
365a2e78ae | |||
9c7ec631d4 | |||
5f3873c502 | |||
dcef21995a | |||
e3878caa75 | |||
4f7f4e00ae | |||
75ecae9bcb | |||
3a31bfbbb8 | |||
241f62089a | |||
fe8390a578 | |||
9d81e8d026 | |||
7e0bfcb7e9 | |||
aaec581334 | |||
ea3cbcedc6 | |||
bf07546645 | |||
2c9cc224c0 | |||
655ed8c6f4 | |||
e757e1b8a9 | |||
fbed36ad0e | |||
eef844c747 | |||
b7a4ee2a40 | |||
ba59a2b642 | |||
573897cd71 | |||
7a8d90a27d | |||
8de2570960 | |||
1a3a094265 | |||
40c776c747 | |||
b1f3df21e9 | |||
dec4e15fc5 | |||
14b2a97791 | |||
0a2de2ea44 | |||
69480dcd87 | |||
5722856e54 | |||
0a6f90788f | |||
71fd3c8dd2 | |||
b8cc90a7f4 | |||
c6a4d9dfd1 | |||
a2eb3b01d2 | |||
3bc852bbae | |||
370b4861cb | |||
6750a88e74 | |||
5f04ceabbc | |||
fbfb0993a1 | |||
7fc9757e9d | |||
3ae61a70f2 | |||
c0eeb04874 | |||
7fd2c979f0 | |||
550ef94ec4 | |||
66e95a243a | |||
53578fee8b | |||
06b8ad744a | |||
82837109d4 | |||
4d5c30c29a | |||
e81f8e5f37 | |||
a47236d247 | |||
a750628a44 | |||
74773bed62 | |||
aa0b424ca6 | |||
852af79075 | |||
6fe70853a5 | |||
cb6e31e12f | |||
8ca7c5374f | |||
ab91fe1cb5 | |||
ed6fb9b523 | |||
8af7aed9b3 | |||
f3c6321501 | |||
c85a3b4487 | |||
fee40e2121 | |||
00008e7f40 | |||
c32fb488e7 | |||
6a6a87626a | |||
c3d5a8c19c | |||
737568922b | |||
a1e3d37461 | |||
5d46aa27d0 | |||
4f685d5c3d | |||
b22b2a7203 | |||
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 | |||
e38eed7dae | |||
a35d5f9b90 | |||
0b496dd228 | |||
ad52ade4bf | |||
3ba092459a | |||
6a6d65b98a | |||
f3fe4acc39 | |||
8684d4592b | |||
8328de41d8 | |||
94985fc500 | |||
81982b5114 | |||
a73c465e47 | |||
4ba0e398b9 | |||
8fd38588c0 | |||
a7fe9dcac3 | |||
ec9f68cad5 | |||
94594efdb0 | |||
1b79eb2f63 | |||
8ebda79f68 | |||
ae9083e695 | |||
9f7050aaae | |||
99abfa5f13 | |||
95303946b2 | |||
1d09f07d2b | |||
618769b62f | |||
fac377bf8c | |||
dc7561df74 | |||
eb2cd31d4b | |||
e242ea6d8d | |||
a0a3f2fd56 | |||
d606106f1c | |||
d5598d1c61 | |||
db05b2133b | |||
8368355240 | |||
20c5fc40cc | |||
af3cb5db2e | |||
deb3e9fdce | |||
3682cf6170 | |||
6255154190 | |||
a6eb17f8fd | |||
27b9b41e21 | |||
4d5557d599 | |||
f5cfa2daa7 | |||
f2533ec0ef | |||
d0f6f6ca30 | |||
34a1b4647d | |||
a68a786a73 | |||
19c981545e | |||
571bcb8173 | |||
2b259cf11a | |||
f9ad59900a | |||
beb54b62fb | |||
bd095e739d | |||
e2e55c6faa | |||
cedd1292c1 | |||
05cf790493 | |||
f9ef86d604 | |||
9a6e4a1a2c | |||
bd13c9bae8 | |||
87b2d75794 | |||
54f47a4015 | |||
eca38175ff | |||
b8b87a6e29 | |||
8cbc18e130 | |||
cfe8e7979b | |||
093627ce0a | |||
0da279e4cd | |||
884a5e9c19 | |||
8cb5b21e2f | |||
ee1ab0c728 | |||
18f4d1099e | |||
655bdb9a75 | |||
3da5d61b5b | |||
5509256a02 | |||
cde50d4d92 | |||
07bba0fde7 | |||
1e945c2cac | |||
7f50660918 | |||
cbf58ac613 | |||
2edd3ff98d | |||
bcad7cd1ea | |||
4d5c0a2a0d | |||
8051fb9d36 |
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Force text files to have unix eols, so Windows/Cygwin does not break them
|
||||||
|
*.* eol=lf
|
||||||
|
|
||||||
|
# Except for images because then on checkout the files have been altered.
|
||||||
|
*.png binary
|
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
|
19
.travis.yml
Normal file
19
.travis.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# generic packages and tmux
|
||||||
|
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 apt-get install -y libevent-dev libncurses-dev
|
||||||
|
- git clone https://github.com/tmux/tmux.git
|
||||||
|
- cd tmux
|
||||||
|
- git checkout 2.5
|
||||||
|
- sh autogen.sh
|
||||||
|
- ./configure && make && sudo make install
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules
|
||||||
|
# manual `git clone` required for testing `tmux-test` plugin itself
|
||||||
|
- git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test; true
|
||||||
|
- lib/tmux-test/setup
|
||||||
|
|
||||||
|
script: ./tests/run_tests_in_isolation
|
143
CHANGELOG.md
143
CHANGELOG.md
@ -1,6 +1,149 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
### master
|
### master
|
||||||
|
- Remove deprecated "restoring shell history" feature.
|
||||||
|
|
||||||
|
### v4.0.0, 2022-04-10
|
||||||
|
- Proper handling of `automatic-rename` window option.
|
||||||
|
- save and restore tmux pane title (breaking change: you have to re-save to be
|
||||||
|
able to properly restore!)
|
||||||
|
|
||||||
|
### v3.0.0, 2021-08-30
|
||||||
|
- save and restore tmux pane contents (@laomaiweng)
|
||||||
|
- update tmux-test to solve issue with recursing git submodules in that project
|
||||||
|
- set options quietly in `resurrect.tmux` script
|
||||||
|
- improve pane contents restoration: `cat <file>` is no longer shown in pane
|
||||||
|
content history
|
||||||
|
- refactoring: drop dependency on `paste` command
|
||||||
|
- bugfix for pane contents restoration
|
||||||
|
- expand tilde char `~` if used with `@resurrect-dir`
|
||||||
|
- do not save empty trailing lines when pane content is saved
|
||||||
|
- do not save pane contents if pane is empty (only for 'save pane contents'
|
||||||
|
feature)
|
||||||
|
- "save pane contents" feature saves files to a separate directory
|
||||||
|
- archive and compress pane contents file
|
||||||
|
- make archive & compress pane contents process more portable
|
||||||
|
- `mutt` added to the list of automatically restored programs
|
||||||
|
- added guide for migrating from tmuxinator
|
||||||
|
- fixed a bug for restoring commands on tmux 2.5 (and probably tmux 2.4)
|
||||||
|
- do not create another resurrect file if there are no changes (credit @vburdo)
|
||||||
|
- allow using '$HOSTNAME' in @resurrect-dir
|
||||||
|
- add zsh history saving and restoring
|
||||||
|
- delete resurrect files older than 30 days, but keep at least 5 files
|
||||||
|
- add save and restore hooks
|
||||||
|
- always use `-ao` flags for `ps` command to detect commands
|
||||||
|
- Deprecate restoring shell history feature.
|
||||||
|
- `view` added to the list of automatically restored programs
|
||||||
|
- Enable vim session strategy to work with custom session files,
|
||||||
|
e.g. `vim -S Session1.vim`.
|
||||||
|
- Enable restoring command arguments for inline strategies with `*` character.
|
||||||
|
- Kill session "0" if it wasn't restored.
|
||||||
|
- Add `@resurrect-delete-backup-after` option to specify how many days of
|
||||||
|
backups to keep - default is 30.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
- 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
|
||||||
|
- enable restoring programs with arguments (e.g. "rails console") and also
|
||||||
|
processes that contain program name
|
||||||
|
- improve `irb` restore strategy
|
||||||
|
|
||||||
|
### v0.1.0, 2014-08-28
|
||||||
|
- refactor checking if saved tmux session exists
|
||||||
|
- spinner while tmux sessions are restored
|
||||||
|
|
||||||
|
### v0.0.5, 2014-08-28
|
||||||
|
- restore pane processes
|
||||||
|
- user option for disabling pane process restoring
|
||||||
|
- enable whitelisting processes that will be restored
|
||||||
|
- expand readme with configuration options
|
||||||
|
- enable command strategies; enable restoring vim sessions
|
||||||
|
- update readme: explain restoring vim sessions
|
||||||
|
|
||||||
|
### v0.0.4, 2014-08-26
|
||||||
|
- restore pane layout for each window
|
||||||
|
- bugfix: correct pane ordering in a window
|
||||||
|
|
||||||
### v0.0.3, 2014-08-26
|
### v0.0.3, 2014-08-26
|
||||||
- save and restore current and alternate session
|
- save and restore current and alternate session
|
||||||
|
12
CONTRIBUTING.md
Normal file
12
CONTRIBUTING.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
### Contributing
|
||||||
|
|
||||||
|
Code contributions are welcome!
|
||||||
|
|
||||||
|
### Reporting a bug
|
||||||
|
|
||||||
|
If you find a bug please report it in the issues. When reporting a bug please
|
||||||
|
attach:
|
||||||
|
- 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
|
119
README.md
119
README.md
@ -1,20 +1,71 @@
|
|||||||
# Tmux Session Saver
|
# Tmux Resurrect
|
||||||
|
|
||||||
Enables saving and restoring of tmux sessions.
|
[](https://travis-ci.org/tmux-plugins/tmux-resurrect)
|
||||||
|
|
||||||
|
Restore `tmux` environment after system restart.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
`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 even (optionally)
|
||||||
|
[restores vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)!
|
||||||
|
|
||||||
|
Automatic restoring and continuous saving of tmux env is also possible with
|
||||||
|
[tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) plugin.
|
||||||
|
|
||||||
|
### Screencast
|
||||||
|
|
||||||
|
[](https://vimeo.com/104763018)
|
||||||
|
|
||||||
### Key bindings
|
### Key bindings
|
||||||
|
|
||||||
- `prefix + M-s` - save
|
- `prefix + Ctrl-s` - save
|
||||||
- `prefix + M-r` - restore
|
- `prefix + Ctrl-r` - restore
|
||||||
|
|
||||||
|
### About
|
||||||
|
|
||||||
|
This plugin goes to great lengths to save and restore all the details from your
|
||||||
|
`tmux` environment. Here's what's been taken care of:
|
||||||
|
|
||||||
|
- all sessions, windows, panes and their order
|
||||||
|
- current working directory for each pane
|
||||||
|
- **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
|
||||||
|
- "grouped sessions" (useful feature when using tmux with multiple monitors)
|
||||||
|
- programs running within a pane! More details in the
|
||||||
|
[restoring programs doc](docs/restoring_programs.md).
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
|
||||||
|
- [restoring vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)
|
||||||
|
- [restoring pane contents](docs/restoring_pane_contents.md)
|
||||||
|
- [restoring a previously saved environment](docs/restoring_previously_saved_environment.md)
|
||||||
|
|
||||||
|
Requirements / dependencies: `tmux 1.9` or higher, `bash`.
|
||||||
|
|
||||||
|
Tested and working on Linux, OSX and Cygwin.
|
||||||
|
|
||||||
|
`tmux-resurrect` is idempotent! It will not try to restore panes or windows that
|
||||||
|
already exist.<br/>
|
||||||
|
The single exception to this is when tmux is started with only 1 pane in order
|
||||||
|
to restore previous tmux env. Only in this case will this single pane be
|
||||||
|
overwritten.
|
||||||
|
|
||||||
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
|
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
|
||||||
|
|
||||||
Add plugin to the list of TPM plugins in `.tmux.conf`:
|
Add plugin to the list of TPM plugins in `.tmux.conf`:
|
||||||
|
|
||||||
set -g @tpm_plugins " \
|
set -g @plugin 'tmux-plugins/tmux-resurrect'
|
||||||
tmux-plugins/tpm \
|
|
||||||
tmux-plugins/tmux-session-saver \
|
|
||||||
"
|
|
||||||
|
|
||||||
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
|
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
|
||||||
use the plugin.
|
use the plugin.
|
||||||
@ -23,18 +74,56 @@ use the plugin.
|
|||||||
|
|
||||||
Clone the repo:
|
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`:
|
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:
|
|
||||||
|
|
||||||
# type this in terminal
|
|
||||||
$ tmux source-file ~/.tmux.conf
|
|
||||||
|
|
||||||
|
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`.
|
||||||
You should now be able to use the plugin.
|
You should now be able to use the plugin.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- [Guide for migrating from tmuxinator](docs/migrating_from_tmuxinator.md)
|
||||||
|
|
||||||
|
**Configuration**
|
||||||
|
|
||||||
|
- [Changing the default key bindings](docs/custom_key_bindings.md).
|
||||||
|
- [Setting up hooks on save & restore](docs/hooks.md).
|
||||||
|
- Only a conservative list of programs is restored by default:<br/>
|
||||||
|
`vi vim nvim emacs man less more tail top htop irssi weechat mutt`.<br/>
|
||||||
|
[Restoring programs doc](docs/restoring_programs.md) explains how to restore
|
||||||
|
additional programs.
|
||||||
|
- [Change a directory](docs/save_dir.md) where `tmux-resurrect` saves tmux
|
||||||
|
environment.
|
||||||
|
|
||||||
|
**Optional features**
|
||||||
|
|
||||||
|
- [Restoring vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)
|
||||||
|
is nice if you're a vim/neovim user.
|
||||||
|
- [Restoring pane contents](docs/restoring_pane_contents.md) feature.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
|
||||||
|
restoring and continuous saving of tmux env
|
||||||
|
|
||||||
|
### Reporting bugs and contributing
|
||||||
|
|
||||||
|
Both contributing and bug reports are welcome. Please check out
|
||||||
|
[contributing guidelines](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### Credits
|
||||||
|
|
||||||
|
[Mislav Marohnić](https://github.com/mislav) - the idea for the plugin came from his
|
||||||
|
[tmux-session script](https://github.com/mislav/dotfiles/blob/2036b5e03fb430bbcbc340689d63328abaa28876/bin/tmux-session).
|
||||||
|
|
||||||
### License
|
### License
|
||||||
[MIT](LICENSE.md)
|
[MIT](LICENSE.md)
|
||||||
|
11
docs/custom_key_bindings.md
Normal file
11
docs/custom_key_bindings.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Custom key bindings
|
||||||
|
|
||||||
|
The default key bindings are:
|
||||||
|
|
||||||
|
- `prefix + Ctrl-s` - save
|
||||||
|
- `prefix + Ctrl-r` - restore
|
||||||
|
|
||||||
|
To change these, add to `.tmux.conf`:
|
||||||
|
|
||||||
|
set -g @resurrect-save 'S'
|
||||||
|
set -g @resurrect-restore 'R'
|
33
docs/hooks.md
Normal file
33
docs/hooks.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Save & Restore Hooks
|
||||||
|
|
||||||
|
Hooks allow to set custom commands that will be executed during session save
|
||||||
|
and restore. Most hooks are called with zero arguments, unless explicitly
|
||||||
|
stated otherwise.
|
||||||
|
|
||||||
|
Currently the following hooks are supported:
|
||||||
|
|
||||||
|
- `@resurrect-hook-post-save-layout`
|
||||||
|
|
||||||
|
Called after all sessions, panes and windows have been saved.
|
||||||
|
|
||||||
|
Passed single argument of the state file.
|
||||||
|
|
||||||
|
- `@resurrect-hook-post-save-all`
|
||||||
|
|
||||||
|
Called at end of save process right before the spinner is turned off.
|
||||||
|
|
||||||
|
- `@resurrect-hook-pre-restore-all`
|
||||||
|
|
||||||
|
Called before any tmux state is altered.
|
||||||
|
|
||||||
|
- `@resurrect-hook-pre-restore-pane-processes`
|
||||||
|
|
||||||
|
Called before running processes are restored.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Here is an example how to save and restore window geometry for most terminals in X11.
|
||||||
|
Add this to `.tmux.conf`:
|
||||||
|
|
||||||
|
set -g @resurrect-hook-post-save-all 'eval $(xdotool getwindowgeometry --shell $WINDOWID); echo 0,$X,$Y,$WIDTH,$HEIGHT > $HOME/.tmux/resurrect/geometry'
|
||||||
|
set -g @resurrect-hook-pre-restore-all 'wmctrl -i -r $WINDOWID -e $(cat $HOME/.tmux/resurrect/geometry)'
|
72
docs/migrating_from_tmuxinator.md
Normal file
72
docs/migrating_from_tmuxinator.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# Migrating from `tmuxinator`
|
||||||
|
|
||||||
|
### Why migrate to `tmux-resurrect`?
|
||||||
|
|
||||||
|
Here are some reasons why you'd want to migrate from `tmuxinator` to
|
||||||
|
`tmux-resurrect`:
|
||||||
|
|
||||||
|
- **Less dependencies**<br/>
|
||||||
|
`tmuxinator` depends on `ruby` which can be a hassle to set up if you're not a
|
||||||
|
rubyist.<br/>
|
||||||
|
`tmux-resurrect` depends just on `bash` which is virtually
|
||||||
|
omnipresent.
|
||||||
|
|
||||||
|
- **Simplicity**<br/>
|
||||||
|
`tmuxinator` has an executable, CLI interface with half dozen commands and
|
||||||
|
command completion.<br/>
|
||||||
|
`tmux-resurrect` defines just 2 tmux key bindings.
|
||||||
|
|
||||||
|
- **No configuration**<br/>
|
||||||
|
`tmuxinator` is all about config files (and their constant updating).<br/>
|
||||||
|
`tmux-resurrect` requires no configuration to work.
|
||||||
|
|
||||||
|
- **Better change handling**<br/>
|
||||||
|
When you make a change to any aspect of tmux layout, you also have to
|
||||||
|
update related `tmuxinator` project file (and test to make sure change is
|
||||||
|
ok).<br/>
|
||||||
|
With `tmux-resurrect` there's nothing to do: your change will be
|
||||||
|
remembered on the next save.
|
||||||
|
|
||||||
|
### How to migrate?
|
||||||
|
|
||||||
|
1. Install `tmux-resurrect`.
|
||||||
|
2. Open \*all* existing `tmuxinator` projects.<br/>
|
||||||
|
Verify all projects are open by pressing `prefix + s` and checking they are
|
||||||
|
all on the list.
|
||||||
|
3. Perform a `tmux-resurrect` save.
|
||||||
|
|
||||||
|
That's it! You can continue using just `tmux-resurrect` should you choose so.
|
||||||
|
|
||||||
|
Note: it probably makes no sense to use both tools at the same time as they do
|
||||||
|
the same thing (creating tmux environment for you to work in).
|
||||||
|
Technically however, there should be no issues.
|
||||||
|
|
||||||
|
### Usage differences
|
||||||
|
|
||||||
|
`tmuxinator` focuses on managing individual tmux sessions (projects).
|
||||||
|
`tmux-resurrect` keeps track of the \*whole* tmux environment: all sessions are
|
||||||
|
saved and restored together.
|
||||||
|
|
||||||
|
A couple tips if you decide to switch to `tmux-resurrect`:
|
||||||
|
|
||||||
|
- Keep all tmux sessions (projects) running all the time.<br/>
|
||||||
|
If you want to work on an existing project, you should be able to just
|
||||||
|
\*switch* to an already open session using `prefix + s`.<br/>
|
||||||
|
This is different from `tmuxinator` where you'd usually run `mux new [project]`
|
||||||
|
in order to start working on something.
|
||||||
|
|
||||||
|
- No need to kill sessions with `tmux kill-session` (unless you really don't
|
||||||
|
want to work on it ever).<br/>
|
||||||
|
It's the recurring theme by now: just keep all the sessions running all the
|
||||||
|
time. This is convenient and also cheap in terms of resources.
|
||||||
|
|
||||||
|
- The only 2 situations when you need `tmux-resurrect`:<br/>
|
||||||
|
1) Save tmux environment just before restarting/shutting down your
|
||||||
|
computer.<br/>
|
||||||
|
2) Restore tmux env after you turn the computer on.
|
||||||
|
|
||||||
|
### Other questions?
|
||||||
|
|
||||||
|
Still have questions? Feel free to open an
|
||||||
|
[issue](ihttps://github.com/tmux-plugins/tmux-resurrect/issues). We'll try to
|
||||||
|
answer it and also update this doc.
|
39
docs/restoring_bash_history.md
Normal file
39
docs/restoring_bash_history.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
tmux-ressurect no longer restores shell history for each pane, as of [this PR](https://github.com/tmux-plugins/tmux-resurrect/pull/308).
|
||||||
|
|
||||||
|
As a workaround, you can use the `HISTFILE` environment variable to preserve history for each pane separately, and modify
|
||||||
|
`PROMPT_COMMAND` to make sure history gets saved with each new command.
|
||||||
|
|
||||||
|
Unfortunately, we haven't found a perfect way of getting a unique identifier for each pane, as the `TMUX_PANE` variable
|
||||||
|
seems to occasionally change when resurrecting. As a workaround, the example below sets a unique ID in each pane's `title`.
|
||||||
|
The downside of this implementation is that pane titles must all be unique across sessions/windows, and also must use the `pane_id_prefix`.
|
||||||
|
|
||||||
|
Any improvements/suggestions for getting a unique, persistent ID for each pane are welcome!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pane_id_prefix="resurrect_"
|
||||||
|
|
||||||
|
# Create history directory if it doesn't exist
|
||||||
|
HISTS_DIR=$HOME/.bash_history.d
|
||||||
|
mkdir -p "${HISTS_DIR}"
|
||||||
|
|
||||||
|
if [ -n "${TMUX_PANE}" ]; then
|
||||||
|
|
||||||
|
# Check if we've already set this pane title
|
||||||
|
pane_id=$(tmux display -pt "${TMUX_PANE:?}" "#{pane_title}")
|
||||||
|
if [[ $pane_id != "$pane_id_prefix"* ]]; then
|
||||||
|
|
||||||
|
# if not, set it to a random ID
|
||||||
|
random_id=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
|
||||||
|
printf "\033]2;$pane_id_prefix$random_id\033\\"
|
||||||
|
pane_id=$(tmux display -pt "${TMUX_PANE:?}" "#{pane_title}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use the pane's random ID for the HISTFILE
|
||||||
|
export HISTFILE="${HISTS_DIR}/bash_history_tmux_${pane_id}"
|
||||||
|
else
|
||||||
|
export HISTFILE="${HISTS_DIR}/bash_history_no_tmux"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stash the new history each time a command runs.
|
||||||
|
export PROMPT_COMMAND="$PROMPT_COMMAND;history -a"
|
||||||
|
```
|
31
docs/restoring_pane_contents.md
Normal file
31
docs/restoring_pane_contents.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Restoring pane contents
|
||||||
|
|
||||||
|
This plugin enables saving and restoring tmux pane contents.
|
||||||
|
|
||||||
|
This feature can be enabled by adding this line to `.tmux.conf`:
|
||||||
|
|
||||||
|
set -g @resurrect-capture-pane-contents 'on'
|
||||||
|
|
||||||
|
##### Known issue
|
||||||
|
|
||||||
|
When using this feature, please check the value of `default-command`
|
||||||
|
tmux option. That can be done with `$ tmux show -g default-command`.
|
||||||
|
|
||||||
|
The value should NOT contain `&&` or `||` operators. If it does, simplify the
|
||||||
|
option so those operators are removed.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- this will cause issues (notice the `&&` and `||` operators):
|
||||||
|
|
||||||
|
set -g default-command "which reattach-to-user-namespace > /dev/null && reattach-to-user-namespace -l $SHELL || $SHELL -l"
|
||||||
|
|
||||||
|
- this is ok:
|
||||||
|
|
||||||
|
set -g default-command "reattach-to-user-namespace -l $SHELL"
|
||||||
|
|
||||||
|
Related [bug](https://github.com/tmux-plugins/tmux-resurrect/issues/98).
|
||||||
|
|
||||||
|
Alternatively, you can let
|
||||||
|
[tmux-sensible](https://github.com/tmux-plugins/tmux-sensible)
|
||||||
|
handle this option in a cross-platform way and you'll have no problems.
|
14
docs/restoring_previously_saved_environment.md
Normal file
14
docs/restoring_previously_saved_environment.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Restoring previously saved environment
|
||||||
|
|
||||||
|
None of the previous saves are deleted (unless you explicitly do that). All save
|
||||||
|
files are kept in `~/.tmux/resurrect/` directory, or `~/.local/share/tmux/resurrect`
|
||||||
|
(unless `${XDG_DATA_HOME}` says otherwise).<br/>
|
||||||
|
Here are the steps to restore to a previous point in time:
|
||||||
|
|
||||||
|
- make sure you start this with a "fresh" tmux instance
|
||||||
|
- `$ cd ~/.tmux/resurrect/`
|
||||||
|
- locate the save file you'd like to use for restore (file names have a timestamp)
|
||||||
|
- symlink the `last` file to the desired save file: `$ ln -sf <file_name> last`
|
||||||
|
- do a restore with `tmux-resurrect` key: `prefix + Ctrl-r`
|
||||||
|
|
||||||
|
You should now be restored to the time when `<file_name>` save happened.
|
205
docs/restoring_programs.md
Normal file
205
docs/restoring_programs.md
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
# Restoring programs
|
||||||
|
- [General instructions](#general-instructions)
|
||||||
|
- [Clarifications](#clarifications)
|
||||||
|
- [Working with NodeJS](#nodejs)
|
||||||
|
- [Restoring Mosh](#mosh)
|
||||||
|
|
||||||
|
### General instructions <a name="general-instructions"></a>
|
||||||
|
Only a conservative list of programs is restored by default:<br/>
|
||||||
|
`vi vim nvim emacs man less more tail top htop irssi weechat mutt`.
|
||||||
|
|
||||||
|
This can be configured with `@resurrect-processes` option in `.tmux.conf`. It
|
||||||
|
contains space-separated list of additional programs to restore.
|
||||||
|
|
||||||
|
- Example restoring additional programs:
|
||||||
|
|
||||||
|
set -g @resurrect-processes 'ssh psql mysql sqlite3'
|
||||||
|
|
||||||
|
- Programs with arguments should be double quoted:
|
||||||
|
|
||||||
|
set -g @resurrect-processes 'some_program "git log"'
|
||||||
|
|
||||||
|
- Start with tilde to restore a program whose process contains target name:
|
||||||
|
|
||||||
|
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"'
|
||||||
|
|
||||||
|
- Use `*` to expand the arguments from the saved command when restoring:
|
||||||
|
|
||||||
|
set -g @resurrect-processes 'some_program "~rails server->rails server *"'
|
||||||
|
|
||||||
|
- Don't restore any programs:
|
||||||
|
|
||||||
|
set -g @resurrect-processes 'false'
|
||||||
|
|
||||||
|
- Restore **all** programs (dangerous!):
|
||||||
|
|
||||||
|
set -g @resurrect-processes ':all:'
|
||||||
|
|
||||||
|
Be *very careful* with this: tmux-resurrect can not know which programs take
|
||||||
|
which context, and a `sudo mkfs.vfat /dev/sdb` that was just formatting an
|
||||||
|
external USB stick could wipe your backup hard disk if that's what's attached
|
||||||
|
after rebooting.
|
||||||
|
|
||||||
|
This option is primarily useful for experimentation (e.g., to find out which
|
||||||
|
program is recognized in a pane).
|
||||||
|
|
||||||
|
### Clarifications <a name="clarfications"></a>
|
||||||
|
|
||||||
|
> I don't understand tilde `~`, what is it and why is it used when restoring
|
||||||
|
programs?
|
||||||
|
|
||||||
|
Let's say you use `rails server` command often. You want `tmux-resurrect` to
|
||||||
|
save and restore it automatically. You might try adding `rails server` to the
|
||||||
|
list of programs that will be restored:
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"rails server"' # will NOT work
|
||||||
|
|
||||||
|
Upon save, `rails server` command will actually be saved as this command:
|
||||||
|
`/Users/user/.rbenv/versions/2.0.0-p481/bin/ruby script/rails server`
|
||||||
|
(if you wanna see how is any command saved, check it yourself in
|
||||||
|
`~/.tmux/resurrect/last` file).
|
||||||
|
|
||||||
|
When programs are restored, the `rails server` command will NOT be restored
|
||||||
|
because it does not **strictly** match the long
|
||||||
|
`/Users/user/.rbenv/versions/2.0.0-p481/bin/ruby script/rails server` string.
|
||||||
|
|
||||||
|
The tilde `~` at the start of the string relaxes process name matching.
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~rails server"' # OK
|
||||||
|
|
||||||
|
The above option says: "restore full process if `rails server` string is found
|
||||||
|
ANYWHERE in the process name".
|
||||||
|
|
||||||
|
If you check long process string, there is in fact a `rails server` string at
|
||||||
|
the end, so now the process will be successfully restored.
|
||||||
|
|
||||||
|
> What is arrow `->` and why is is used?
|
||||||
|
|
||||||
|
(Please read the above clarification about tilde `~`).
|
||||||
|
|
||||||
|
Continuing with our `rails server` example, when the process is finally restored
|
||||||
|
correctly it might not look pretty as you'll see the whole
|
||||||
|
`/Users/user/.rbenv/versions/2.0.0-p481/bin/ruby script/rails server` string in
|
||||||
|
the command line.
|
||||||
|
|
||||||
|
Naturally, you'd rather want to see just `rails server` (what you initially
|
||||||
|
typed), but that information is now unfortunately lost.
|
||||||
|
|
||||||
|
To aid this, you can use arrow `->`: (**note**: there is no space before and after `->`)
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~rails server->rails server"' # OK
|
||||||
|
|
||||||
|
This option says: "when this process is restored use `rails server` as the
|
||||||
|
command name".
|
||||||
|
|
||||||
|
Full (long) process name is now ignored and you'll see just `rails server` in
|
||||||
|
the command line when the program is restored.
|
||||||
|
|
||||||
|
> What is asterisk `*` and why is it used?
|
||||||
|
|
||||||
|
(Please read the above clarifications about tilde `~` and arrow `->`).
|
||||||
|
|
||||||
|
Continuing with the `rails server` example, you might have added flags for e.g.
|
||||||
|
verbose logging, but with the above configuration, the flags would be lost.
|
||||||
|
|
||||||
|
To preserve the command arguments when restoring, use the asterisk `*`: (**note**: there **must** be a space before `*`)
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~rails server->rails server *"'
|
||||||
|
|
||||||
|
This option says: "when this process is restored use `rails server` as the
|
||||||
|
command name, but preserve its arguments".
|
||||||
|
|
||||||
|
> Now I understand the tilde and the arrow, but things still don't work for me
|
||||||
|
|
||||||
|
Here's the general workflow for figuring this out:
|
||||||
|
|
||||||
|
- Set up your whole tmux environment manually.<br/>
|
||||||
|
In our example case, we'd type `rails server` in a pane where we want it to
|
||||||
|
run.
|
||||||
|
- Save tmux env (it will get saved to `~/.tmux/resurrect/last`).
|
||||||
|
- Open `~/.tmux/resurrect/last` file and try to find full process string for
|
||||||
|
your program.<br/>
|
||||||
|
Unfortunately this is a little vague but it should be easy. A smart
|
||||||
|
thing to do for our example is to search for string `rails` in the `last`
|
||||||
|
file.
|
||||||
|
- Now that you know the full and the desired process string use tilde `~` and
|
||||||
|
arrow `->` in `.tmux.conf` to make things work.
|
||||||
|
|
||||||
|
### Working with NodeJS <a name="nodejs"></a>
|
||||||
|
If you are working with NodeJS, you may get some troubles with configuring restoring programs.
|
||||||
|
|
||||||
|
Particularly, some programs like `gulp`, `grunt` or `npm` are not saved with parameters so tmux-resurrect cannot restore it. This is actually **not tmux-resurrect's issue** but more likely, those programs' issues. For example if you run `gulp watch` or `npm start` and then try to look at `ps` or `pgrep`, you will only see `gulp` or `npm`.
|
||||||
|
|
||||||
|
To deal with these issues, one solution is to use [yarn](https://yarnpkg.com/en/docs/install) which a package manager for NodeJS and an alternative for `npm`. It's nearly identical to `npm` and very easy to use. Therefore you don't have to do any migration, you can simply use it immediately. For example:
|
||||||
|
- `npm test` is equivalent to `yarn test`,
|
||||||
|
- `npm run watch:dev` is equivalent to `yarn watch:dev`
|
||||||
|
- more interestingly, `gulp watch:dev` is equivalent to `yarn gulp watch:dev`
|
||||||
|
|
||||||
|
Before continuing, please ensure that you understand the [clarifications](#clarifications) section about `~` and `->`
|
||||||
|
|
||||||
|
#### yarn
|
||||||
|
It's fairly straight forward if you have been using `yarn` already.
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~yarn watch"'
|
||||||
|
set -g @resurrect-processes '"~yarn watch->yarn watch"'
|
||||||
|
|
||||||
|
|
||||||
|
#### npm
|
||||||
|
Instead of
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~npm run watch"' # will NOT work
|
||||||
|
|
||||||
|
we use
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~yarn watch"' # OK
|
||||||
|
|
||||||
|
|
||||||
|
#### gulp
|
||||||
|
Instead of
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~gulp test"' # will NOT work
|
||||||
|
|
||||||
|
we use
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~yarn gulp test"' # OK
|
||||||
|
|
||||||
|
|
||||||
|
#### nvm
|
||||||
|
If you use `nvm` in your project, here is how you could config tmux-resurrect:
|
||||||
|
|
||||||
|
set -g @resurrect-processes '"~yarn gulp test->nvm use && gulp test"'
|
||||||
|
|
||||||
|
#### Another problem
|
||||||
|
Let take a look at this example
|
||||||
|
|
||||||
|
set -g @resurrect-processes '\
|
||||||
|
"~yarn gulp test->gulp test" \
|
||||||
|
"~yarn gulp test-it->gulp test-it" \
|
||||||
|
'
|
||||||
|
**This will not work properly**, only `gulp test` is run, although you can see the command `node /path/to/yarn gulp test-it` is added correctly in `.tmux/resurrect/last` file.
|
||||||
|
|
||||||
|
The reason is when restoring program, the **command part after the dash `-` is ignored** so instead of command `gulp test-it`, the command `gulp test` which will be run.
|
||||||
|
|
||||||
|
A work around, for this problem until it's fixed, is:
|
||||||
|
- the config should be like this:
|
||||||
|
|
||||||
|
set -g @resurrect-processes '\
|
||||||
|
"~yarn gulp test->gulp test" \
|
||||||
|
"~yarn gulp \"test-it\"->gulp test-it" \
|
||||||
|
|
||||||
|
- and in `.tmux/resurrect/last`, we should add quote to `test-it` word
|
||||||
|
|
||||||
|
... node:node /path/to/yarn gulp "test-it"
|
||||||
|
|
||||||
|
|
||||||
|
### Restoring Mosh <a name="#mosh"></a>
|
||||||
|
Mosh spawns a `mosh-client` process, so we need to specify that as the process to be resurrected.
|
||||||
|
|
||||||
|
set -g @resurrect-processes 'mosh-client'
|
||||||
|
|
||||||
|
Additionally a mosh-client strategy is provided to handle extracting the original arguments and re-run Mosh.
|
19
docs/restoring_vim_and_neovim_sessions.md
Normal file
19
docs/restoring_vim_and_neovim_sessions.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Restoring vim and neovim sessions
|
||||||
|
|
||||||
|
- 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 and neovim sessions if `Session.vim` file
|
||||||
|
is present.
|
||||||
|
|
||||||
|
> If you're using the vim binary provided by MacVim.app then you'll need to set `@resurrect-processes`, for example:
|
||||||
|
> ```
|
||||||
|
> set -g @resurrect-processes '~Vim -> vim'
|
||||||
|
> ```
|
15
docs/save_dir.md
Normal file
15
docs/save_dir.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# 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'
|
||||||
|
|
||||||
|
Using environment variables or shell interpolation in this option is not
|
||||||
|
allowed as the string is used literally. So the following won't do what is
|
||||||
|
expected:
|
||||||
|
|
||||||
|
set -g @resurrect-dir '/path/$MY_VAR/$(some_executable)'
|
||||||
|
|
||||||
|
Only the following variables and special chars are allowed:
|
||||||
|
`$HOME`, `$HOSTNAME`, and `~`.
|
1
lib/tmux-test
Submodule
1
lib/tmux-test
Submodule
Submodule lib/tmux-test added at c38f488f15
40
resurrect.tmux
Executable file
40
resurrect.tmux
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
source "$CURRENT_DIR/scripts/variables.sh"
|
||||||
|
source "$CURRENT_DIR/scripts/helpers.sh"
|
||||||
|
|
||||||
|
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/save.sh"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
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/restore.sh"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
set_default_strategies() {
|
||||||
|
tmux set-option -gq "${restore_process_strategy_option}irb" "default_strategy"
|
||||||
|
tmux set-option -gq "${restore_process_strategy_option}mosh-client" "default_strategy"
|
||||||
|
}
|
||||||
|
|
||||||
|
set_script_path_options() {
|
||||||
|
tmux set-option -gq "$save_path_option" "$CURRENT_DIR/scripts/save.sh"
|
||||||
|
tmux set-option -gq "$restore_path_option" "$CURRENT_DIR/scripts/restore.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
set_save_bindings
|
||||||
|
set_restore_bindings
|
||||||
|
set_default_strategies
|
||||||
|
set_script_path_options
|
||||||
|
}
|
||||||
|
main
|
22
save_command_strategies/gdb.sh
Executable file
22
save_command_strategies/gdb.sh
Executable 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
|
26
save_command_strategies/linux_procfs.sh
Executable file
26
save_command_strategies/linux_procfs.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
PANE_PID="$1"
|
||||||
|
COMMAND_PID=$(pgrep -P $PANE_PID)
|
||||||
|
|
||||||
|
exit_safely_if_empty_ppid() {
|
||||||
|
if [ -z "$PANE_PID" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
full_command() {
|
||||||
|
[[ -z "$COMMAND_PID" ]] && exit 0
|
||||||
|
# See: https://unix.stackexchange.com/a/567021
|
||||||
|
# Avoid complications with system printf by using bash subshell interpolation.
|
||||||
|
# This will properly escape sequences and null in cmdline.
|
||||||
|
cat /proc/${COMMAND_PID}/cmdline | xargs -0 bash -c 'printf "%q " "$0" "$@"'
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
exit_safely_if_empty_ppid
|
||||||
|
full_command
|
||||||
|
}
|
||||||
|
main
|
22
save_command_strategies/pgrep.sh
Executable file
22
save_command_strategies/pgrep.sh
Executable 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
|
24
save_command_strategies/ps.sh
Executable file
24
save_command_strategies/ps.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/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() {
|
||||||
|
ps -ao "ppid,args" |
|
||||||
|
sed "s/^ *//" |
|
||||||
|
grep "^${PANE_PID}" |
|
||||||
|
cut -d' ' -f2-
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
exit_safely_if_empty_ppid
|
||||||
|
full_command
|
||||||
|
}
|
||||||
|
main
|
@ -1,8 +1,17 @@
|
|||||||
# configurable constants
|
if [ -d "$HOME/.tmux/resurrect" ]; then
|
||||||
default_sessions_dir="$HOME/.tmux/sessions"
|
default_resurrect_dir="$HOME/.tmux/resurrect"
|
||||||
sessions_dir_option="@sessions-dir"
|
else
|
||||||
|
default_resurrect_dir="${XDG_DATA_HOME:-$HOME/.local/share}"/tmux/resurrect
|
||||||
|
fi
|
||||||
|
resurrect_dir_option="@resurrect-dir"
|
||||||
|
|
||||||
SUPPORTED_VERSION="1.9"
|
SUPPORTED_VERSION="1.9"
|
||||||
|
RESURRECT_FILE_PREFIX="tmux_resurrect"
|
||||||
|
RESURRECT_FILE_EXTENSION="txt"
|
||||||
|
_RESURRECT_DIR=""
|
||||||
|
_RESURRECT_FILE_PATH=""
|
||||||
|
|
||||||
|
d=$'\t'
|
||||||
|
|
||||||
# helper functions
|
# helper functions
|
||||||
get_tmux_option() {
|
get_tmux_option() {
|
||||||
@ -46,17 +55,105 @@ supported_tmux_version_ok() {
|
|||||||
$CURRENT_DIR/check_tmux_version.sh "$SUPPORTED_VERSION"
|
$CURRENT_DIR/check_tmux_version.sh "$SUPPORTED_VERSION"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remove_first_char() {
|
||||||
|
echo "$1" | cut -c2-
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_pane_contents_option_on() {
|
||||||
|
local option="$(get_tmux_option "$pane_contents_option" "off")"
|
||||||
|
[ "$option" == "on" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
files_differ() {
|
||||||
|
! cmp -s "$1" "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
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}"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# pane content file helpers
|
||||||
|
|
||||||
|
pane_contents_create_archive() {
|
||||||
|
tar cf - -C "$(resurrect_dir)/save/" ./pane_contents/ |
|
||||||
|
gzip > "$(pane_contents_archive_file)"
|
||||||
|
}
|
||||||
|
|
||||||
|
pane_content_files_restore_from_archive() {
|
||||||
|
local archive_file="$(pane_contents_archive_file)"
|
||||||
|
if [ -f "$archive_file" ]; then
|
||||||
|
mkdir -p "$(pane_contents_dir "restore")"
|
||||||
|
gzip -d < "$archive_file" |
|
||||||
|
tar xf - -C "$(resurrect_dir)/restore/"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# path helpers
|
# path helpers
|
||||||
|
|
||||||
sessions_dir() {
|
resurrect_dir() {
|
||||||
echo $(get_tmux_option "$sessions_dir_option" "$default_sessions_dir")
|
if [ -z "$_RESURRECT_DIR" ]; then
|
||||||
|
local path="$(get_tmux_option "$resurrect_dir_option" "$default_resurrect_dir")"
|
||||||
|
# expands tilde, $HOME and $HOSTNAME if used in @resurrect-dir
|
||||||
|
echo "$path" | sed "s,\$HOME,$HOME,g; s,\$HOSTNAME,$(hostname),g; s,\~,$HOME,g"
|
||||||
|
else
|
||||||
|
echo "$_RESURRECT_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
_RESURRECT_DIR="$(resurrect_dir)"
|
||||||
|
|
||||||
|
resurrect_file_path() {
|
||||||
|
if [ -z "$_RESURRECT_FILE_PATH" ]; then
|
||||||
|
local timestamp="$(date +"%Y%m%dT%H%M%S")"
|
||||||
|
echo "$(resurrect_dir)/${RESURRECT_FILE_PREFIX}_${timestamp}.${RESURRECT_FILE_EXTENSION}"
|
||||||
|
else
|
||||||
|
echo "$_RESURRECT_FILE_PATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
_RESURRECT_FILE_PATH="$(resurrect_file_path)"
|
||||||
|
|
||||||
|
last_resurrect_file() {
|
||||||
|
echo "$(resurrect_dir)/last"
|
||||||
}
|
}
|
||||||
|
|
||||||
session_path() {
|
pane_contents_dir() {
|
||||||
local timestamp="$(date +"%Y-%m-%dT%H:%M:%S")"
|
echo "$(resurrect_dir)/$1/pane_contents/"
|
||||||
echo "$(sessions_dir)/tmux_session_${timestamp}.txt"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
last_session_path() {
|
pane_contents_file() {
|
||||||
echo "$(sessions_dir)/last"
|
local save_or_restore="$1"
|
||||||
|
local pane_id="$2"
|
||||||
|
echo "$(pane_contents_dir "$save_or_restore")/pane-${pane_id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
pane_contents_file_exists() {
|
||||||
|
local pane_id="$1"
|
||||||
|
[ -f "$(pane_contents_file "restore" "$pane_id")" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
pane_contents_archive_file() {
|
||||||
|
echo "$(resurrect_dir)/pane_contents.tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_hook() {
|
||||||
|
local kind="$1"
|
||||||
|
shift
|
||||||
|
local args="" hook=""
|
||||||
|
|
||||||
|
hook=$(get_tmux_option "$hook_prefix$kind" "")
|
||||||
|
|
||||||
|
# If there are any args, pass them to the hook (in a way that preserves/copes
|
||||||
|
# with spaces and unusual characters.
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
printf -v args "%q " "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$hook" ]; then
|
||||||
|
eval "$hook $args"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
198
scripts/process_restore_helpers.sh
Normal file
198
scripts/process_restore_helpers.sh
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
restore_pane_processes_enabled() {
|
||||||
|
local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
|
||||||
|
if [ "$restore_processes" == "false" ]; then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_pane_process() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
local session_name="$2"
|
||||||
|
local window_number="$3"
|
||||||
|
local pane_index="$4"
|
||||||
|
local dir="$5"
|
||||||
|
local command
|
||||||
|
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"
|
||||||
|
|
||||||
|
local inline_strategy="$(_get_inline_strategy "$pane_full_command")" # might not be defined
|
||||||
|
if [ -n "$inline_strategy" ]; then
|
||||||
|
# inline strategy exists
|
||||||
|
# check for additional "expansion" of inline strategy, e.g. `vim` to `vim -S`
|
||||||
|
if _strategy_exists "$inline_strategy"; then
|
||||||
|
local strategy_file="$(_get_strategy_file "$inline_strategy")"
|
||||||
|
local inline_strategy="$($strategy_file "$pane_full_command" "$dir")"
|
||||||
|
fi
|
||||||
|
command="$inline_strategy"
|
||||||
|
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")"
|
||||||
|
command="$strategy_command"
|
||||||
|
else
|
||||||
|
# just invoke the raw command
|
||||||
|
command="$pane_full_command"
|
||||||
|
fi
|
||||||
|
tmux send-keys -t "${session_name}:${window_number}.${pane_index}" "$command" "C-m"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# private functions below
|
||||||
|
|
||||||
|
_process_should_be_restored() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
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
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_restore_all_processes() {
|
||||||
|
local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
|
||||||
|
if [ "$restore_processes" == ":all:" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_process_on_the_restore_list() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
# TODO: make this work without eval
|
||||||
|
eval set $(_restore_list)
|
||||||
|
local proc
|
||||||
|
local match
|
||||||
|
for proc in "$@"; do
|
||||||
|
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}//"
|
||||||
|
}
|
||||||
|
|
||||||
|
# given full command: 'ruby /Users/john/bin/my_program arg1 arg2'
|
||||||
|
# and inline strategy: '~bin/my_program->my_program *'
|
||||||
|
# returns: 'arg1 arg2'
|
||||||
|
_get_command_arguments() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
local match="$2"
|
||||||
|
if _proc_starts_with_tildae "$match"; then
|
||||||
|
match="$(remove_first_char "$match")"
|
||||||
|
fi
|
||||||
|
echo "$pane_full_command" | sed "s,^.*${match}[^ ]* *,,"
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_proc_restore_command() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
local proc="$2"
|
||||||
|
local match="$3"
|
||||||
|
local restore_element="$(_get_proc_restore_element "$proc")"
|
||||||
|
if [[ "$restore_element" =~ " ${inline_strategy_arguments_token}" ]]; then
|
||||||
|
# replaces "%" with command arguments
|
||||||
|
local command_arguments="$(_get_command_arguments "$pane_full_command" "$match")"
|
||||||
|
echo "$restore_element" | sed "s,${inline_strategy_arguments_token},${command_arguments},"
|
||||||
|
else
|
||||||
|
echo "$restore_element"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_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
|
||||||
|
# user didn't define any processes
|
||||||
|
echo "$default_processes"
|
||||||
|
else
|
||||||
|
echo "$default_processes $user_processes"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_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_command "$pane_full_command" "$proc" "$match")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_strategy_exists() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
local strategy="$(_get_command_strategy "$pane_full_command")"
|
||||||
|
if [ -n "$strategy" ]; then # strategy set?
|
||||||
|
local strategy_file="$(_get_strategy_file "$pane_full_command")"
|
||||||
|
[ -e "$strategy_file" ] # strategy file exists?
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_command_strategy() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
local command="$(_just_command "$pane_full_command")"
|
||||||
|
get_tmux_option "${restore_process_strategy_option}${command}" ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_just_command() {
|
||||||
|
echo "$1" | cut -d' ' -f1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_strategy_file() {
|
||||||
|
local pane_full_command="$1"
|
||||||
|
local strategy="$(_get_command_strategy "$pane_full_command")"
|
||||||
|
local command="$(_just_command "$pane_full_command")"
|
||||||
|
echo "$CURRENT_DIR/../strategies/${command}_${strategy}.sh"
|
||||||
|
}
|
14
scripts/restore.exp
Executable file
14
scripts/restore.exp
Executable 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
|
387
scripts/restore.sh
Executable file
387
scripts/restore.sh
Executable file
@ -0,0 +1,387 @@
|
|||||||
|
#!/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=""
|
||||||
|
|
||||||
|
RESTORING_FROM_SCRATCH="false"
|
||||||
|
|
||||||
|
RESTORE_PANE_CONTENTS="false"
|
||||||
|
|
||||||
|
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" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_from_scratch_true() {
|
||||||
|
RESTORING_FROM_SCRATCH="true"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_restoring_from_scratch() {
|
||||||
|
[ "$RESTORING_FROM_SCRATCH" == "true" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_pane_contents_true() {
|
||||||
|
RESTORE_PANE_CONTENTS="true"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_restoring_pane_contents() {
|
||||||
|
[ "$RESTORE_PANE_CONTENTS" == "true" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
restored_session_0_true() {
|
||||||
|
RESTORED_SESSION_0="true"
|
||||||
|
}
|
||||||
|
|
||||||
|
has_restored_session_0() {
|
||||||
|
[ "$RESTORED_SESSION_0" == "true" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tmux option stored in a global variable so that we don't have to "ask"
|
||||||
|
# tmux server each time.
|
||||||
|
cache_tmux_default_command() {
|
||||||
|
local default_shell="$(get_tmux_option "default-shell" "")"
|
||||||
|
local opt=""
|
||||||
|
if [ "$(basename "$default_shell")" == "bash" ]; then
|
||||||
|
opt="-l "
|
||||||
|
fi
|
||||||
|
export TMUX_DEFAULT_COMMAND="$(get_tmux_option "default-command" "$opt$default_shell")"
|
||||||
|
}
|
||||||
|
|
||||||
|
tmux_default_command() {
|
||||||
|
echo "$TMUX_DEFAULT_COMMAND"
|
||||||
|
}
|
||||||
|
|
||||||
|
pane_creation_command() {
|
||||||
|
echo "cat '$(pane_contents_file "restore" "${1}:${2}.${3}")'; exec $(tmux_default_command)"
|
||||||
|
}
|
||||||
|
|
||||||
|
new_window() {
|
||||||
|
local session_name="$1"
|
||||||
|
local window_number="$2"
|
||||||
|
local dir="$3"
|
||||||
|
local pane_index="$4"
|
||||||
|
local pane_id="${session_name}:${window_number}.${pane_index}"
|
||||||
|
dir="${dir/#\~/$HOME}"
|
||||||
|
if is_restoring_pane_contents && pane_contents_file_exists "$pane_id"; then
|
||||||
|
local pane_creation_command="$(pane_creation_command "$session_name" "$window_number" "$pane_index")"
|
||||||
|
tmux new-window -d -t "${session_name}:${window_number}" -c "$dir" "$pane_creation_command"
|
||||||
|
else
|
||||||
|
tmux new-window -d -t "${session_name}:${window_number}" -c "$dir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
new_session() {
|
||||||
|
local session_name="$1"
|
||||||
|
local window_number="$2"
|
||||||
|
local dir="$3"
|
||||||
|
local pane_index="$4"
|
||||||
|
local pane_id="${session_name}:${window_number}.${pane_index}"
|
||||||
|
if is_restoring_pane_contents && pane_contents_file_exists "$pane_id"; then
|
||||||
|
local pane_creation_command="$(pane_creation_command "$session_name" "$window_number" "$pane_index")"
|
||||||
|
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name" -c "$dir" "$pane_creation_command"
|
||||||
|
else
|
||||||
|
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name" -c "$dir"
|
||||||
|
fi
|
||||||
|
# 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 dir="$3"
|
||||||
|
local pane_index="$4"
|
||||||
|
local pane_id="${session_name}:${window_number}.${pane_index}"
|
||||||
|
if is_restoring_pane_contents && pane_contents_file_exists "$pane_id"; then
|
||||||
|
local pane_creation_command="$(pane_creation_command "$session_name" "$window_number" "$pane_index")"
|
||||||
|
tmux split-window -t "${session_name}:${window_number}" -c "$dir" "$pane_creation_command"
|
||||||
|
else
|
||||||
|
tmux split-window -t "${session_name}:${window_number}" -c "$dir"
|
||||||
|
fi
|
||||||
|
# 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_active window_flags pane_index pane_title dir pane_active pane_command pane_full_command; do
|
||||||
|
dir="$(remove_first_char "$dir")"
|
||||||
|
pane_full_command="$(remove_first_char "$pane_full_command")"
|
||||||
|
if [ "$session_name" == "0" ]; then
|
||||||
|
restored_session_0_true
|
||||||
|
fi
|
||||||
|
if pane_exists "$session_name" "$window_number" "$pane_index"; then
|
||||||
|
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" "$dir" "$pane_index"
|
||||||
|
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" "$dir" "$pane_index"
|
||||||
|
elif session_exists "$session_name"; then
|
||||||
|
new_window "$session_name" "$window_number" "$dir" "$pane_index"
|
||||||
|
else
|
||||||
|
new_session "$session_name" "$window_number" "$dir" "$pane_index"
|
||||||
|
fi
|
||||||
|
# set pane title
|
||||||
|
tmux select-pane -t "$session_name:$window_number.$pane_index" -T "$pane_title"
|
||||||
|
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_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
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_if_restoring_pane_contents() {
|
||||||
|
if capture_pane_contents_option_on; then
|
||||||
|
cache_tmux_default_command
|
||||||
|
restore_pane_contents_true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# functions called from main (ordered)
|
||||||
|
|
||||||
|
restore_all_panes() {
|
||||||
|
detect_if_restoring_from_scratch # sets a global variable
|
||||||
|
detect_if_restoring_pane_contents # sets a global variable
|
||||||
|
if is_restoring_pane_contents; then
|
||||||
|
pane_content_files_restore_from_archive
|
||||||
|
fi
|
||||||
|
while read line; do
|
||||||
|
if is_line_type "pane" "$line"; then
|
||||||
|
restore_pane "$line"
|
||||||
|
fi
|
||||||
|
done < $(last_resurrect_file)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_session_0() {
|
||||||
|
if is_restoring_from_scratch && ! has_restored_session_0; then
|
||||||
|
local current_session="$(tmux display -p "#{client_session}")"
|
||||||
|
if [ "$current_session" == "0" ]; then
|
||||||
|
tmux switch-client -n
|
||||||
|
fi
|
||||||
|
tmux kill-session -t "0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_window_properties() {
|
||||||
|
local window_name
|
||||||
|
\grep '^window' $(last_resurrect_file) |
|
||||||
|
while IFS=$d read line_type session_name window_number window_name window_active window_flags window_layout automatic_rename; do
|
||||||
|
tmux select-layout -t "${session_name}:${window_number}" "$window_layout"
|
||||||
|
|
||||||
|
# Below steps are properly handling window names and automatic-rename
|
||||||
|
# option. `rename-window` is an extra command in some scenarios, but we
|
||||||
|
# opted for always doing it to keep the code simple.
|
||||||
|
window_name="$(remove_first_char "$window_name")"
|
||||||
|
tmux rename-window -t "${session_name}:${window_number}" "$window_name"
|
||||||
|
if [ "${automatic_rename}" = ":" ]; then
|
||||||
|
tmux set-option -u -t "${session_name}:${window_number}" automatic-rename
|
||||||
|
else
|
||||||
|
tmux set-option -t "${session_name}:${window_number}" automatic-rename "$automatic_rename"
|
||||||
|
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, $6, $8, $11; }' $(last_resurrect_file) |
|
||||||
|
while IFS=$d read -r 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_active_pane_for_each_window() {
|
||||||
|
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $9 == 1 { print $2, $3, $6; }' $(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_zoomed_windows() {
|
||||||
|
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $5 ~ /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/ && $6 ~ /[*-]/ { print $2, $5, $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)
|
||||||
|
}
|
||||||
|
|
||||||
|
# A cleanup that happens after 'restore_all_panes' seems to fix fish shell
|
||||||
|
# users' restore problems.
|
||||||
|
cleanup_restored_pane_contents() {
|
||||||
|
if is_restoring_pane_contents; then
|
||||||
|
rm "$(pane_contents_dir "restore")"/*
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if supported_tmux_version_ok && check_saved_session_exists; then
|
||||||
|
start_spinner "Restoring..." "Tmux restore complete!"
|
||||||
|
execute_hook "pre-restore-all"
|
||||||
|
restore_all_panes
|
||||||
|
handle_session_0
|
||||||
|
restore_window_properties >/dev/null 2>&1
|
||||||
|
execute_hook "pre-restore-pane-processes"
|
||||||
|
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
|
||||||
|
cleanup_restored_pane_contents
|
||||||
|
execute_hook "post-restore-all"
|
||||||
|
stop_spinner
|
||||||
|
display_message "Tmux restore complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
main
|
278
scripts/save.sh
Executable file
278
scripts/save.sh
Executable file
@ -0,0 +1,278 @@
|
|||||||
|
#!/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'
|
||||||
|
|
||||||
|
# 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 format
|
||||||
|
format+="pane"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{session_name}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{window_index}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{window_active}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+=":#{window_flags}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{pane_index}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{pane_title}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+=":#{pane_current_path}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{pane_active}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{pane_current_command}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{pane_pid}"
|
||||||
|
format+="${delimiter}"
|
||||||
|
format+="#{history_size}"
|
||||||
|
echo "$format"
|
||||||
|
}
|
||||||
|
|
||||||
|
window_format() {
|
||||||
|
local format
|
||||||
|
format+="window"
|
||||||
|
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+="#{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)"
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
number_nonempty_lines_on_screen() {
|
||||||
|
local pane_id="$1"
|
||||||
|
tmux capture-pane -pJ -t "$pane_id" |
|
||||||
|
sed '/^$/d' |
|
||||||
|
wc -l |
|
||||||
|
sed 's/ //g'
|
||||||
|
}
|
||||||
|
|
||||||
|
# tests if there was any command output in the current pane
|
||||||
|
pane_has_any_content() {
|
||||||
|
local pane_id="$1"
|
||||||
|
local history_size="$(tmux display -p -t "$pane_id" -F "#{history_size}")"
|
||||||
|
local cursor_y="$(tmux display -p -t "$pane_id" -F "#{cursor_y}")"
|
||||||
|
# doing "cheap" tests first
|
||||||
|
[ "$history_size" -gt 0 ] || # history has any content?
|
||||||
|
[ "$cursor_y" -gt 0 ] || # cursor not in first line?
|
||||||
|
[ "$(number_nonempty_lines_on_screen "$pane_id")" -gt 1 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_pane_contents() {
|
||||||
|
local pane_id="$1"
|
||||||
|
local start_line="-$2"
|
||||||
|
local pane_contents_area="$3"
|
||||||
|
if pane_has_any_content "$pane_id"; then
|
||||||
|
if [ "$pane_contents_area" = "visible" ]; then
|
||||||
|
start_line="0"
|
||||||
|
fi
|
||||||
|
# the printf hack below removes *trailing* empty lines
|
||||||
|
printf '%s\n' "$(tmux capture-pane -epJ -S "$start_line" -t "$pane_id")" > "$(pane_contents_file "save" "$pane_id")"
|
||||||
|
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_active window_flags pane_index pane_title dir pane_active pane_command pane_pid history_size; do
|
||||||
|
# not saving panes from grouped sessions
|
||||||
|
if is_session_grouped "$session_name"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
full_command="$(pane_full_command $pane_pid)"
|
||||||
|
dir=$(echo $dir | sed 's/ /\\ /') # escape all spaces in directory path
|
||||||
|
echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${pane_title}${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_name window_active window_flags window_layout; do
|
||||||
|
# not saving windows from grouped sessions
|
||||||
|
if is_session_grouped "$session_name"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
automatic_rename="$(tmux show-window-options -vt "${session_name}:${window_index}" automatic-rename)"
|
||||||
|
# If the option was unset, use ":" as a placeholder.
|
||||||
|
[ -z "${automatic_rename}" ] && automatic_rename=":"
|
||||||
|
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${window_layout}${d}${automatic_rename}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_state() {
|
||||||
|
tmux display-message -p "$(state_format)"
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_pane_contents() {
|
||||||
|
local pane_contents_area="$(get_tmux_option "$pane_contents_area_option" "$default_pane_contents_area")"
|
||||||
|
dump_panes_raw |
|
||||||
|
while IFS=$d read line_type session_name window_number window_active window_flags pane_index pane_title dir pane_active pane_command pane_pid history_size; do
|
||||||
|
capture_pane_contents "${session_name}:${window_number}.${pane_index}" "$history_size" "$pane_contents_area"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_old_backups() {
|
||||||
|
# remove resurrect files older than 30 days (default), but keep at least 5 copies of backup.
|
||||||
|
local delete_after="$(get_tmux_option "$delete_backup_after_option" "$default_delete_backup_after")"
|
||||||
|
local -a files
|
||||||
|
files=($(ls -t $(resurrect_dir)/${RESURRECT_FILE_PREFIX}_*.${RESURRECT_FILE_EXTENSION} | tail -n +6))
|
||||||
|
[[ ${#files[@]} -eq 0 ]] ||
|
||||||
|
find "${files[@]}" -type f -mtime "+${delete_after}" -exec rm -v "{}" \; > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
save_all() {
|
||||||
|
local resurrect_file_path="$(resurrect_file_path)"
|
||||||
|
local last_resurrect_file="$(last_resurrect_file)"
|
||||||
|
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"
|
||||||
|
execute_hook "post-save-layout" "$resurrect_file_path"
|
||||||
|
if files_differ "$resurrect_file_path" "$last_resurrect_file"; then
|
||||||
|
ln -fs "$(basename "$resurrect_file_path")" "$last_resurrect_file"
|
||||||
|
else
|
||||||
|
rm "$resurrect_file_path"
|
||||||
|
fi
|
||||||
|
if capture_pane_contents_option_on; then
|
||||||
|
mkdir -p "$(pane_contents_dir "save")"
|
||||||
|
dump_pane_contents
|
||||||
|
pane_contents_create_archive
|
||||||
|
rm "$(pane_contents_dir "save")"/*
|
||||||
|
fi
|
||||||
|
remove_old_backups
|
||||||
|
execute_hook "post-save-all"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_output() {
|
||||||
|
[ "$SCRIPT_OUTPUT" != "quiet" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if supported_tmux_version_ok; then
|
||||||
|
if show_output; then
|
||||||
|
start_spinner "Saving..." "Tmux environment saved!"
|
||||||
|
fi
|
||||||
|
save_all
|
||||||
|
if show_output; then
|
||||||
|
stop_spinner
|
||||||
|
display_message "Tmux environment saved!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
main
|
@ -1,141 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
source "$CURRENT_DIR/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 session not found!"
|
|
||||||
exit
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_first_char() {
|
|
||||||
echo "$1" | cut -c2-
|
|
||||||
}
|
|
||||||
|
|
||||||
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 -d -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; do
|
|
||||||
window_name="$(remove_first_char $window_name)"
|
|
||||||
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_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" } /^pane/ && $6 ~ /[*-]/ { print $2, $5, $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; then
|
|
||||||
check_saved_session_exists
|
|
||||||
restore_all_sessions
|
|
||||||
restore_active_pane_for_each_window
|
|
||||||
restore_active_and_alternate_windows
|
|
||||||
restore_active_and_alternate_sessions
|
|
||||||
display_message "Restored all Tmux sessions!"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
main
|
|
@ -1,63 +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}"
|
|
||||||
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() {
|
|
||||||
tmux list-panes -a -F "$(pane_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_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
|
|
8
scripts/spinner_helpers.sh
Normal file
8
scripts/spinner_helpers.sh
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
start_spinner() {
|
||||||
|
$CURRENT_DIR/tmux_spinner.sh "$1" "$2" &
|
||||||
|
export SPINNER_PID=$!
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_spinner() {
|
||||||
|
kill $SPINNER_PID
|
||||||
|
}
|
29
scripts/tmux_spinner.sh
Executable file
29
scripts/tmux_spinner.sh
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# This script shows tmux spinner with a message. It is intended to be running
|
||||||
|
# as a background process which should be `kill`ed at the end.
|
||||||
|
#
|
||||||
|
# Example usage:
|
||||||
|
#
|
||||||
|
# ./tmux_spinner.sh "Working..." "End message!" &
|
||||||
|
# SPINNER_PID=$!
|
||||||
|
# ..
|
||||||
|
# .. execute commands here
|
||||||
|
# ..
|
||||||
|
# kill $SPINNER_PID # Stops spinner and displays 'End message!'
|
||||||
|
|
||||||
|
MESSAGE="$1"
|
||||||
|
END_MESSAGE="$2"
|
||||||
|
SPIN='-\|/'
|
||||||
|
|
||||||
|
trap "tmux display-message '$END_MESSAGE'; exit" SIGINT SIGTERM
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local i=0
|
||||||
|
while true; do
|
||||||
|
i=$(( (i+1) %4 ))
|
||||||
|
tmux display-message " ${SPIN:$i:1} $MESSAGE"
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
main
|
48
scripts/variables.sh
Normal file
48
scripts/variables.sh
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# key bindings
|
||||||
|
default_save_key="C-s"
|
||||||
|
save_option="@resurrect-save"
|
||||||
|
save_path_option="@resurrect-save-script-path"
|
||||||
|
|
||||||
|
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 view nvim emacs man less more tail top htop irssi weechat mutt'
|
||||||
|
|
||||||
|
# User defined processes that are restored
|
||||||
|
# 'false' - nothing is restored
|
||||||
|
# ':all:' - all processes are restored
|
||||||
|
#
|
||||||
|
# user defined list of programs that are restored:
|
||||||
|
# 'my_program foo another_program'
|
||||||
|
restore_processes_option="@resurrect-processes"
|
||||||
|
restore_processes=""
|
||||||
|
|
||||||
|
# Defines part of the user variable. Example usage:
|
||||||
|
# set -g @resurrect-strategy-vim "session"
|
||||||
|
restore_process_strategy_option="@resurrect-strategy-"
|
||||||
|
|
||||||
|
inline_strategy_token="->"
|
||||||
|
inline_strategy_arguments_token="*"
|
||||||
|
|
||||||
|
save_command_strategy_option="@resurrect-save-command-strategy"
|
||||||
|
default_save_command_strategy="ps"
|
||||||
|
|
||||||
|
# Pane contents capture options.
|
||||||
|
# @resurrect-pane-contents-area option can be:
|
||||||
|
# 'visible' - capture only the visible pane area
|
||||||
|
# 'full' - capture the full pane contents
|
||||||
|
pane_contents_option="@resurrect-capture-pane-contents"
|
||||||
|
pane_contents_area_option="@resurrect-pane-contents-area"
|
||||||
|
default_pane_contents_area="full"
|
||||||
|
|
||||||
|
# set to 'on' to ensure panes are never ever overwritten
|
||||||
|
overwrite_option="@resurrect-never-overwrite"
|
||||||
|
|
||||||
|
# Hooks are set via ${hook_prefix}${name}, i.e. "@resurrect-hook-post-save-all"
|
||||||
|
hook_prefix="@resurrect-hook-"
|
||||||
|
|
||||||
|
delete_backup_after_option="@resurrect-delete-backup-after"
|
||||||
|
default_delete_backup_after="30" # days
|
@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
source "$CURRENT_DIR/scripts/helpers.sh"
|
|
||||||
|
|
||||||
default_save_key="M-s"
|
|
||||||
save_option="@session-saver-save"
|
|
||||||
|
|
||||||
default_restore_key="M-r"
|
|
||||||
restore_option="@session-saver-restore"
|
|
||||||
|
|
||||||
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"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
set_save_bindings
|
|
||||||
set_restore_bindings
|
|
||||||
}
|
|
||||||
main
|
|
23
strategies/irb_default_strategy.sh
Executable file
23
strategies/irb_default_strategy.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# "irb default strategy"
|
||||||
|
#
|
||||||
|
# Example irb process with junk variables:
|
||||||
|
# irb RBENV_VERSION=1.9.3-p429 GREP_COLOR=34;47 TERM_PROGRAM=Apple_Terminal
|
||||||
|
#
|
||||||
|
# When executed, the above will fail. This strategy handles that.
|
||||||
|
|
||||||
|
ORIGINAL_COMMAND="$1"
|
||||||
|
DIRECTORY="$2"
|
||||||
|
|
||||||
|
original_command_wo_junk_vars() {
|
||||||
|
echo "$ORIGINAL_COMMAND" |
|
||||||
|
sed 's/RBENV_VERSION[^ ]*//' |
|
||||||
|
sed 's/GREP_COLOR[^ ]*//' |
|
||||||
|
sed 's/TERM_PROGRAM[^ ]*//'
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
echo "$(original_command_wo_junk_vars)"
|
||||||
|
}
|
||||||
|
main
|
25
strategies/mosh-client_default_strategy.sh
Executable file
25
strategies/mosh-client_default_strategy.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# "mosh-client default strategy"
|
||||||
|
#
|
||||||
|
# Example mosh-client process:
|
||||||
|
# mosh-client -# charm tmux at | 198.199.104.142 60001
|
||||||
|
#
|
||||||
|
# When executed, the above will fail. This strategy handles that.
|
||||||
|
|
||||||
|
ORIGINAL_COMMAND="$1"
|
||||||
|
DIRECTORY="$2"
|
||||||
|
|
||||||
|
mosh_command() {
|
||||||
|
local args="$ORIGINAL_COMMAND"
|
||||||
|
|
||||||
|
args="${args#*-#}"
|
||||||
|
args="${args%|*}"
|
||||||
|
|
||||||
|
echo "mosh $args"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
echo "$(mosh_command)"
|
||||||
|
}
|
||||||
|
main
|
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
|
23
strategies/vim_session.sh
Executable file
23
strategies/vim_session.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# "vim session strategy"
|
||||||
|
#
|
||||||
|
# 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 (without the `-S` flag).
|
||||||
|
|
||||||
|
ORIGINAL_COMMAND="$1"
|
||||||
|
DIRECTORY="$2"
|
||||||
|
|
||||||
|
vim_session_file_exists() {
|
||||||
|
[ -e "${DIRECTORY}/Session.vim" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if vim_session_file_exists; then
|
||||||
|
echo "vim -S"
|
||||||
|
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 :-Z 0 :/tmp 0 bash :
|
||||||
|
pane red 1 :bash 0 :-Z 1 :/tmp 0 bash :
|
||||||
|
pane red 1 :bash 0 :-Z 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 :-Z 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 :-Z 0 :/tmp 0 bash :
|
||||||
|
pane red 1 :bash 0 :-Z 1 :/tmp 0 bash :
|
||||||
|
pane red 1 :bash 0 :-Z 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 :-Z 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
|
42
tests/helpers/create_and_save_tmux_test_environment.exp
Executable file
42
tests/helpers/create_and_save_tmux_test_environment.exp
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
#!/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
|
||||||
|
toggle_zoom_pane
|
||||||
|
|
||||||
|
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"
|
70
tests/helpers/expect_helpers.exp
Normal file
70
tests/helpers/expect_helpers.exp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# 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 toggle_zoom_pane {} {
|
||||||
|
send "z"
|
||||||
|
sleep 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
BIN
video/issue_vid.png
Normal file
BIN
video/issue_vid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
video/screencast_img.png
Normal file
BIN
video/screencast_img.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
110
video/script.md
Normal file
110
video/script.md
Normal 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.
|
Reference in New Issue
Block a user