Simplify tmux config and bootstrap plugins with TPM

This commit is contained in:
Fabio Scotto di Santolo
2026-04-17 16:09:59 +02:00
parent f468bbaaa6
commit 8c3b399a30
172 changed files with 135 additions and 8410 deletions

View File

@@ -1,44 +1,21 @@
#=== KEYBINDINGS ====#
#=== BEHAVIOR ===#
# change prefix
unbind C-b
set -g prefix C-a
bind C-a send-prefix
# split panes using | and -
bind | split-window -h
bind - split-window -v
unbind '"'
unbind %
# switch panes using Alt-arrow without prefix
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# switch windows using Alt-<number> without prefix
bind-key -n M-1 select-window -t 1
bind-key -n M-2 select-window -t 2
bind-key -n M-3 select-window -t 3
bind-key -n M-4 select-window -t 4
bind-key -n M-5 select-window -t 5
# Other session shortcuts
bind S command-prompt -p "New Session:" "new-session -A -s '%%'"
bind K confirm kill-session
set-option -g detach-on-destroy off
#=== LAYOUT ====#
set -g renumber-window on
set -g base-index 1
setw -g pane-base-index 1
bind c new-window -c '#{pane_current_path}'
# Enable mouse control (clickable windows, panes, resizable panes)
set -g mouse on
# Fix Tmux for colors
bind-key c new-window -c '#{pane_current_path}'
bind-key -n M-Left select-pane -L
bind-key -n M-Right select-pane -R
bind-key -n M-Up select-pane -U
bind-key -n M-Down select-pane -D
# Session popup: Enter switches, Ctrl-k kills, Ctrl-n creates.
bind-key S display-popup -E -w 80% -h 80% "~/.tmux/bin/session-popup '#{pane_current_path}'"
#=== TERMINAL ===#
set -g default-terminal "screen-256color"
set -ag terminal-features ',screen-256color:RGB,tmux*:RGB,alacritty:RGB,foot:RGB,xterm-256color:RGB,gnome*:RGB'
set -g allow-passthrough on
@@ -46,15 +23,15 @@ set -ga update-environment TERM
set -ga update-environment TERM_PROGRAM
#=== PLUGINS ===#
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'sei40kr/tmux-project'
set-option -g @plugin 'b0o/tmux-autoreload'
set -g @plugin 'b0o/tmux-autoreload'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
#=== THEME: Yaru Blue Dark ===#
# palette
set -g @yaru_bg "#2b2b2e"
set -g @yaru_bg_light "#3b3b3e"
set -g @yaru_fg "#e0e0e0"
@@ -66,11 +43,10 @@ set -g @yaru_sep "#4a4a52"
set -g @yaru_red "#c7162b"
set -g @yaru_green "#4caf50"
# status bar
set -g status-position top
set -g status-style "bg=#{@yaru_bg}"
set -g status-left-length 120
set -g status-right-length 120
set -g status-right-length 80
set -g status-left ""
set -ga status-left "#[bg=#{@yaru_bg},fg=#{@yaru_blue},bold] #S "
@@ -82,22 +58,18 @@ set -ga status-left "#[bg=#{@yaru_bg},fg=#{@yaru_sep}]#{?window_zoomed_flag,│,
set -ga status-left "#[bg=#{@yaru_bg},fg=#{@yaru_orange}]#{?window_zoomed_flag, zoom ,}"
set -g status-right ""
set -g status-right '#(gitmux "#{pane_current_path}") '
set -ga status-right "#[bg=#{@yaru_bg},fg=#{@yaru_sep}]│"
set -ga status-right "#[bg=#{@yaru_bg},fg=#{@yaru_blue_b}] #H"
# pane borders
setw -g pane-border-status off
setw -g pane-active-border-style "fg=#{@yaru_blue}"
setw -g pane-border-style "fg=#{@yaru_sep}"
setw -g pane-border-lines single
# message and mode
set -g message-style "bg=#{@yaru_blue},fg=#{@yaru_bg},bold"
set -g message-command-style "bg=#{@yaru_blue},fg=#{@yaru_bg},bold"
set -g mode-style "bg=#{@yaru_orange},fg=#{@yaru_bg},bold"
# window status
set -g window-status-format " #I:#{?#{!=:#{window_name},Window},#W,} "
set -g window-status-style "bg=#{@yaru_bg},fg=#{@yaru_fg_dim}"
set -g window-status-last-style "bg=#{@yaru_bg},fg=#{@yaru_fg}"
@@ -107,11 +79,10 @@ set -gF window-status-separator "#[bg=#{@yaru_bg},fg=#{@yaru_sep}]│"
set -g window-status-current-format " #I:#{?#{!=:#{window_name},Window},#W,} "
set -g window-status-current-style "bg=#{@yaru_blue},fg=#{@yaru_bg},bold"
# Continuum options
set -g @continuum-restore 'on'
#=== SESSION RESTORE ===#
# Project options
set -ag @project-base-dirs ,"${HOME}/.dotfiles","${HOME}/Projects:0:2"
set -g @continuum-restore 'on'
set -g @continuum-save-interval '15'
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'

View File

@@ -0,0 +1,55 @@
#!/bin/sh
set -eu
default_dir=${1:-$HOME}
new_session() {
printf 'New session name: '
IFS= read -r session_name
[ -n "$session_name" ] || return 0
if ! tmux has-session -t "$session_name" 2>/dev/null; then
tmux new-session -d -s "$session_name" -c "$default_dir"
fi
tmux switch-client -t "$session_name"
exit 0
}
while :; do
selection=$(
tmux list-sessions -F '#{session_name}' |
fzf \
--layout=reverse \
--height=100% \
--border \
--prompt='sessions> ' \
--header='Enter switch Ctrl-n new Ctrl-k kill' \
--preview 'tmux list-windows -t {} -F "#{window_index}: #{window_name}#{?window_active, [active],}"' \
--preview-window='right,60%' \
--expect=ctrl-k,ctrl-n
) || exit 0
key=$(printf '%s\n' "$selection" | sed -n '1p')
session_name=$(printf '%s\n' "$selection" | sed -n '2p')
case "$key" in
ctrl-n)
new_session
;;
esac
[ -n "$session_name" ] || exit 0
case "$key" in
ctrl-k)
tmux kill-session -t "$session_name"
;;
*)
tmux switch-client -t "$session_name"
exit 0
;;
esac
done

View File

