112 lines
4.0 KiB
Bash
Executable File
112 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Cycle the agent popup through the windows of the session underneath it rather
|
|
# than through the agent session's own window list. Bound to <prefix>C-n and
|
|
# <prefix>C-p inside the agent session (see tmux.conf).
|
|
#
|
|
# Root windows with no agent window are stepped over; cycling never creates an
|
|
# agent window, only <prefix>A does. Both the client underneath and the popup
|
|
# move, so closing the popup leaves you on the window you cycled to.
|
|
|
|
set -e
|
|
|
|
direction=${1:-next}
|
|
us=$'\x1f'
|
|
|
|
# Fall back to plain window cycling within the agent session when there is no
|
|
# usable client underneath (popup opened before this was configured, or the
|
|
# recorded client has gone away).
|
|
fallback() {
|
|
case $direction in
|
|
prev) tmux previous-window -t agent ;;
|
|
*) tmux next-window -t agent ;;
|
|
esac
|
|
exit 0
|
|
}
|
|
|
|
popup_client=$(tmux display-message -p '#{@agent_popup_client}' 2>/dev/null || true)
|
|
[ -n "$popup_client" ] || fallback
|
|
|
|
# The session and window underneath the popup, as the client sees them now.
|
|
client_info=$(tmux list-clients -F "#{client_name}$us#{session_name}$us#{window_id}" \
|
|
| awk -v FS="$us" -v client="$popup_client" '$1 == client { print $2 FS $3; exit }')
|
|
[ -n "$client_info" ] || fallback
|
|
IFS=$us read -r client_session client_window <<<"$client_info"
|
|
|
|
# Every agent window, with what it is keyed to.
|
|
agent_ids=()
|
|
agent_dirs=()
|
|
agent_origins=()
|
|
while IFS=$us read -r window_id dir origin; do
|
|
[ -n "$dir" ] || continue
|
|
agent_ids+=("$window_id")
|
|
agent_dirs+=("$dir")
|
|
agent_origins+=("$origin")
|
|
done < <(tmux list-windows -t agent \
|
|
-F "#{window_id}$us#{@agent_dir}$us#{@agent_origin_window}" 2>/dev/null || true)
|
|
[ ${#agent_ids[@]} -gt 0 ] || exit 0
|
|
|
|
# The agent window serving a root window: by recorded origin first, then by the
|
|
# directory its first pane sits in. Mirrors agent-sync.sh's resolution, reversed.
|
|
agent_window_for() {
|
|
local root_window=$1 start_path=$2 current_path=$3 i
|
|
for i in "${!agent_ids[@]}"; do
|
|
if [ -n "${agent_origins[$i]}" ] && [ "${agent_origins[$i]}" = "$root_window" ]; then
|
|
printf '%s\n' "${agent_ids[$i]}"
|
|
return 0
|
|
fi
|
|
done
|
|
for i in "${!agent_ids[@]}"; do
|
|
if [ "${agent_dirs[$i]}" = "$start_path" ] || [ "${agent_dirs[$i]}" = "$current_path" ]; then
|
|
printf '%s\n' "${agent_ids[$i]}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# The underlying session's windows, in its own order, each with its first pane's
|
|
# directory. Panes are listed grouped by window in index order.
|
|
root_windows=()
|
|
root_paths=()
|
|
seen_window=""
|
|
while IFS=$us read -r window_id start_path current_path; do
|
|
[ "$window_id" != "$seen_window" ] || continue
|
|
seen_window=$window_id
|
|
root_windows+=("$window_id")
|
|
root_paths+=("$start_path$us$current_path")
|
|
done < <(tmux list-panes -s -t "$client_session" \
|
|
-F "#{window_id}$us#{pane_start_path}$us#{pane_current_path}" 2>/dev/null || true)
|
|
|
|
count=${#root_windows[@]}
|
|
[ "$count" -gt 0 ] || exit 0
|
|
|
|
# Where the client sits in that order.
|
|
current=0
|
|
for i in "${!root_windows[@]}"; do
|
|
if [ "${root_windows[$i]}" = "$client_window" ]; then
|
|
current=$i
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$direction" = prev ]; then step=-1; else step=1; fi
|
|
|
|
# Walk in that direction for the first window with an agent window, wrapping.
|
|
# Landing back on the starting window means nothing else has one: selecting it
|
|
# again is a no-op, which is the wanted behaviour.
|
|
i=$current
|
|
for _ in $(seq 1 "$count"); do
|
|
i=$(( (i + step + count) % count ))
|
|
target_root=${root_windows[$i]}
|
|
IFS=$us read -r start_path current_path <<<"${root_paths[$i]}"
|
|
target_agent=$(agent_window_for "$target_root" "$start_path" "$current_path") || continue
|
|
|
|
# Re-key the agent window to the window being moved to, so the sync hook that
|
|
# the select below fires agrees with what happened here instead of dragging
|
|
# the client back to wherever that window was last opened from.
|
|
tmux set-window-option -t "$target_agent" @agent_origin_window "$target_root"
|
|
tmux select-window -t "$target_root"
|
|
tmux select-window -t "$target_agent"
|
|
exit 0
|
|
done
|