Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for linked windows #300

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions scripts/restore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,34 @@ restore_pane_layout_for_each_window() {
done
}

restore_linked_windows() {
\grep '^link' $(last_resurrect_file) |
while IFS=$d read line_type session_name window_number window_source; do
if session_exists "$session_name"; then
tmux link-window -s "${window_source}" -t "${session_name}:${window_number}"
else
# Create session if it doesn't exist. This situation occurs
# when a session only contains linked windows. Otherwise the
# session will have been created at some point while
# restoring the panes.
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name"

local created_window_num="$(first_window_num)"
# If the window number we created is the same as our target
# window, we move it up by 1
if [ $created_window_num -eq $window_number ]; then
original_window_num=$created_window_num
let created_window_num=$original_window_num+1
tmux move-window -s "${session_name}:${original_window_num}" -t "${session_name}:${created_window_num}"
fi
tmux link-window -s "${window_source}" -t "${session_name}:${window_number}"
# We keep the window around to the end so that the session
# doesn't disappear
tmux kill-window -t "${session_name}:${created_window_num}"
fi
done
}

restore_shell_history() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ { print $2, $3, $7, $10; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number pane_index pane_command; do
Expand Down Expand Up @@ -359,6 +387,7 @@ main() {
restore_active_pane_for_each_window
restore_zoomed_windows
restore_grouped_sessions # also restores active and alt windows for grouped sessions
restore_linked_windows
restore_active_and_alternate_windows
restore_active_and_alternate_sessions
execute_hook "post-restore-all"
Expand Down
72 changes: 68 additions & 4 deletions scripts/save.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pane_format() {
format+="#{pane_pid}"
format+="${delimiter}"
format+="#{history_size}"
format+="${delimiter}"
format+="#{window_id}"
echo "$format"
}

Expand All @@ -66,6 +68,8 @@ window_format() {
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{window_layout}"
format+="${delimiter}"
format+="#{window_id}"
echo "$format"
}

Expand All @@ -79,6 +83,35 @@ state_format() {
echo "$format"
}

set_canonical_window() {
# We store the canonical window targets using variables named based on the
# window id, because Bash <4.0 doesn't support associative arrays. This
# technique is based on https://stackoverflow.com/a/11776875.
local session_name="$1"
local window_number="$2"
local window_id_num="${3:1}"
local window_target="${session_name}:${window_number}"

printf -v "CANONICAL_WINDOW__${window_id_num}" %s "$window_target"
}

get_canonical_window() {
local window_id_num="${1:1}"

var_name="CANONICAL_WINDOW__${window_id_num}"
echo "${!var_name}"
}

is_canonical_window() {
local session_name="$1"
local window_number="$2"
local window_id="$3"
local window_target="${session_name}:${window_number}"

canonical_window=$(get_canonical_window "$window_id")
[[ "$canonical_window" == "$window_target" ]]
}

dump_panes_raw() {
tmux list-panes -a -F "$(pane_format)"
}
Expand Down Expand Up @@ -223,15 +256,40 @@ fetch_and_dump_grouped_sessions(){
fi
}

set_canonical_windows() {
# When multiple windows are linked via link-window, they will all share the
# same window_id. For each window_id, we arbitrarily pick one of the linked
# window locations (session_name:window_number) to be the canonical window. We
# will only output information for that window, and output a link line for all
# other windows linked to the given id.

# NB: We use bash process substitution instead of piping into the while
# loop to avoid losing the variables set in set_canonical_window. See
# http://mywiki.wooledge.org/BashFAQ/024 for more info.
while IFS=$d read line_type session_name window_index window_active window_flags window_layout window_id; do
# not saving windows from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
# We run this for every window, so that for any given window_id, the
# final time we encounter it will be the location we call canonical.
# This is arbitrary, so just picked easiest to implement.
set_canonical_window "$session_name" "$window_index" "$window_id"
done < <(dump_windows_raw)
}

# translates pane pid to process command running inside a pane
dump_panes() {
local full_command
dump_panes_raw |
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid history_size; do
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid history_size window_id; do
# not saving panes from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
if ! is_canonical_window "$session_name" "$window_number" "$window_id"; 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_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}"
Expand All @@ -240,12 +298,17 @@ dump_panes() {

dump_windows() {
dump_windows_raw |
while IFS=$d read line_type session_name window_index window_active window_flags window_layout; do
while IFS=$d read line_type session_name window_index window_active window_flags window_layout window_id; do
# not saving windows from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_active}${d}${window_flags}${d}${window_layout}"
if is_canonical_window "$session_name" "$window_index" "$window_id"; then
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_active}${d}${window_flags}${d}${window_layout}"
else
canonical_window=$(get_canonical_window "$window_id")
echo "link${d}${session_name}${d}${window_index}${d}${canonical_window}"
fi
done
}

Expand All @@ -256,7 +319,7 @@ dump_state() {
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_name window_active window_flags pane_index dir pane_active pane_command pane_pid history_size; do
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid history_size window_id; do
capture_pane_contents "${session_name}:${window_number}.${pane_index}" "$history_size" "$pane_contents_area"
done
}
Expand All @@ -281,6 +344,7 @@ save_all() {
local last_resurrect_file="$(last_resurrect_file)"
mkdir -p "$(resurrect_dir)"
fetch_and_dump_grouped_sessions > "$resurrect_file_path"
set_canonical_windows
dump_panes >> "$resurrect_file_path"
dump_windows >> "$resurrect_file_path"
dump_state >> "$resurrect_file_path"
Expand Down