@@ -1,139 +0,0 @@
# Created by https://www.toptal.com/developers/gitignore/api/linux,macos,windows,vim,git,zsh
# Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,windows,vim,git,zsh
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Vim ###
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
### Zsh ###
# Zsh compiled script + zrecompile backup
*.zwc
*.zwc.old
# Zsh completion-optimization dumpfile
*zcompdump*
# Zsh zcalc history
.zcalc_history
# A popular plugin manager's files
._zinit
.zinit_lstupd
# zdharma/zshelldoc tool's files
zsdoc/data
# robbyrussell/oh-my-zsh/plugins/per-directory-history plugin's files
# (when set-up to store the history in the local directory)
.directory_history
# MichaelAquilina/zsh-autoswitch-virtualenv plugin's files
# (for Zsh plugins using Python)
.venv
# Zunit tests' output
/tests/_output/*
!/tests/_output/.gitkeep

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Maddison Hellstrom
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,166 +0,0 @@
# tmux-autoreload [![version](https://img.shields.io/github/v/tag/b0o/tmux-autoreload?style=flat&color=yellow&label=version&sort=semver)](https://github.com/b0o/tmux-autoreload/releases) [![license: MIT](https://img.shields.io/github/license/b0o/tmux-autoreload?style=flat&color=green)](https://mit-license.org)
tmux-autoreload watches your tmux configuration file and automatically reloads
it on change.
## Install
#### Dependencies
- [entr](https://github.com/eradman/entr)
## Installation
To install tmux-autoreload with TPM (https://github.com/tmux-plugins/tpm), add the
following line to the end of your tmux configuration file:
```sh
set-option -g @plugin 'b0o/tmux-autoreload'
```
Then, inside tmux, press `prefix + I` to fetch the plugin.
If you don't use a plugin manager, git clone tmux-autoreload to the location of your
choice and run it directly:
```sh
run-shell "/path/to/tmux-autoreload/tmux-autoreload.tmux"
```
## Setup
Once installed, you should be good to go unless you use non-standard
configuration file paths or want to customize how tmux-autoreload behaves.
### Configuration file paths
If your tmux config file is at a non-standard location or if you have multiple,
specify them in `@tmux-autoreload-configs`, separated by commas:
```sh
set-option -g @tmux-autoreload-configs '/path/to/configs/a.conf,/path/to/configs/b.conf'
```
### Entrypoints
Normally, tmux-autoreload will source whichever file changed. If you wish to
source a specific set of files when any configuration file changes, use
`@tmux-autoreload-entrypoints`:
```sh
set-option -g @tmux-autoreload-entrypoints '/path/to/entrypoint.conf'
```
You can specify multiple entrypoints separated by commas. All entrypoints
will be sourced when any watched file changes.
Set `@tmux-autoreload-entrypoints` to 1 to use the standard tmux configuration
files as entrypoints, usually `/etc/tmux.conf` and `~/.tmux.conf.` You can see
these files with:
```sh
tmux display-message -p "#{config_files}"
```
#### Entrypoint Notes
- If entrypoints are configured, a changed file itself will not necessarily
be reloaded unless it's an entrypoint or is sourced by an entrypoint.
- Entrypoints will not be watched unless they're a standard tmux
configuration file like `~/.tmux.conf` or are included in `@tmux-autoreload-configs.`
### All Options
```
@tmux-autoreload-configs (Default: unset)
A comma-delimited list of paths to configuration files which should be
watched in addition to the base tmux configuration files.
@tmux-autoreload-entrypoints (Default: unset)
A comma-delimited list of paths to configuration files which should be
reloaded when any watched configuration file changes. If unset, the changed
file itself will be reloaded.
If set, only the entrypoints will be reloaded, not necessarily the changed
file.
If set to 1, the base tmux configuration files are used as the entrypoints
(you can see the base configuration files with the command tmux
display-message -p "#{config_files}").
@tmux-autoreload-quiet 0|1 (Default: 0)
If set to 1, tmux-autoreload will not display status messages.
```
## Advanced Usage
<!-- USAGE -->
```
Usage: tmux-autoreload.tmux [-f] [OPT...]
Automatically reloads your tmux configuration files on change.
Options
-h Display usage information.
-v Display tmux-autoreload version and copyright information.
-f Run in foreground (do not fork).
-k Kill the running tmux-autoreload instance.
-s Show tmux-autoreload status
Installation
To install tmux-autoreload with TPM (https://github.com/tmux-plugins/tpm), add the
following line to the end of your tmux configuration file:
set-option -g @plugin 'b0o/tmux-autoreload'
If you don't use a plugin manager, git clone tmux-autoreload to the location of your
choice and run it directly:
run-shell "/path/to/tmux-autoreload/tmux-autoreload.tmux"
Once installed, you should be good to go unless you use non-standard
configuration file paths or want to customize how tmux-autoreload behaves.
Configuration file paths
If your config file is at a non-standard location or if you have multiple,
specify them in @tmux-autoreload-configs, separated by commas:
set-option -g @tmux-autoreload-configs '/path/to/configs/a.conf,/path/to/configs/b.conf'
Entrypoints
Normally, tmux-autoreload will source whichever file changed. If you wish to
source a specific set of files when any configuration file changes, use
@tmux-autoreload-entrypoints:
set-option -g @tmux-autoreload-entrypoints '/path/to/entrypoint.conf'
You can specify multiple entrypoints separated by commas. All entrypoints
will be sourced when any watched file changes.
Set @tmux-autoreload-entrypoints to 1 to use the standard tmux configuration
files as entrypoints, usually /etc/tmux.conf and ~/.tmux.conf. You can see
these files with:
tmux display-message -p "#{config_files}".
Entrypoint Notes:
- If entrypoints are configured, a changed file itself will not necessarily
be reloaded unless it's an entrypoint or is sourced by an entrypoint.
- Entrypoints will not be watched unless they're a standard tmux
configuration file like ~/.tmux.conf or are included in @tmux-autoreload-configs.
Other Options
@tmux-autoreload-quiet 0|1 (Default: 0)
If set to 1, tmux-autoreload will not display messages
```
<!-- /USAGE -->
## License
<!-- LICENSE -->
&copy; 2021 Maddison Hellstrom
Released under the MIT License.
<!-- /LICENSE -->

View File

@@ -1,154 +0,0 @@
#!/bin/bash
# Copyright (C) 2020-2021 Maddison Hellstrom <https://github.com/b0o>, MIT License.
set -Eeuo pipefail
if [[ ${BASH_VERSINFO[0]} -ge 5 || (${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 4) ]]; then
shopt -s inherit_errexit
fi
declare -g self prog basedir reporoot
self="$(readlink -e "${BASH_SOURCE[0]}")"
prog="$(basename "$self")"
basedir="$(realpath -m "$self/..")"
reporoot="$(realpath -m "$basedir/..")"
# gendocs configuration {{{
declare -g main="${reporoot}/tmux-autoreload.tmux"
declare -gA targets=(
[readme]="$reporoot/README.md"
)
declare -gi copyright_start=2021
function target_readme() {
section -s USAGE -c <<< "$("$main" -h 2>&1)"
section -s LICENSE << EOF
&copy; ${copyright_start}$( (($(date +%Y) == copyright_start)) || date +-%Y) Maddison Hellstrom
Released under the MIT License.
EOF
}
# }}}
declare -gA sections
function section() {
local section
local -i code=0
local lang
local opt OPTARG
local -i OPTIND
while getopts "s:cC:" opt "$@"; do
case "$opt" in
s)
section="$OPTARG"
;;
c)
code=1
;;
C)
code=1
lang="$OPTARG"
;;
\?)
return 1
;;
esac
done
shift $((OPTIND - 1))
local -a lines=('')
if [[ $code -eq 1 ]]; then
lines+=('```'"${lang:-}" '')
fi
mapfile -tO ${#lines[@]} lines
if [[ $code -eq 1 ]]; then
lines+=('' '```')
fi
sections["$section"]="$(printf '%s\n' "${lines[@]}")\n"
}
function regen_section() {
local section="$1"
local content="${sections[$section]}"
awk < "$target" -v "section=$section" -v "content=$content" '
BEGIN {
d = 0
}
{
if (match($0, "^<!-- " section " -->$")) {
d = 1
print $0
print content
next
}
if (match($0, "^<!-- /" section " -->$")) {
d = 0
print $0
next
}
}
d == 0 {
print $0
}
'
}
function main() {
local opt OPTARG
local -i OPTIND
while getopts "h" opt "$@"; do
case "$opt" in
h)
echo "usage: $prog [opt].. [target].." >&2
return 0
;;
\?)
return 1
;;
esac
done
shift $((OPTIND - 1))
local -a targets_selected=("${!targets[@]}")
if [[ $# -gt 0 ]]; then
targets_selected=("$@")
fi
local t target
for t in "${targets_selected[@]}"; do
[[ -v "targets[$t]" ]] || {
echo "unknown target: $t" >&2
return 1
}
target="${targets["$t"]}"
[[ -e "$target" ]] || {
echo "target file not found: $target" >&2
return 1
}
sections=()
"target_${t}" || {
echo "unknown target: $t"
return 1
}
local s
for s in "${!sections[@]}"; do
regen_section "$s" > "${target}_"
mv "${target}_" "$target"
done
done
}
main "$@"

View File

@@ -1,258 +0,0 @@
#!/usr/bin/env bash
# Automatically reloads your tmux configuration files on change.
#
# Copyright 2021 Maddison Hellstrom <github.com/b0o>, MIT License.
set -Eeuo pipefail
if [[ ${BASH_VERSINFO[0]} -ge 5 || (${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 4) ]]; then
shopt -s inherit_errexit
fi
function years_from() {
local from="$1" to
to="${2:-$(date +%Y)}"
if [[ "$from" == "$to" ]]; then
echo "$from"
else
echo "$from-$to"
fi
}
declare -g self prog name
self="$(realpath -e "${BASH_SOURCE[0]}")"
prog="$(basename "$self")"
name="${prog%.tmux}"
declare -gr version="0.0.1"
declare -gr authors=("$(years_from 2021) Maddison Hellstrom <github.com/b0o>")
declare -gr repo_short="b0o/$name"
declare -gr repository="https://github.com/$repo_short"
declare -gr issues="https://github.com/$repo_short/issues"
declare -gr license="MIT"
declare -gr license_url="https://mit-license.org"
function usage() {
cat <<EOF
Usage: $prog [-f] [OPT...]
Automatically reloads your tmux configuration files on change.
Options
-h Display usage information.
-v Display $name version and copyright information.
-f Run in foreground (do not fork).
-k Kill the running $name instance.
-s Show $name status
Installation
To install $name with TPM (https://github.com/tmux-plugins/tpm), add the
following line to the end of your tmux configuration file:
set-option -g @plugin '$repo_short'
If you don't use a plugin manager, git clone $name to the location of your
choice and run it directly:
run-shell "/path/to/$name/$prog"
Once installed, you should be good to go unless you use non-standard
configuration file paths or want to customize how $name behaves.
Configuration file paths
If your config file is at a non-standard location or if you have multiple,
specify them in @$name-configs, separated by commas:
set-option -g @$name-configs '/path/to/configs/a.conf,/path/to/configs/b.conf'
Entrypoints
Normally, $name will source whichever file changed. If you wish to
source a specific set of files when any configuration file changes, use
@$name-entrypoints:
set-option -g @$name-entrypoints '/path/to/entrypoint.conf'
You can specify multiple entrypoints separated by commas. All entrypoints
will be sourced when any watched file changes.
Set @$name-entrypoints to 1 to use the standard tmux configuration
files as entrypoints, usually /etc/tmux.conf and ~/.tmux.conf. You can see
these files with:
tmux display-message -p "#{config_files}".
Entrypoint Notes:
- If entrypoints are configured, a changed file itself will not necessarily
be reloaded unless it's an entrypoint or is sourced by an entrypoint.
- Entrypoints will not be watched unless they're a standard tmux
configuration file like ~/.tmux.conf or are included in @$name-configs.
Other Options
@$name-quiet 0|1 (Default: 0)
If set to 1, $name will not display messages
EOF
}
function usage_version() {
cat <<EOF
$name v$version
Repository: $repository
Issues: $issues
License: $license ($license_url)
Copyright: ${authors[0]}
EOF
if [[ ${#authors[@]} -gt 1 ]]; then
printf ' %s\n' "${authors[@]:1}"
fi
}
function display_message() {
if [[ "$(tmux show-option -gv "@$name-quiet")" == "1" ]]; then
return 0
fi
# `tmux display-message -c` is broken in v3.2a
# https://github.com/tmux/tmux/issues/2737#issuecomment-898861216
if [[ "$(tmux display-message -p "#{version}")" == "3.2a" ]]; then
tmux display-message "$@"
else
while read -r client; do
tmux display-message -c "$client" "$@"
done < <(tmux list-clients -F '#{client_name}')
fi
}
function get_base_configs() {
tmux display-message -p "#{config_files}" | tr ',' '\n' | sort -u
}
function get_user_configs() {
tmux show-option -gv "@$name-configs" | tr ',' '\n' | sort -u
}
function get_entrypoints() {
local entrypoints
entrypoints="$(tmux show-option -gv "@$name-entrypoints")"
if [[ -z "$entrypoints" || "$entrypoints" == "0" ]]; then
return 0
fi
if [[ "$entrypoints" == "1" ]]; then
get_base_configs
else
echo "$entrypoints" | tr ',' '\n'
fi
}
function get_instance() {
local -i instance_pid
instance_pid="$(tmux show-options -gv "@$name-pid")"
if [[ "$instance_pid" -gt 0 ]] && ps "$instance_pid" &>/dev/null; then
echo "$instance_pid"
return 0
fi
return 1
}
function reload() {
local -a entrypoints
mapfile -t entrypoints < <(get_entrypoints)
if [[ ${#entrypoints[@]} -eq 0 ]]; then
entrypoints=("$@")
fi
if msg="$(tmux source-file "${entrypoints[@]}")"; then
display_message "Reloaded $(
printf '%s\n' "${entrypoints[@]}" | xargs -n1 basename | tr '\n' ',' | sed 's/,$/\n/; s/,/, /g'
)"
else
display_message -d 0 "#[fg=white,bg=red,bold]ERROR: $msg"
fi
}
function onexit() {
local -i code=$?
local -i entr_pid=$1
display_message "$name exited with code $code" || true
if [[ -v entr_pid && $entr_pid -gt 1 ]] && ps "$entr_pid" &>/dev/null; then
kill "$entr_pid" || true
fi
tmux set-option -gu "@$name-pid" &
return "$code"
}
function kill_instance() {
local -i instance_pid
if instance_pid="$(get_instance)"; then
kill "$instance_pid"
return $?
fi
echo "$name -k: kill failed: no instance found" >&2
return 1
}
function get_status() {
local -i instance_pid
if instance_pid="$(get_instance)"; then
echo "running: $instance_pid"
return 0
fi
echo "not running"
return 1
}
function main() {
if ! [[ "${1:-}" =~ ^-[hvfksr]$ ]]; then
"$self" -f "$@" &>/dev/null &
disown
exit $?
fi
local opt OPTARG
local -i OPTIND
while getopts "hvfksr:" opt "$@"; do
case "$opt" in
h)
usage
return 0
;;
v)
usage_version
return 0
;;
f)
# Silently ignore -f
;;
k)
kill_instance
return
;;
s)
get_status
return
;;
r)
reload "$OPTARG"
return
;;
\?)
return 1
;;
esac
done
shift $((OPTIND - 1))
if get_instance &>/dev/null; then
return 0
fi
command -v entr &>/dev/null || {
echo "Command not found: entr" >&2
display_message -d 0 "Failed to start $name: Command not found: entr"
return 1
}
tmux set-option -g "@$name-pid" $$
# shellcheck disable=2016
entr -np sh -c '"$0" -r "$1"' "$self" /_ <<<"$(printf '%s\n' "$(get_base_configs)" "$(get_user_configs)")" &
# shellcheck disable=2064
trap "onexit $!" EXIT
wait
}
main "$@"

View File

@@ -1,2 +0,0 @@
# Force text files to have unix eols, so Windows/Cygwin does not break them
*.* eol=lf

View File

@@ -1,45 +0,0 @@
# Changelog
### master
- bugfix: "auto restore" feature stopped working
- bugfix: prevent race condition when auto-saving with locks (@v9v)
- Multiple users on a system can now each run continuum on their own.
### v3.1.0, 2015-03-14
- properly quote scripts
- bugfix: "auto restore" feature does not work on tmux `1.9a`
- bugfix: do not count `tmux source-file .tmux.conf` as a tmux process (when
checking if other tmux server is running). Previously, this caused
interpolation command not to be inserted into `status-right` because `tmux
source-file` was falsely detected as another tmux server.
- add `#{continuum_status}` status line interpolation
### v3.0.0, 2015-02-20
- rename the plugin from `tmux-resurrect-auto` to `tmux-continuum`
### v2.2.0, 2015-02-20
- document tmux multi-server behavior in the readme
- do not auto-restore tmux environment if another tmux server is already running
(we don't want to duplicate stuff)
- bugfixes for 'tmux auto start' OS X Terminal.app and iTerm scripts
- prevent saving for the first 15 minutes only when plugin is sourced the first
time (not on subsequent sources or tmux.conf reloads)
- do not start auto-saving if there's another tmux server running (we don't want
for save files from various tmux environments to override each other)
### v2.1.0, 2015-02-18
- enable "tmux auto start" for OS X
- enable customizing "tmux auto start" for OS X
- fix errors when creating a launchd plist file for auto-start on OS X
### v2.0.0, 2015-02-15
- enable automatic environment restore when tmux is started
### v1.0.0, 2015-02-12
- first working version
- run the save script in the background
- do not start saving right after tmux is started
- add a check for tmux version to the initializer script
- when interval is set to '0' autosave is disabled
- bugfix: helper files not loaded
- update readme with the instructions how to disable auto saving

View File

@@ -1,15 +0,0 @@
### Contributing
Code contributions are welcome!
If you wanna contribute a bigger feature, please open a github issue so we can
discuss it together first.
### 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

View File

@@ -1,19 +0,0 @@
Copyright (C) Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,103 +0,0 @@
# tmux-continuum
Features:
- continuous saving of tmux environment
- automatic tmux start when computer/server is turned on
- automatic restore when tmux is started
Together, these features enable uninterrupted tmux usage. No matter the computer
or server restarts, if the machine is on, tmux will be there how you left it off
the last time it was used.
Tested and working on Linux, OSX and Cygwin.
#### Continuous saving
Tmux environment will be saved at an interval of 15 minutes. All the saving
happens in the background without impact to your workflow.
This action starts automatically when the plugin is installed. Note it requires
the status line to be `on` to run (since it uses a hook in status-right to run).
#### Automatic tmux start
Tmux is automatically started after the computer/server is turned on.
See the [instructions](docs/automatic_start.md) on how to enable this for your
system.
#### Automatic restore
Last saved environment is automatically restored when tmux is started.
Put `set -g @continuum-restore 'on'` in `.tmux.conf` to enable this.
Note: automatic restore happens **exclusively** on tmux server start. No other
action (e.g. sourcing `.tmux.conf`) triggers this.
#### Dependencies
`tmux 1.9` or higher, `bash`,
[tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) plugin.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Please make sure you have
[tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) installed.
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
Hit `prefix + I` to fetch the plugin and source it. The plugin will
automatically start "working" in the background, no action required.
### Manual Installation
Please make sure you have
[tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) installed.
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-continuum ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/continuum.tmux
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`
The plugin will automatically start "working" in the background, no action
required.
### Docs
- [frequently asked questions](docs/faq.md)
- [behavior when running multiple tmux servers](docs/multiple_tmux_servers.md) -
this doc is safe to skip, but you might want to read it if you're using tmux
with `-L` or `-S` flags
- [automatically start tmux after the computer is turned on](docs/automatic_start.md)
- [continuum status in tmux status line](docs/continuum_status.md)
### 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
### Known Issues
- In order to be executed periodically, the plugin updates the `status-right` tmux variable. In case some plugin (usually themes) overwrites the `status-right` variable, the autosave feature stops working. To fix this issue, place the plugin last in the TPM plugins list.
### Reporting bugs and contributing
Both contributing and bug reports are welcome. Please check out
[contributing guidelines](CONTRIBUTING.md).
### License
[MIT](LICENSE.md)

View File

@@ -1,89 +0,0 @@
#!/usr/bin/env bash
set -x
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/helpers.sh"
source "$CURRENT_DIR/scripts/variables.sh"
source "$CURRENT_DIR/scripts/shared.sh"
save_command_interpolation="#($CURRENT_DIR/scripts/continuum_save.sh)"
supported_tmux_version_ok() {
"$CURRENT_DIR/scripts/check_tmux_version.sh" "$SUPPORTED_VERSION"
}
handle_tmux_automatic_start() {
"$CURRENT_DIR/scripts/handle_tmux_automatic_start.sh"
}
another_tmux_server_running() {
if just_started_tmux_server; then
another_tmux_server_running_on_startup
else
# script loaded after tmux server start can have multiple clients attached
[ "$(number_tmux_processes_except_current_server)" -gt "$(number_current_server_client_processes)" ]
fi
}
delay_saving_environment_on_first_plugin_load() {
if [ -z "$(get_tmux_option "$last_auto_save_option" "")" ]; then
# last save option not set, this is first time plugin load
set_last_save_timestamp
fi
}
add_resurrect_save_interpolation() {
local status_right_value="$(get_tmux_option "status-right" "")"
# check interpolation not already added
if ! [[ "$status_right_value" == *"$save_command_interpolation"* ]]; then
local new_value="${save_command_interpolation}${status_right_value}"
set_tmux_option "status-right" "$new_value"
fi
}
just_started_tmux_server() {
local tmux_start_time
tmux_start_time="$(tmux display-message -p -F '#{start_time}')"
local restore_max_delay
restore_max_delay="$(get_tmux_option "$auto_restore_max_delay_option" "${auto_restore_max_delay_default}")"
[ "$tmux_start_time" == "" ] || [ "$tmux_start_time" -gt "$(($(date +%s)-${restore_max_delay}))" ]
}
start_auto_restore_in_background() {
"$CURRENT_DIR/scripts/continuum_restore.sh" &
}
update_tmux_option() {
local option="$1"
local option_value="$(get_tmux_option "$option")"
# replace interpolation string with a script to execute
local new_option_value="${option_value/$status_interpolation_string/$status_script}"
set_tmux_option "$option" "$new_option_value"
}
main() {
if supported_tmux_version_ok; then
handle_tmux_automatic_start
# Advanced edge case handling: start auto-saving only if this is the
# only tmux server. We don't want saved files from more environments to
# overwrite each other.
if ! another_tmux_server_running; then
# give user a chance to restore previously saved session
delay_saving_environment_on_first_plugin_load
add_resurrect_save_interpolation
fi
if just_started_tmux_server; then
start_auto_restore_in_background
fi
# Put "#{continuum_status}" interpolation in status-right or
# status-left tmux option to get current tmux continuum status.
update_tmux_option "status-right"
update_tmux_option "status-left"
fi
}
main

View File

@@ -1,47 +0,0 @@
# Automatic Tmux start
Tmux is automatically started after the computer/server is turned on.
### OS X
To enable this feature:
- put `set -g @continuum-boot 'on'` in `.tmux.conf`
- reload tmux config with this shell command: `$ tmux source-file ~/.tmux.conf`
Next time the computer is started:
- `Terminal.app` window will open and resize to maximum size
- `tmux` command will be executed in the terminal window
- if "auto restore" feature is enabled, tmux will start restoring previous env
Config options:
- `set -g @continuum-boot-options 'fullscreen'` - terminal window
will go fullscreen
- `set -g @continuum-boot-options 'iterm'` - start [iTerm](https://www.iterm2.com) instead
of `Terminal.app`
- `set -g @continuum-boot-options 'iterm,fullscreen'` - start `iTerm`
in fullscreen
- `set -g @continuum-boot-options 'kitty'` - start [kitty](https://sw.kovidgoyal.net/kitty) instead
of `Terminal.app`
- `set -g @continuum-boot-options 'kitty,fullscreen'` - start `kitty`
in fullscreen
- `set -g @continuum-boot-options 'alacritty'` - start [alacritty](https://github.com/alacritty/alacritty) instead of `Terminal.app`
- `set -g @continuum-boot-options 'alacritty,fullscreen'` - start `alacritty`
in fullscreen
Note: The first time you reboot your machine and activate this feature you may be prompted about a script requiring
access to a system program (i.e. - System Events). If this happens tmux will not start automatically and you will need
to go to `System Preferences -> Security & Privacy -> Accessability` and add the script to the list of apps that are
allowed to control your computer.
### Linux
Help with this would be greatly appreciated. Please get in touch.
#### Systemd
##### this will only start the tmux server, it will *not* start any terminal emulator
To enable automatic start with systemd:
- Put `set -g @continuum-boot 'on'` in `.tmux.conf`
- reload tmux config with this shell command: `$ tmux source-file ~/.tmux.conf`
- see [systemd](/docs/systemd_details.md) for more details about how this is implemented

View File

@@ -1,17 +0,0 @@
## Continuum status in tmux status line
There is an option to display current status of tmux continuum in tmux status
line. This is done via `#{continuum_status}` interpolation and it works with
both `status-right` and `status-left` tmux native options.
Example usage:
set -g status-right 'Continuum status: #{continuum_status}'
When running, `#{continuum_status}` will show continuum save interval:
Continuum status: 15
or if continuous saving is disabled:
Continuum status: off

View File

@@ -1,45 +0,0 @@
### FAQ
> Will a previous save be overwritten immediately after I start tmux?
No, first automatic save starts 15 minutes after tmux is started. If automatic
restore is not enabled, that gives you enough time to manually restore from a
previous save.
> I want to make a restore to a previous point in time, but it seems that save
is now overwritten?
Read how to [restore a previously saved environment](https://github.com/tmux-plugins/tmux-resurrect/blob/master/docs/restoring_previously_saved_environment.md)
> Will this plugin fill my hard disk?
Most likely no. A regular save file is in the range of 5Kb. And `tmux-resurrect` already has a `remove_old_backups()` routine that will ["remove resurrect files older than 30 days, but keep at least 5 copies of backup."](https://github.com/tmux-plugins/tmux-resurrect/blob/da1a7558024b8552f7262b39ed22e3d679304f99/scripts/save.sh#L271-L277)
> How do I change the save interval to i.e. 1 hour?
The interval is always measured in minutes. So setting the interval to `60`
(minutes) will do the trick. Put this in `.tmux.conf`:
set -g @continuum-save-interval '60'
and then source `tmux.conf` by executing this command in the shell
`$ tmux source-file ~/.tmux.conf`.
> How do I stop automatic saving?
Just set the save interval to `0`. Put this in `.tmux.conf`
set -g @continuum-save-interval '0'
and then source `tmux.conf` by executing this command in the shell
`$ tmux source-file ~/.tmux.conf`.
> I had automatic restore turned on, how do I disable it now?
Just remove `set -g @continuum-restore 'on'` from `tmux.conf`.
To be absolutely sure automatic restore doesn't happen, create a
`tmux_no_auto_restore` file in your home directory (command:
`$ touch ~/tmux_no_auto_restore`). Automatic restore won't happen if this file
exists.

View File

@@ -1,20 +0,0 @@
### Behavior when running multiple tmux servers
(This is safe to skip if you're always running a single tmux server.)
If you're an advanced tmux user, you might be running multiple tmux servers at
the same time. Maybe you start the first tmux server with `$ tmux` and then
later another one with e.g. `$ tmux -S/tmp/foo`.
You probably don't want to "auto restore" the same environment in the second
tmux that uses `/tmp/foo` socket. You also probably don't want two tmux
environments both having "auto save" feature on (think about overwrites).
This plugin handles multi-server scenario by giving precedence to the tmux
server that was first started.
In the above example, the server started with `$ tmux` will do "auto
restore" (if enabled) and will start "auto saving".
"Auto restore" or "auto saving" **will not** happen for the second server that
was started later with the `$ tmux -S/tmp/foo` command. The plugin will
detect the presence of another server (`$ tmux`) and give it precedence.

View File

@@ -1,13 +0,0 @@
# Systemd automatic start for tmux
The first time tmux starts when `@continuum-boot` is set to 'on' tmux-continuum will generate a user level systemd unit file which it will save to `${HOME}/.config/systemd/user/tmux.service` and enable it. From then on when that user logs in, either through a GUI session or on the console or via ssh, Systemd will start the tmux server.
The command used to start the tmux server is determined via the `@continuum-systemd-start-cmd` option that can be set in .tmux.conf. (Remember to reload your configuration with `tmux source ~/.tmux.conf` afterwards.
The default command to use is `new-session -d`. If you want more control over what sessions get started then you should set up your sessions in tmux.conf and set `@continuum-systemd-start-cmd = 'start-server'`. As this will be executed as part of systemd's ExecStart statement there will be no shell parsing. See [Systemd manual](http://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines) for more details.
To control the tmux service you can use all the standard `systemctl` commands using the `--user` argument. eg to see if the tmux server has started:
systemctl --user status tmux.service

View File

@@ -1,33 +0,0 @@
#!/usr/bin/env bash
VERSION="$1"
UNSUPPORTED_MSG="$2"
# this is used to get "clean" integer version number. Examples:
# `tmux 1.9` => `19`
# `1.9a` => `19`
get_digits_from_string() {
local string="$1"
local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
echo "$only_digits"
}
tmux_version_int() {
local tmux_version_string=$(tmux -V)
echo "$(get_digits_from_string "$tmux_version_string")"
}
exit_if_unsupported_version() {
local current_version="$1"
local supported_version="$2"
if [ "$current_version" -lt "$supported_version" ]; then
exit 1
fi
}
main() {
local supported_version_int="$(get_digits_from_string "$VERSION")"
local current_version_int="$(tmux_version_int)"
exit_if_unsupported_version "$current_version_int" "$supported_version_int"
}
main

View File

@@ -1,29 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
auto_restore_enabled() {
local auto_restore_value="$(get_tmux_option "$auto_restore_option" "$auto_restore_default")"
[ "$auto_restore_value" == "on" ] && [ ! -f "$auto_restore_halt_file" ]
}
fetch_and_run_tmux_resurrect_restore_script() {
# give tmux some time to start and source all the plugins
sleep 1
local resurrect_restore_script_path="$(get_tmux_option "$resurrect_restore_path_option" "")"
if [ -n "$resurrect_restore_script_path" ]; then
"$resurrect_restore_script_path"
fi
}
main() {
# Advanced edge case handling: auto restore only if this is the only tmux
# server. If another tmux server exists, it is assumed auto-restore is not wanted.
if auto_restore_enabled && ! another_tmux_server_running_on_startup; then
fetch_and_run_tmux_resurrect_restore_script
fi
}
main

View File

@@ -1,60 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/shared.sh"
supported_tmux_version_ok() {
"$CURRENT_DIR/check_tmux_version.sh" "$SUPPORTED_VERSION"
}
get_interval() {
get_tmux_option "$auto_save_interval_option" "$auto_save_interval_default"
}
auto_save_not_disabled() {
[ "$(get_interval)" -gt 0 ]
}
enough_time_since_last_run_passed() {
local last_saved_timestamp="$(get_tmux_option "$last_auto_save_option" "0")"
local interval_minutes="$(get_interval)"
local interval_seconds="$((interval_minutes * 60))"
local next_run="$((last_saved_timestamp + $interval_seconds))"
[ "$(current_timestamp)" -ge "$next_run" ]
}
fetch_and_run_tmux_resurrect_save_script() {
local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "")"
if [ -n "$resurrect_save_script_path" ]; then
"$resurrect_save_script_path" "quiet" >/dev/null 2>&1 &
set_last_save_timestamp
fi
}
acquire_lock() {
# Sometimes tmux starts multiple saves in parallel. We want only one
# save to be running, otherwise we can get corrupted saved state.
local lockdir_prefix="/tmp/tmux-continuum-$(current_tmux_server_pid)-lock-"
# The following implements a lock that auto-expires after 100...200s.
local lock_generation=$((`date +%s` / 100))
local lockdir1="${lockdir_prefix}${lock_generation}"
local lockdir2="${lockdir_prefix}$(($lock_generation + 1))"
if mkdir "$lockdir1"; then
trap "rmdir "$lockdir1"" EXIT
if mkdir "$lockdir2"; then
trap "rmdir "$lockdir1" "$lockdir2"" EXIT
return 0
fi
fi
return 1 # Someone else has the lock.
}
main() {
if supported_tmux_version_ok && auto_save_not_disabled && enough_time_since_last_run_passed && acquire_lock; then
fetch_and_run_tmux_resurrect_save_script
fi
}
main

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
print_status() {
local save_int="$(get_tmux_option "$auto_save_interval_option")"
local status=""
local style_wrap
if [ $save_int -gt 0 ]; then
style_wrap="$(get_tmux_option "$status_on_style_wrap_option" "")"
status="$save_int"
else
style_wrap="$(get_tmux_option "$status_off_style_wrap_option" "")"
status="off"
fi
if [ -n "$style_wrap" ]; then
status="${style_wrap/$status_wrap_string/$status}"
fi
echo "$status"
}
print_status

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
is_tmux_automatic_start_enabled() {
local auto_start_value="$(get_tmux_option "$auto_start_option" "$auto_start_default")"
[ "$auto_start_value" == "on" ]
}
is_osx() {
[ $(uname) == "Darwin" ]
}
is_systemd() {
[ $(ps -o comm= -p1) == 'systemd' ]
}
main() {
if is_tmux_automatic_start_enabled; then
if is_osx; then
"$CURRENT_DIR/handle_tmux_automatic_start/osx_enable.sh"
elif is_systemd; then
"$CURRENT_DIR/handle_tmux_automatic_start/systemd_enable.sh"
fi
else
if is_osx; then
"$CURRENT_DIR/handle_tmux_automatic_start/osx_disable.sh"
elif is_systemd; then
"$CURRENT_DIR/handle_tmux_automatic_start/systemd_disable.sh"
fi
fi
}
main

View File

@@ -1,68 +0,0 @@
#!/usr/bin/env bash
# for "true full screen" call the script with "fullscreen" as the first argument
TRUE_FULL_SCREEN="$1"
start_terminal_and_run_tmux() {
osascript <<-EOF
tell application "alacritty"
activate
delay 0.5
tell application "System Events" to tell process "alacritty"
set frontmost to true
keystroke "tmux"
key code 36
end tell
end tell
EOF
}
resize_window_to_full_screen() {
osascript <<-EOF
tell application "alacritty"
activate
tell application "System Events"
if (every window of process "alacritty") is {} then
keystroke "n" using command down
end if
tell application "Finder"
set desktopSize to bounds of window of desktop
end tell
set position of front window of process "alacritty" to {0, 0}
set size of front window of process "alacritty" to {item 3 of desktopSize, item 4 of desktopSize}
end tell
end tell
EOF
}
resize_to_true_full_screen() {
osascript <<-EOF
tell application "Alacritty"
activate
delay 0.5
tell application "System Events" to tell process "Alacritty"
if front window exists then
tell front window
if value of attribute "AXFullScreen" then
set value of attribute "AXFullScreen" to false
else
set value of attribute "AXFullScreen" to true
end if
end tell
end if
end tell
end tell
EOF
}
main() {
start_terminal_and_run_tmux
if [ "$TRUE_FULL_SCREEN" == "fullscreen" ]; then
resize_to_true_full_screen
else
resize_window_to_full_screen
fi
}
main

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/../variables.sh"
main() {
rm "$osx_auto_start_file_path" > /dev/null 2>&1
}
main

View File

@@ -1,72 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/../helpers.sh"
source "$CURRENT_DIR/../variables.sh"
template() {
local tmux_start_script="$1"
local is_fullscreen="$2"
local fullscreen_tag=""
if [ "$is_fullscreen" == "true" ]; then
# newline and spacing so tag is aligned with other tags in template
fullscreen_tag=$'\n <string>fullscreen</string>'
fi
local content
read -r -d '' content <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${osx_auto_start_file_name}</string>
<key>ProgramArguments</key>
<array>
<string>${tmux_start_script}</string>$fullscreen_tag
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOF
echo "$content"
}
get_strategy() {
local options="$1"
if [[ "$options" =~ "iterm" ]]; then
echo "iterm"
elif [[ "$options" =~ "kitty" ]]; then
echo "kitty"
elif [[ "$options" =~ "alacritty" ]]; then
echo "alacritty"
else
# Terminal.app is the default console app
echo "terminal"
fi
}
get_fullscreen_option_value() {
local options="$1"
if [[ "$options" =~ "fullscreen" ]]; then
echo "true"
else
echo "false"
fi
}
main() {
local options="$(get_tmux_option "$auto_start_config_option" "$auto_start_config_default")"
local strategy="$(get_strategy "$options")"
local fullscreen_option_value="$(get_fullscreen_option_value "$options")"
local tmux_start_script_path="${CURRENT_DIR}/osx_${strategy}_start_tmux.sh"
local launchd_plist_file_content="$(template "$tmux_start_script_path" "$fullscreen_option_value")"
if ! diff "$osx_auto_start_file_path" <(echo "$launchd_plist_file_content") &>/dev/null ; then
echo "$launchd_plist_file_content" > "$osx_auto_start_file_path"
fi
}
main

View File

@@ -1,66 +0,0 @@
#!/usr/bin/env bash
# for "true full screen" call the script with "fullscreen" as the first argument
TRUE_FULL_SCREEN="$1"
start_iterm_and_run_tmux() {
osascript <<-EOF
tell application "iTerm"
activate
# open iterm window
try
set _session to current session of current window
on error
set _term to (make new terminal)
tell _term
launch session "Tmux"
set _session to current session
end tell
end try
# start tmux
tell _session
write text "tmux"
end tell
end tell
EOF
}
resize_window_to_full_screen() {
osascript <<-EOF
tell application "iTerm"
set winID to id of window 1
tell application "Finder"
set desktopSize to bounds of window of desktop
end tell
set bounds of window id winID to desktopSize
end tell
EOF
}
resize_to_true_full_screen() {
osascript <<-EOF
tell application "iTerm"
# wait for iTerm to start
delay 1
activate
# short wait for iTerm to gain focus
delay 0.1
# Command + Enter for fullscreen
tell application "System Events"
key code 36 using {command down}
end tell
end tell
EOF
}
main() {
start_iterm_and_run_tmux
if [ "$TRUE_FULL_SCREEN" == "fullscreen" ]; then
resize_to_true_full_screen
else
resize_window_to_full_screen
fi
}
main

View File

@@ -1,60 +0,0 @@
#!/usr/bin/env bash
# for "true full screen" call the script with "fullscreen" as the first argument
TRUE_FULL_SCREEN="$1"
start_terminal_and_run_tmux() {
osascript <<-EOF
tell application "kitty"
activate
delay 5
tell application "System Events" to tell process "kitty"
set frontmost to true
keystroke "tmux"
key code 36
end tell
end tell
EOF
}
resize_window_to_full_screen() {
osascript <<-EOF
tell application "kitty"
activate
tell application "System Events"
if (every window of process "kitty") is {} then
keystroke "n" using command down
end if
tell application "Finder"
set desktopSize to bounds of window of desktop
end tell
set position of front window of process "kitty" to {0, 0}
set size of front window of process "kitty" to {item 3 of desktopSize, item 4 of desktopSize}
end tell
end tell
EOF
}
resize_to_true_full_screen() {
osascript <<-EOF
tell application "kitty"
activate
delay 1
tell application "System Events" to tell process "kitty"
keystroke "f" using {control down, command down}
end tell
end tell
EOF
}
main() {
start_terminal_and_run_tmux
if [ "$TRUE_FULL_SCREEN" == "fullscreen" ]; then
resize_to_true_full_screen
else
resize_window_to_full_screen
fi
}
main

View File

@@ -1,52 +0,0 @@
#!/usr/bin/env bash
# for "true full screen" call the script with "fullscreen" as the first argument
TRUE_FULL_SCREEN="$1"
start_terminal_and_run_tmux() {
osascript <<-EOF
tell application "Terminal"
if not (exists window 1) then reopen
activate
set winID to id of window 1
do script "tmux" in window id winID
end tell
EOF
}
resize_window_to_full_screen() {
osascript <<-EOF
tell application "Terminal"
set winID to id of window 1
tell application "Finder"
set desktopSize to bounds of window of desktop
end tell
set bounds of window id winID to desktopSize
end tell
EOF
}
resize_to_true_full_screen() {
osascript <<-EOF
tell application "Terminal"
# waiting for Terminal.app to start
delay 1
activate
# short wait for Terminal to gain focus
delay 0.1
tell application "System Events"
keystroke "f" using {control down, command down}
end tell
end tell
EOF
}
main() {
start_terminal_and_run_tmux
if [ "$TRUE_FULL_SCREEN" == "fullscreen" ]; then
resize_to_true_full_screen
else
resize_window_to_full_screen
fi
}
main

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
# Maintainer: Sven Vowe @nuclearglow
# Contact maintainer for any change to this file.
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/../variables.sh"
main() {
systemctl --user disable ${systemd_service_name}
}
main

View File

@@ -1,78 +0,0 @@
#!/usr/bin/env bash
# Maintainer: Sven Vowe @nuclearglow
# Contact maintainer for any change to this file.
CURRENT_DIR="$( dirname ${BASH_SOURCE[0]} )"
source "$CURRENT_DIR/../helpers.sh"
source "$CURRENT_DIR/../variables.sh"
template() {
local tmux_start_script="$1"
shift
local options="$@"
local content=""
local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "$(realpath ${CURRENT_DIR}/../../../tmux-resurrect/scripts/save.sh)")"
local tmux_path="$(command -v tmux)"
read -r -d '' content <<-EOF
[Unit]
Description=tmux default session (detached)
Documentation=man:tmux(1)
[Service]
Type=forking
Environment=DISPLAY=:0
ExecStart=${tmux_path} ${systemd_tmux_server_start_cmd}
ExecStop=${resurrect_save_script_path}
ExecStop=${tmux_path} kill-server
KillMode=control-group
RestartSec=2
[Install]
WantedBy=default.target
EOF
echo "$content"
}
systemd_tmux_is_enabled() {
systemctl --user is-enabled $(basename "${systemd_unit_file_path}") >/dev/null 2>&1
}
enable_tmux_unit_on_boot() {
if ! systemd_tmux_is_enabled; then
systemctl --user enable ${systemd_service_name}
fi
}
systemd_unit_file() {
local options="$(get_tmux_option "$auto_start_config_option" "${auto_start_config_default}")"
local systemd_tmux_server_start_cmd="$(get_tmux_option "${systemd_tmux_server_start_cmd_option}" "${systemd_tmux_server_start_cmd_default}" )"
local tmux_start_script_path="${CURRENT_DIR}/linux_start_tmux.sh"
local systemd_unit_file=$(template "${tmux_start_script_path}" "${options}")
echo "$systemd_unit_file"
}
write_unit_file() {
systemd_unit_file > "${systemd_unit_file_path}"
}
write_unit_file_unless_exists() {
local systemd_unit_file_dir=$(dirname ${systemd_unit_file_path})
if ! [ -d $systemd_unit_file_dir ]; then
mkdir -p $systemd_unit_file_dir
write_unit_file
elif ! [ -e "${systemd_unit_file_path}" ]; then
write_unit_file
fi
}
main() {
write_unit_file_unless_exists
enable_tmux_unit_on_boot
}
main

View File

@@ -1,49 +0,0 @@
get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
set_tmux_option() {
local option="$1"
local value="$2"
tmux set-option -gq "$option" "$value"
}
# multiple tmux server detection helpers
current_tmux_server_pid() {
echo "$TMUX" |
cut -f2 -d","
}
all_tmux_processes() {
# ignores `tmux source-file .tmux.conf` command used to reload tmux.conf
local user_id=$(id -u)
ps -u $user_id -o "command pid" |
\grep "^tmux" |
\grep -v "^tmux source"
}
number_tmux_processes_except_current_server() {
all_tmux_processes |
\grep -v " $(current_tmux_server_pid)$" |
wc -l |
sed "s/ //g"
}
number_current_server_client_processes() {
tmux list-clients |
wc -l |
sed "s/ //g"
}
another_tmux_server_running_on_startup() {
# there are 2 tmux processes (current tmux server + 1) on tmux startup
[ "$(number_tmux_processes_except_current_server)" -gt 1 ]
}

View File

@@ -1,7 +0,0 @@
current_timestamp() {
echo "$(date +%s)"
}
set_last_save_timestamp() {
set_tmux_option "$last_auto_save_option" "$(current_timestamp)"
}

View File

@@ -1,43 +0,0 @@
SUPPORTED_VERSION="1.9"
# these tmux options contain paths to tmux resurrect save and restore scripts
resurrect_save_path_option="@resurrect-save-script-path"
resurrect_restore_path_option="@resurrect-restore-script-path"
auto_save_interval_option="@continuum-save-interval"
auto_save_interval_default="15"
# time when the tmux environment was last saved (unix timestamp)
last_auto_save_option="@continuum-save-last-timestamp"
auto_restore_option="@continuum-restore"
auto_restore_default="off"
auto_restore_halt_file="${HOME}/tmux_no_auto_restore"
auto_restore_max_delay_option="@continuum-restore-max-delay"
auto_restore_max_delay_default="10"
# tmux auto start options
auto_start_option="@continuum-boot"
auto_start_default="off"
# comma separated list of additional options for tmux auto start
auto_start_config_option="@continuum-boot-options"
auto_start_config_default=""
osx_auto_start_file_name="Tmux.Start.plist"
osx_auto_start_file_path="${HOME}/Library/LaunchAgents/${osx_auto_start_file_name}"
status_interpolation_string="\#{continuum_status}"
status_script="#($CURRENT_DIR/scripts/continuum_status.sh)"
# below options set style/color for #{continuum_status} interpolation
status_on_style_wrap_option="@continuum-status-on-wrap-style" # example value: "#[fg=green]#{value}#[fg=white]"
status_off_style_wrap_option="@continuum-status-off-wrap-style" # example value: "#[fg=yellow,bold]#{value}#[fg=white,nobold]"
status_wrap_string="\#{value}"
systemd_service_name="tmux.service"
systemd_unit_file_path="$HOME/.config/systemd/user/${systemd_service_name}"
systemd_tmux_server_start_cmd_option="@continuum-systemd-start-cmd"
systemd_tmux_server_start_cmd_default="new-session -d"

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019-2021 Seong Yong-ju
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,79 +0,0 @@
# tmux-project
Search projects and open them in a new session.
## Prequisites
- find
- [fzf](https://github.com/junegunn/fzf) (needs `fzf-tmux` to be installed)
## Install
### Installation with [tpm](https://github.com/tmux-plugins/tpm) (recommended)
Add the following line to your `.tmux.conf`.
```tmux
set -g @plugin 'sei40kr/tmux-project'
```
---
It is also recommended to add the following line to your `.tmux.conf` to prevent
a client from being detached when the active session is destroyed.
```tmux
set detach-on-destroy no-detached
```
## Customization
| Variable | Default value | Description |
| :------------------------------ | :-------------------- | :-------------------------------------------------------------------------------- |
| `@project-key` | `"g"` | The key to invoke the project search. If you set it to `""`, the key is disabled. |
| `@project-base-dirs` | `""` | A comma-separated list of directories and their depths to search for projects. |
| `@project-rooters` | `".git"` | A comma-separated list of rooters. |
| `@project-fzf-tmux-layout-opts` | `""` | The layout options for fzf-tmux. See `fzf-tmux(1)` for details. |
| `@project-fzf-opts` | `"--preview 'ls {}'"` | The options for fzf. See `fzf(1)` for details. |
### Setting `@project-base-dirs`
`@project-base-dirs` is a comma-separated list of directories and their depths to search for projects.
Each element of the list is in the following format:
```
/path/to/dir[:<min depth>[:<max depth>]]
```
- If you omit `<min depth>` and `<max depth>`, they are set to `0` and `0` respectively.
- If you omit `<max depth>`, it is set to `<min depth>`. (means `<min depth>` is the exact depth)
If you omit the depth or explicitly set it to `0`, the directory itself will be
added as a project. In that case, you can add the directory as a project even if
it contains no rooter.
---
For example, if you want to search for ghq repositories as projects:
```tmux
set -ag @project-base-dirs ,"${GHQ_ROOT}:3"
```
For example, if you want to add `~/.vim` itself as a project:
```tmux
set -ag @project-base-dirs ,"${HOME}/.vim"
```
## tmuxinator Integration
tmux-project can be integrated with [tmuxinator](https://github.com/tmuxinator/tmuxinator).
When you have tmuxinator installed and the selected project has
`.tmuxinator.yml` at the root, tmux-project will start a new session with
tmuxinator.
## License
MIT

View File

@@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1683014792,
"narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,30 +0,0 @@
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, flake-utils, nixpkgs }: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
packages.default = pkgs.callPackage ./packages/default.nix { };
devShells.default =
let
tmux_conf = pkgs.writeText "tmux.conf" ''
run-shell ${self.packages.${system}.default.rtp}/project.tmux
'';
in
pkgs.mkShell {
buildInputs = with pkgs; [ tmux fzf ];
shellHook = ''
TMUX= TMUX_TMPDIR= exec tmux -f ${tmux_conf}
'';
};
}
);
}

View File

@@ -1,8 +0,0 @@
get_tmux_option() {
local option=$1
local default_value=$2
local option_value
option_value=$(tmux show -gqv "$option")
echo "${option_value:-$default_value}"
}

View File

@@ -1,98 +0,0 @@
#!/usr/bin/env bash
basedir="$(cd "$(dirname "$0")/.." && pwd)"
# shellcheck source=../lib/tmux.bash
. "$basedir/lib/tmux.bash"
find_projects() {
local -a base_dirs rooters rooter_opts
IFS=',' read -ra base_dirs < <(get_tmux_option '@project-base-dirs')
IFS=',' read -ra rooters < <(get_tmux_option '@project-rooters' '.git')
for rooter in "${rooters[@]}"; do
rooter_opts+=("-o" "-name" "$rooter")
done
rooter_opts=('(' "${rooter_opts[@]:1}" ')')
for base_dir in "${base_dirs[@]}"; do
# If the base_dir is empty, skip it
if [[ -z "$base_dir" ]]; then
continue
fi
local -a tmp
IFS=':' read -ra tmp <<<"$base_dir"
path="${tmp[0]}"
min_depth="${tmp[1]:-0}"
max_depth="${tmp[2]:-${min_depth}}"
if [[ min_depth -eq 0 && max_depth -eq 0 ]]; then
# If min_depth and max_depth are both 0, that means we
# want to add the base_dir itself as a project.
# In that case, add the base_dir as a project even if it
# contains no rooter.
if [[ -d "$path" || -L "$path" ]]; then
echo "$path"
fi
else
find "$path" -mindepth "$((min_depth + 1))" \
-maxdepth "$((max_depth + 1))" \
"${rooter_opts[@]}" \
-printf '%h\n'
fi
done
}
# Convert a path to a session name. The session name is the last component of
# the path. Dots are replaced with underscores. If the path starts with a dot,
# the dot is removed.
# e.g. /home/username/.emacs.d -> emacs_d
to_session_name() {
local session_name="$1"
session_name="${session_name##*/}"
# Dots are not allowed in a tmux session name
# e.g. .emacs.d -> _emacs_d
session_name="${session_name//./_}"
# If the path starts with a slash (a dot), remove it
# e.g. .emacs.d -> _emacs_d -> emacs_d
session_name="${session_name#_}"
echo "$session_name"
}
main() {
local fzf_opts fzf_tmux_layout_opts selected_path session_name
# shellcheck disable=SC2207
fzf_opts="$(get_tmux_option "@project-fzf-opts" "--preview 'ls {}'")"
# shellcheck disable=SC2207
fzf_tmux_layout_opts="$(get_tmux_option "@project-fzf-tmux-layout-opts")"
selected_path="$(find_projects | eval "fzf-tmux ${fzf_tmux_layout_opts} -- ${fzf_opts}")"
if [[ -z "$selected_path" ]]; then
return 0
fi
# If the selected project has .tmuxinator.yml file, use tmuxinator to
# start the session.
if command -v tmuxinator &>/dev/null && [[ -f "${selected_path}/.tmuxinator.yml" ]]; then
tmuxinator start -p "${selected_path}/.tmuxinator.yml"
return 0
fi
session_name="$(to_session_name "$selected_path")"
# If the session already exists, attach to it. Otherwise, create a new
# session and attach to it.
if ! tmux has-session -t "$session_name" 2>/dev/null; then
# Return 0 even if creating the session fails.
tmux new-session -d -s "$session_name" -c "$selected_path" \; \
set -t "$session_name" destroy-unattached off || :
fi
tmux switch-client -t "$session_name"
}
main "$@"

View File

@@ -1,11 +0,0 @@
{ lib, tmuxPlugins }:
tmuxPlugins.mkTmuxPlugin {
pluginName = "project";
version = "unstable-2023-05-04";
src = ../.;
meta = with lib; {
license = licenses.mit;
platforms = platforms.unix;
};
}

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env bash
basedir="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib/tmux.bash
. "${basedir}/lib/tmux.bash"
key="$(get_tmux_option '@project-key' 'g')"
if [[ -n "$key" ]]; then
tmux bind-key "$key" run-shell -b "${basedir}/libexec/switch-project.bash"
fi

View File

@@ -1,5 +0,0 @@
# 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

View File

@@ -1,3 +0,0 @@
run_tests
tests/run_tests_in_isolation
tests/helpers/helpers.sh

View File

@@ -1,3 +0,0 @@
[submodule "lib/tmux-test"]
path = lib/tmux-test
url = https://github.com/tmux-plugins/tmux-test.git

View File

@@ -1,19 +0,0 @@
# 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

View File

@@ -1,163 +0,0 @@
# Changelog
### 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
- save and restore current and alternate session
- fix a bug with non-existing window names
- restore active pane for each window that has multiple panes
- restore active and alternate window for each session
### v0.0.2, 2014-08-26
- saving a new session does not remove the previous one
- make the directory where sessions are stored configurable
- support only Tmux v1.9 or greater
- display a nice error message if saved session file does not exist
- added README
### v0.0.1, 2014-08-26
- started a project
- basic saving and restoring works

View File

@@ -1,12 +0,0 @@
### 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

View File

@@ -1,19 +0,0 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,129 +0,0 @@
# Tmux Resurrect
[![Build Status](https://travis-ci.org/tmux-plugins/tmux-resurrect.svg?branch=master)](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
[![screencast screenshot](/video/screencast_img.png)](https://vimeo.com/104763018)
### Key bindings
- `prefix + Ctrl-s` - save
- `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)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-resurrect'
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin.
### Manual Installation
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-resurrect ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/resurrect.tmux
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`.
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
[MIT](LICENSE.md)

View File

@@ -1,11 +0,0 @@
# 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'

View File

@@ -1,33 +0,0 @@
# 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)'

View File

@@ -1,72 +0,0 @@
# 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.

View File

@@ -1,39 +0,0 @@
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"
```

View File

@@ -1,31 +0,0 @@
# 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.

View File

@@ -1,14 +0,0 @@
# 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.

View File

@@ -1,205 +0,0 @@
# 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.

View File

@@ -1,19 +0,0 @@
# 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'
> ```

View File

@@ -1,15 +0,0 @@
# 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 `~`.

View File

@@ -1,2 +0,0 @@
.vagrant/
lib/

View File

@@ -1,19 +0,0 @@
# 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

View File

@@ -1,46 +0,0 @@
# Changelog
### master
- move `setup` task to `.travis.yml` for travis tests
- "merge" travis.yml and travis_for_plugins.yml files (no need to keep em
separate)
- add more useful helper functions
- remove tmux-test repo as a submodule from self, this causes issues with
`$ git submodule update --recursive --init` command that some users use for
managing other plugins
- add new helper `teardown_helper`
- add `run_tests` helper
- change CLI syntax for choosing vagrant machine to run the tests on
- enable running just a single test via `run_tests` cli interface
- add `--keep-running` cli option to continue running vagrant after the tests
are done executing
- start using tmux 2.0 for tests
- use tmux 2.5 for tests
### v0.2.0, 2015-02-22
- `setup` script gitignores `tests/helpers.sh`
- move `tests/helpers.sh` to `tests/helpers/helpers.sh`
- `setup` undo removes added lines from gitignore file
### v0.1.0, 2015-02-22
- changes so that 'tmux-test' can be included with tmux plugins
- do not gitignore submodules directory
- add installation and usage instructions
- copy `.travis.yml` to the project root when running `setup` script
- add a brief mention of travis CI to the readme
- add test helpers
- `setup` script symlinks helpers file to `tests/` directory
- `setup` script can undo most of its actions
- add a tmux scripting test
- `tmux-test` uses `tmux-test` to test itself
- update `tmux-test` submodule
- a different `travis.yml` for `tmux-test` and for plugins
### v0.0.1, 2015-02-21
- git init
- add vagrant provisioning scripts for ubuntu and debian
- add a ".travis.yml" file
- generic "run_tests" script
- "run_tests_in_isolation" script
- add "Vagrantfile"
- enable passing VM names as arguments to "run_tests" script

View File

@@ -1,19 +0,0 @@
Copyright (C) Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,134 +0,0 @@
# tmux-test
[![Build Status](https://travis-ci.org/tmux-plugins/tmux-test.png?branch=master)](https://travis-ci.org/tmux-plugins/tmux-test)
A small framework for isolated testing of tmux plugins. Isolation is achieved by
running the tests in `Vagrant`. Works on [travis](travis-ci.org) too.
Extracted from [tmux plugin manager](https://github.com/tmux-plugins/tpm) and
[tmux-copycat](https://github.com/tmux-plugins/tmux-copycat).
Dependencies: `Vagrant` (not required when running on travis).
### Setup
Let's say you made tmux plugin with the following file hierarchy:
```text
/tmux-plugin
|-- plugin.tmux
`-- scripts
`-- plugin_script.sh
```
From your project root directory (tmux-plugin/) execute the following shell
command to fetch `tmux-test` and add it as a submodule:
$ git submodule add https://github.com/tmux-plugins/tmux-test.git lib/tmux-test
Run the `setup` script:
$ lib/tmux-test/setup
The project directory will now look like this (additions have comments):
```text
/tmux-plugin
|-- plugin.tmux
|-- run_tests # symlink, gitignored
|-- .gitignore # 2 lines appended to gitignore
|-- .travis.yml # added
|-- lib/tmux-test/ # git submodule
|-- scripts
| `-- plugin_script.sh
`-- tests # dir to put the tests in
`-- run_tests_in_isolation.sh # symlink, gitignored
`-- helpers
`-- helpers.sh # symlinked bash helpers, gitignored
```
`tmux-test` is now set up. You are ok to commit the additions to the repo.
### Writing and running tests
A test is any executable with a name starting with `test_` in `tests/`
directory.
Now that you installed `tmux-test` let's create an example test.
- create a `tests/test_example.sh` file with the following content (it's a
`bash` script but it can be any executable):
#/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# bash helpers provided by 'tmux-test'
source $CURRENT_DIR/helpers/helpers.sh
# installs plugin from current repo in Vagrant (or on Travis)
install_tmux_plugin_under_test_helper
# start tmux in background (plugin under test is sourced)
tmux new -d
# get first session name
session_name="$(tmux list-sessions -F "#{session_name}")"
# fail the test if first session name is not "0"
if [ "$session_name" == "0" ]; then
# fail_helper is also provided by 'tmux-test'
fail_helper "First session name is not '0' by default"
fi
# sets the right script exit code ('tmux-test' helper)
exit_helper
- make the test file executable with `$ chmod +x tests/test_example.sh`
- run the test by executing `./run_tests` from the project root directory
- the first invocation might take some time because Vagrant's ubuntu virtual
machine is downloading. You should see `Success, tests pass!` message when it's
done.
Check out more example test scripts in this project's [tests/ directory](tests/).
### Continuous integration
The setup script (`lib/tmux-test/setup`) added a `.travis.yml` file to the
project root. To setup continuous integration, just add/enable the project on
[travis](travis-ci.org).
### Notes
- The `tests/` directory for tests and `lib/tmux-test/` for cloning `tmux-test`
into cannot be changed currently
- Don't run `tests/run_tests_in_isolation` script on your local development
environment. That's an internal test runner meant to be executed in an
isolated environment like `vagrant` or `travis`.<br/>
Use `./run_tests` script.
- You can use `KEEP_RUNNING=true ./run_tests` for faster test running cycle.
If this case `Vagrant` will keep running even after the tests are done.
- You can use `VAGRANT_CWD=lib/tmux-test/ vagrant ssh ubuntu` for ssh login to
`Vagrant`.
### Running `tmux-test` framework tests
`tmux-test` uses itself to test itself. To run framework tests:
- clone this project `$ git clone git@github.com:tmux-plugins/tmux-test.git`
- `$ cd tmux-test`
- run `$ ./run_framework_tests`
### Other goodies
- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for
regex searches in tmux and fast match selection
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
restoring and continuous saving of tmux env
You might want to follow [@brunosutic](https://twitter.com/brunosutic) on
twitter if you want to hear about new tmux plugins or feature updates.
### License
[MIT](LICENSE.md)

View File

@@ -1,17 +0,0 @@
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.synced_folder "../../", "/vagrant"
config.vm.define :ubuntu do |ubuntu|
ubuntu.vm.box = "hashicorp/precise64"
ubuntu.vm.provision "shell", path: "vagrant_ubuntu_provisioning.sh"
end
config.vm.define :centos do |centos|
centos.vm.box = "chef/centos-6.5"
centos.vm.provision "shell", path: "vagrant_centos_provisioning.sh"
end
end

View File

@@ -1,17 +0,0 @@
#!/usr/bin/env bash
# This file is used to run "tmux-test" framework tests.
# "setup" script is needed to run the tests, but it overrides some working dir
# files. To address that, "setup" is run before the tests and its actions are
# undone after.
main() {
git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test
lib/tmux-test/setup
./run_tests
local exit_value=$?
lib/tmux-test/setup "undo"
exit "$exit_value"
}
main

View File

@@ -1,93 +0,0 @@
#!/usr/bin/env bash
# invoke this script from your projects root directory
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# pass "undo" as a script arg to undo most of the setup actions
UNDO_SETUP="$1"
undo() {
[ "$UNDO_SETUP" == "undo" ]
}
restore() {
local file="$1"
rm -f "$file"
git checkout -- "$file" 2>/dev/null
}
gitignore() {
local file="$1"
grep -q "^${file}$" .gitignore 2>/dev/null || echo "$file" >> .gitignore
}
remove_from_gitignore() {
local file="$1"
local escaped_filename="$(echo "$file" | sed "s,/,\\\/,g")"
sed -i"" "/^${escaped_filename}$/d" .gitignore
}
add_files_to_gitignore() {
if ! undo; then
gitignore "run_tests"
gitignore "tests/run_tests_in_isolation"
gitignore "tests/helpers/helpers.sh"
else
remove_from_gitignore "run_tests"
remove_from_gitignore "tests/run_tests_in_isolation"
remove_from_gitignore "tests/helpers/helpers.sh"
fi
}
symlink_user_test_runner() {
local file="run_tests"
if ! undo; then
ln -sf "lib/tmux-test/${file}" "$file"
else
restore "$file"
fi
}
create_directory_for_tests() {
if ! undo; then
mkdir -p tests/helpers/
fi
}
symlink_internal_test_runner() {
local file="tests/run_tests_in_isolation"
if ! undo; then
ln -sf "../lib/tmux-test/${file}" "$file"
else
restore "$file"
fi
}
symlink_test_helpers() {
local file="tests/helpers/helpers.sh"
if ! undo; then
ln -sf "../../lib/tmux-test/${file}" "$file"
else
restore "$file"
fi
}
copy_travis_yml() {
local file=".travis.yml"
if ! undo; then
cp "lib/tmux-test/${file}" "$file"
else
restore "$file"
fi
}
main() {
add_files_to_gitignore
symlink_user_test_runner
create_directory_for_tests
symlink_internal_test_runner
symlink_test_helpers
copy_travis_yml
}
main

View File

@@ -1,68 +0,0 @@
# This file is a symlink from 'tmux-test' plugin.
# You probably don't want to edit it.
# Global variable that keeps the value of test status (success/fail).
# Suggested usage is via `fail_helper` and `exit_helper` functions.
TEST_STATUS="success"
# PRIVATE FUNCTIONS
_clone_the_plugin() {
local plugin_path="${HOME}/.tmux/plugins/tmux-plugin-under-test/"
rm -rf "$plugin_path"
git clone --recursive "${CURRENT_DIR}/../" "$plugin_path" >/dev/null 2>&1
}
_add_plugin_to_tmux_conf() {
set_tmux_conf_helper<<-HERE
run-shell '~/.tmux/plugins/tmux-plugin-under-test/*.tmux'
HERE
}
# PUBLIC HELPER FUNCTIONS
teardown_helper() {
rm -f ~/.tmux.conf
rm -rf ~/.tmux/
tmux kill-server >/dev/null 2>&1
}
set_tmux_conf_helper() {
> ~/.tmux.conf # empty tmux.conf file
while read line; do
echo "$line" >> ~/.tmux.conf
done
}
fail_helper() {
local message="$1"
echo "$message" >&2
TEST_STATUS="fail"
}
exit_helper() {
teardown_helper
if [ "$TEST_STATUS" == "fail" ]; then
echo "FAIL!"
echo
exit 1
else
echo "SUCCESS"
echo
exit 0
fi
}
install_tmux_plugin_under_test_helper() {
_clone_the_plugin
_add_plugin_to_tmux_conf
}
run_tests() {
# get all the functions starting with 'test_' and invoke them
for test in $(compgen -A function | grep "^test_"); do
"$test"
done
exit_helper
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# This file is a symlink from 'tmux-test' plugin.
# You probably don't want to edit it.
# This script should be run within an isolated enviroment (Vagrant, travis).
# Depending on what the tests do, it might NOT be safe to run this script
# directly on the development machine.
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
EXIT_VALUE=0 # running a test suite is successful by default
all_test_files() {
ls -1 "$CURRENT_DIR" | # test files are in the current dir
\grep -i "^test" | # test file names start with "test"
xargs # file names in a single line
}
set_exit_val_to_false() {
EXIT_VALUE=1
}
run_tests() {
local test_file tests_files
if [ "$#" -gt 0 ]; then
test_files="${@//tests\//}" # remove 'tests/' directory prefix
else
test_files="$(all_test_files)"
fi
for test_file in $test_files; do
echo "Running test: $test_file"
"${CURRENT_DIR}/${test_file}"
# handling exit value
local test_exit_value="$?"
if [ "$test_exit_value" -ne 0 ]; then
set_exit_val_to_false
fi
done
}
main() {
run_tests "$@"
exit "$EXIT_VALUE"
}
main "$@"

View File

@@ -1,24 +0,0 @@
#/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# bash helpers provided by 'tmux-test'
source $CURRENT_DIR/helpers/helpers.sh
# installs plugin from current repo in Vagrant (or on Travis)
install_tmux_plugin_under_test_helper
# start tmux in background (plugin under test is sourced)
tmux new -d
# get first session name
session_name="$(tmux list-sessions -F "#{session_name}")"
# fail the test if first session name is not "0"
if ! [ "$session_name" == "0" ]; then
# fail_helper is also provided by 'tmux-test'
fail_helper "First session name is not '0' by default"
fi
# sets the right script exit code ('tmux-test' helper)
exit_helper

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $CURRENT_DIR/helpers/helpers.sh
number_of_windows() {
tmux list-windows |
wc -l |
sed "s/ //g"
}
main() {
# start tmux in the background
tmux new -d
tmux new-window
local number_of_windows="$(number_of_windows)"
if ! [ "$number_of_windows" -eq 2 ]; then
fail_helper "Incorrect number of windows. Expected 2, got $number_of_windows"
fi
exit_helper
}
main

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
# libevent2 installation instructions from here
# https://gist.github.com/rschuman/6168833
sudo su -
yum -y install gcc kernel-devel make automake autoconf ncurses-devel
yum -y install git-core expect vim ruby ruby-devel ruby-irb
# install libevent2 from source
curl http://sourceforge.net/projects/levent/files/latest/download?source=files -L -o libevent2.tar.gz -w 'Last URL was: %{url_effective}'
cd ~/downloads
tar zxvf libevent2.tar.gz
cd ./libevent-*
./configure --prefix=/usr/local
make
make install
# compile tmux
git clone https://github.com/tmux/tmux.git ~/tmux_source
cd ~/tmux_source
git checkout 2.5
sh autogen.sh
LDFLAGS="-L/usr/local/lib -Wl,-rpath=/usr/local/lib" ./configure --prefix=/usr/local
make && sudo make install

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
sudo apt-get update
sudo apt-get install -y git-core expect vim
sudo apt-get install -y python-software-properties software-properties-common
sudo apt-get install -y build-essential libtool autotools-dev autoconf
sudo apt-get install -y pkg-config libevent-dev libncurses-dev
sudo apt-get install -y man-db
# install tmux 2.5
git clone https://github.com/tmux/tmux.git ~/tmux_source
cd ~/tmux_source
git checkout 2.5
sh autogen.sh
./configure && make && sudo make install

View File

@@ -1,40 +0,0 @@
#!/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

View File

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

View File

@@ -1,26 +0,0 @@
#!/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

View File

@@ -1,22 +0,0 @@
#!/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

View File

@@ -1,24 +0,0 @@
#!/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

View File

@@ -1,78 +0,0 @@
#!/usr/bin/env bash
VERSION="$1"
UNSUPPORTED_MSG="$2"
get_tmux_option() {
local option=$1
local default_value=$2
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
# Ensures a message is displayed for 5 seconds in tmux prompt.
# Does not override the 'display-time' tmux option.
display_message() {
local message="$1"
# display_duration defaults to 5 seconds, if not passed as an argument
if [ "$#" -eq 2 ]; then
local display_duration="$2"
else
local display_duration="5000"
fi
# saves user-set 'display-time' option
local saved_display_time=$(get_tmux_option "display-time" "750")
# sets message display time to 5 seconds
tmux set-option -gq display-time "$display_duration"
# displays message
tmux display-message "$message"
# restores original 'display-time' value
tmux set-option -gq display-time "$saved_display_time"
}
# this is used to get "clean" integer version number. Examples:
# `tmux 1.9` => `19`
# `1.9a` => `19`
get_digits_from_string() {
local string="$1"
local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
echo "$only_digits"
}
tmux_version_int() {
local tmux_version_string=$(tmux -V)
echo "$(get_digits_from_string "$tmux_version_string")"
}
unsupported_version_message() {
if [ -n "$UNSUPPORTED_MSG" ]; then
echo "$UNSUPPORTED_MSG"
else
echo "Error, Tmux version unsupported! Please install Tmux version $VERSION or greater!"
fi
}
exit_if_unsupported_version() {
local current_version="$1"
local supported_version="$2"
if [ "$current_version" -lt "$supported_version" ]; then
display_message "$(unsupported_version_message)"
exit 1
fi
}
main() {
local supported_version_int="$(get_digits_from_string "$VERSION")"
local current_version_int="$(tmux_version_int)"
exit_if_unsupported_version "$current_version_int" "$supported_version_int"
}
main

View File

@@ -1,159 +0,0 @@
if [ -d "$HOME/.tmux/resurrect" ]; then
default_resurrect_dir="$HOME/.tmux/resurrect"
else
default_resurrect_dir="${XDG_DATA_HOME:-$HOME/.local/share}"/tmux/resurrect
fi
resurrect_dir_option="@resurrect-dir"
SUPPORTED_VERSION="1.9"
RESURRECT_FILE_PREFIX="tmux_resurrect"
RESURRECT_FILE_EXTENSION="txt"
_RESURRECT_DIR=""
_RESURRECT_FILE_PATH=""
d=$'\t'
# helper functions
get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
# Ensures a message is displayed for 5 seconds in tmux prompt.
# Does not override the 'display-time' tmux option.
display_message() {
local message="$1"
# display_duration defaults to 5 seconds, if not passed as an argument
if [ "$#" -eq 2 ]; then
local display_duration="$2"
else
local display_duration="5000"
fi
# saves user-set 'display-time' option
local saved_display_time=$(get_tmux_option "display-time" "750")
# sets message display time to 5 seconds
tmux set-option -gq display-time "$display_duration"
# displays message
tmux display-message "$message"
# restores original 'display-time' value
tmux set-option -gq display-time "$saved_display_time"
}
supported_tmux_version_ok() {
$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
resurrect_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"
}
pane_contents_dir() {
echo "$(resurrect_dir)/$1/pane_contents/"
}
pane_contents_file() {
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
}

View File

@@ -1,198 +0,0 @@
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"
}

View File

@@ -1,14 +0,0 @@
#!/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

View File

@@ -1,387 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/process_restore_helpers.sh"
source "$CURRENT_DIR/spinner_helpers.sh"
# 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

View File

@@ -1,278 +0,0 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/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

View File

@@ -1,8 +0,0 @@
start_spinner() {
$CURRENT_DIR/tmux_spinner.sh "$1" "$2" &
export SPINNER_PID=$!
}
stop_spinner() {
kill $SPINNER_PID
}

View File

@@ -1,29 +0,0 @@
#!/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

View File

@@ -1,48 +0,0 @@
# 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

View File

@@ -1,23 +0,0 @@
#!/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

View File

@@ -1,25 +0,0 @@
#!/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

View File

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

View File

@@ -1,23 +0,0 @@
#!/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

View File

@@ -1,21 +0,0 @@
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

View File

@@ -1,21 +0,0 @@
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

View File

@@ -1,42 +0,0 @@
#!/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"

View File

@@ -1,70 +0,0 @@
# 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
}

View File

@@ -1,18 +0,0 @@
#!/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"

View File

@@ -1,11 +0,0 @@
# 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 ]
}

View File

@@ -1,33 +0,0 @@
#!/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

Some files were not shown because too many files have changed in this diff Show More