@factiii/runner 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1459 -918
- package/index/image-package.json +1 -1
- package/package.json +6 -4
- package/vendor/tmux-resurrect/COMMIT +1 -0
- package/vendor/tmux-resurrect/LICENSE.md +19 -0
- package/vendor/tmux-resurrect/README.factiii.md +13 -0
- package/vendor/tmux-resurrect/VERSION +1 -0
- package/vendor/tmux-resurrect/save_command_strategies/ps.sh +24 -0
- package/vendor/tmux-resurrect/scripts/check_tmux_version.sh +78 -0
- package/vendor/tmux-resurrect/scripts/helpers.sh +168 -0
- package/vendor/tmux-resurrect/scripts/process_restore_helpers.sh +198 -0
- package/vendor/tmux-resurrect/scripts/restore.sh +410 -0
- package/vendor/tmux-resurrect/scripts/save.sh +328 -0
- package/vendor/tmux-resurrect/scripts/spinner_helpers.sh +8 -0
- package/vendor/tmux-resurrect/scripts/tmux_spinner.sh +29 -0
- package/vendor/tmux-resurrect/scripts/variables.sh +51 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
4
|
+
|
|
5
|
+
source "$CURRENT_DIR/variables.sh"
|
|
6
|
+
source "$CURRENT_DIR/helpers.sh"
|
|
7
|
+
source "$CURRENT_DIR/spinner_helpers.sh"
|
|
8
|
+
|
|
9
|
+
# delimiters
|
|
10
|
+
d=$'\t'
|
|
11
|
+
delimiter=$'\t'
|
|
12
|
+
|
|
13
|
+
# if "quiet" script produces no output
|
|
14
|
+
SCRIPT_OUTPUT="$1"
|
|
15
|
+
|
|
16
|
+
grouped_sessions_format() {
|
|
17
|
+
local format
|
|
18
|
+
format+="#{session_grouped}"
|
|
19
|
+
format+="${delimiter}"
|
|
20
|
+
format+="#{session_group}"
|
|
21
|
+
format+="${delimiter}"
|
|
22
|
+
format+="#{session_id}"
|
|
23
|
+
format+="${delimiter}"
|
|
24
|
+
format+="#{session_name}"
|
|
25
|
+
echo "$format"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pane_format() {
|
|
29
|
+
local format
|
|
30
|
+
format+="pane"
|
|
31
|
+
format+="${delimiter}"
|
|
32
|
+
format+="#{session_name}"
|
|
33
|
+
format+="${delimiter}"
|
|
34
|
+
format+="#{window_index}"
|
|
35
|
+
format+="${delimiter}"
|
|
36
|
+
format+="#{window_active}"
|
|
37
|
+
format+="${delimiter}"
|
|
38
|
+
format+=":#{window_flags}"
|
|
39
|
+
format+="${delimiter}"
|
|
40
|
+
format+="#{pane_index}"
|
|
41
|
+
format+="${delimiter}"
|
|
42
|
+
format+="#{pane_title}"
|
|
43
|
+
format+="${delimiter}"
|
|
44
|
+
format+=":#{pane_current_path}"
|
|
45
|
+
format+="${delimiter}"
|
|
46
|
+
format+="#{pane_active}"
|
|
47
|
+
format+="${delimiter}"
|
|
48
|
+
format+="#{pane_current_command}"
|
|
49
|
+
format+="${delimiter}"
|
|
50
|
+
format+="#{pane_pid}"
|
|
51
|
+
format+="${delimiter}"
|
|
52
|
+
format+="#{history_size}"
|
|
53
|
+
echo "$format"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
window_format() {
|
|
57
|
+
local format
|
|
58
|
+
format+="window"
|
|
59
|
+
format+="${delimiter}"
|
|
60
|
+
format+="#{session_name}"
|
|
61
|
+
format+="${delimiter}"
|
|
62
|
+
format+="#{window_index}"
|
|
63
|
+
format+="${delimiter}"
|
|
64
|
+
format+=":#{window_name}"
|
|
65
|
+
format+="${delimiter}"
|
|
66
|
+
format+="#{window_active}"
|
|
67
|
+
format+="${delimiter}"
|
|
68
|
+
format+=":#{window_flags}"
|
|
69
|
+
format+="${delimiter}"
|
|
70
|
+
format+="#{window_layout}"
|
|
71
|
+
echo "$format"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
state_format() {
|
|
75
|
+
local format
|
|
76
|
+
format+="state"
|
|
77
|
+
format+="${delimiter}"
|
|
78
|
+
format+="#{client_session}"
|
|
79
|
+
format+="${delimiter}"
|
|
80
|
+
format+="#{client_last_session}"
|
|
81
|
+
echo "$format"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
dump_panes_raw() {
|
|
85
|
+
tmux list-panes -a -F "$(pane_format)"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
dump_windows_raw(){
|
|
89
|
+
tmux list-windows -a -F "$(window_format)"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
toggle_window_zoom() {
|
|
93
|
+
local target="$1"
|
|
94
|
+
tmux resize-pane -Z -t "$target"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_save_command_strategy_file() {
|
|
98
|
+
local save_command_strategy="$(get_tmux_option "$save_command_strategy_option" "$default_save_command_strategy")"
|
|
99
|
+
local strategy_file="$CURRENT_DIR/../save_command_strategies/${save_command_strategy}.sh"
|
|
100
|
+
local default_strategy_file="$CURRENT_DIR/../save_command_strategies/${default_save_command_strategy}.sh"
|
|
101
|
+
if [ -e "$strategy_file" ]; then # strategy file exists?
|
|
102
|
+
echo "$strategy_file"
|
|
103
|
+
else
|
|
104
|
+
echo "$default_strategy_file"
|
|
105
|
+
fi
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pane_full_command() {
|
|
109
|
+
local pane_pid="$1"
|
|
110
|
+
local strategy_file="$(_save_command_strategy_file)"
|
|
111
|
+
# execute strategy script to get pane full command
|
|
112
|
+
$strategy_file "$pane_pid"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
number_nonempty_lines_on_screen() {
|
|
116
|
+
local pane_id="$1"
|
|
117
|
+
tmux capture-pane -pJ -t "$pane_id" |
|
|
118
|
+
sed '/^$/d' |
|
|
119
|
+
wc -l |
|
|
120
|
+
sed 's/ //g'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# tests if there was any command output in the current pane
|
|
124
|
+
pane_has_any_content() {
|
|
125
|
+
local pane_id="$1"
|
|
126
|
+
local history_size="$(tmux display -p -t "$pane_id" -F "#{history_size}")"
|
|
127
|
+
local cursor_y="$(tmux display -p -t "$pane_id" -F "#{cursor_y}")"
|
|
128
|
+
# doing "cheap" tests first
|
|
129
|
+
[ "$history_size" -gt 0 ] || # history has any content?
|
|
130
|
+
[ "$cursor_y" -gt 0 ] || # cursor not in first line?
|
|
131
|
+
[ "$(number_nonempty_lines_on_screen "$pane_id")" -gt 1 ]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
capture_pane_contents() {
|
|
135
|
+
local pane_id="$1"
|
|
136
|
+
local start_line="-$2"
|
|
137
|
+
local pane_contents_area="$3"
|
|
138
|
+
if pane_has_any_content "$pane_id"; then
|
|
139
|
+
if [ "$pane_contents_area" = "visible" ]; then
|
|
140
|
+
start_line="0"
|
|
141
|
+
fi
|
|
142
|
+
# the printf hack below removes *trailing* empty lines
|
|
143
|
+
printf '%s\n' "$(tmux capture-pane -epJ -S "$start_line" -t "$pane_id")" > "$(pane_contents_file "save" "$pane_id")"
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
save_shell_history() {
|
|
148
|
+
if [ "$pane_command" = "bash" ]; then
|
|
149
|
+
local history_w='history -w'
|
|
150
|
+
local history_r='history -r'
|
|
151
|
+
local accept_line='C-m'
|
|
152
|
+
local end_of_line='C-e'
|
|
153
|
+
local backward_kill_line='C-u'
|
|
154
|
+
elif [ "$pane_command" = "zsh" ]; then
|
|
155
|
+
# fc -W does not work with -L
|
|
156
|
+
# fc -l format is different from what's written by fc -W
|
|
157
|
+
# fc -R either reads the format produced by fc -W or considers
|
|
158
|
+
# the entire line to be a command. That's why we need -n.
|
|
159
|
+
# fc -l only list the last 16 items by default, I think 64 is more reasonable.
|
|
160
|
+
local history_w='fc -lLn -64 >'
|
|
161
|
+
local history_r='fc -R'
|
|
162
|
+
|
|
163
|
+
local zsh_bindkey="$(zsh -i -c bindkey)"
|
|
164
|
+
local accept_line="$(expr "$(echo "$zsh_bindkey" | grep -m1 '\saccept-line$')" : '^"\(.*\)".*')"
|
|
165
|
+
local end_of_line="$(expr "$(echo "$zsh_bindkey" | grep -m1 '\send-of-line$')" : '^"\(.*\)".*')"
|
|
166
|
+
local backward_kill_line="$(expr "$(echo "$zsh_bindkey" | grep -m1 '\sbackward-kill-line$')" : '^"\(.*\)".*')"
|
|
167
|
+
else
|
|
168
|
+
return
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
local pane_id="$1"
|
|
172
|
+
local pane_command="$2"
|
|
173
|
+
local full_command="$3"
|
|
174
|
+
if [ "$full_command" = ":" ]; then
|
|
175
|
+
# leading space prevents the command from being saved to history
|
|
176
|
+
# (assuming default HISTCONTROL settings)
|
|
177
|
+
local write_command=" $history_w '$(resurrect_history_file "$pane_id" "$pane_command")'"
|
|
178
|
+
local read_command=" $history_r '$(resurrect_history_file "$pane_id" "$pane_command")'"
|
|
179
|
+
# C-e C-u is a Bash shortcut sequence to clear whole line. It is necessary to
|
|
180
|
+
# delete any pending input so it does not interfere with our history command.
|
|
181
|
+
tmux send-keys -t "$pane_id" "$end_of_line" "$backward_kill_line" "$write_command" "$accept_line"
|
|
182
|
+
# Immediately restore after saving
|
|
183
|
+
tmux send-keys -t "$pane_id" "$end_of_line" "$backward_kill_line" "$read_command" "$accept_line"
|
|
184
|
+
fi
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
get_active_window_index() {
|
|
188
|
+
local session_name="$1"
|
|
189
|
+
tmux list-windows -t "$session_name" -F "#{window_flags} #{window_index}" |
|
|
190
|
+
awk '$1 ~ /\*/ { print $2; }'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
get_alternate_window_index() {
|
|
194
|
+
local session_name="$1"
|
|
195
|
+
tmux list-windows -t "$session_name" -F "#{window_flags} #{window_index}" |
|
|
196
|
+
awk '$1 ~ /-/ { print $2; }'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
dump_grouped_sessions() {
|
|
200
|
+
local current_session_group=""
|
|
201
|
+
local original_session
|
|
202
|
+
tmux list-sessions -F "$(grouped_sessions_format)" |
|
|
203
|
+
grep "^1" |
|
|
204
|
+
cut -c 3- |
|
|
205
|
+
sort |
|
|
206
|
+
while IFS=$d read session_group session_id session_name; do
|
|
207
|
+
if [ "$session_group" != "$current_session_group" ]; then
|
|
208
|
+
# this session is the original/first session in the group
|
|
209
|
+
original_session="$session_name"
|
|
210
|
+
current_session_group="$session_group"
|
|
211
|
+
else
|
|
212
|
+
# this session "points" to the original session
|
|
213
|
+
active_window_index="$(get_active_window_index "$session_name")"
|
|
214
|
+
alternate_window_index="$(get_alternate_window_index "$session_name")"
|
|
215
|
+
echo "grouped_session${d}${session_name}${d}${original_session}${d}:${alternate_window_index}${d}:${active_window_index}"
|
|
216
|
+
fi
|
|
217
|
+
done
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fetch_and_dump_grouped_sessions(){
|
|
221
|
+
local grouped_sessions_dump="$(dump_grouped_sessions)"
|
|
222
|
+
get_grouped_sessions "$grouped_sessions_dump"
|
|
223
|
+
if [ -n "$grouped_sessions_dump" ]; then
|
|
224
|
+
echo "$grouped_sessions_dump"
|
|
225
|
+
fi
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# translates pane pid to process command running inside a pane
|
|
229
|
+
dump_panes() {
|
|
230
|
+
local full_command
|
|
231
|
+
dump_panes_raw |
|
|
232
|
+
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
|
|
233
|
+
# not saving panes from grouped sessions
|
|
234
|
+
if is_session_grouped "$session_name"; then
|
|
235
|
+
continue
|
|
236
|
+
fi
|
|
237
|
+
full_command="$(pane_full_command $pane_pid)"
|
|
238
|
+
dir=$(echo $dir | sed 's/ /\\ /') # escape all spaces in directory path
|
|
239
|
+
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}"
|
|
240
|
+
done
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
dump_windows() {
|
|
244
|
+
dump_windows_raw |
|
|
245
|
+
while IFS=$d read line_type session_name window_index window_name window_active window_flags window_layout; do
|
|
246
|
+
# not saving windows from grouped sessions
|
|
247
|
+
if is_session_grouped "$session_name"; then
|
|
248
|
+
continue
|
|
249
|
+
fi
|
|
250
|
+
automatic_rename="$(tmux show-window-options -vt "${session_name}:${window_index}" automatic-rename)"
|
|
251
|
+
# If the option was unset, use ":" as a placeholder.
|
|
252
|
+
[ -z "${automatic_rename}" ] && automatic_rename=":"
|
|
253
|
+
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${window_layout}${d}${automatic_rename}"
|
|
254
|
+
done
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
dump_state() {
|
|
258
|
+
tmux display-message -p "$(state_format)"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
dump_pane_contents() {
|
|
262
|
+
local pane_contents_area="$(get_tmux_option "$pane_contents_area_option" "$default_pane_contents_area")"
|
|
263
|
+
dump_panes_raw |
|
|
264
|
+
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
|
|
265
|
+
capture_pane_contents "${session_name}:${window_number}.${pane_index}" "$history_size" "$pane_contents_area"
|
|
266
|
+
done
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
dump_shell_history() {
|
|
270
|
+
dump_panes |
|
|
271
|
+
while IFS=$d read line_type session_name window_number window_active window_flags pane_index dir pane_active pane_command full_command; do
|
|
272
|
+
save_shell_history "$session_name:$window_number.$pane_index" "$pane_command" "$full_command"
|
|
273
|
+
done
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
remove_old_backups() {
|
|
277
|
+
# remove resurrect files older than 30 days (default), but keep at least 5 copies of backup.
|
|
278
|
+
local delete_after="$(get_tmux_option "$delete_backup_after_option" "$default_delete_backup_after")"
|
|
279
|
+
local -a files
|
|
280
|
+
files=($(ls -t $(resurrect_dir)/${RESURRECT_FILE_PREFIX}_*.${RESURRECT_FILE_EXTENSION} | tail -n +6))
|
|
281
|
+
[[ ${#files[@]} -eq 0 ]] ||
|
|
282
|
+
find "${files[@]}" -type f -mtime "+${delete_after}" -exec rm -v "{}" \; > /dev/null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
save_all() {
|
|
286
|
+
local resurrect_file_path="$(resurrect_file_path)"
|
|
287
|
+
local last_resurrect_file="$(last_resurrect_file)"
|
|
288
|
+
mkdir -p "$(resurrect_dir)"
|
|
289
|
+
fetch_and_dump_grouped_sessions > "$resurrect_file_path"
|
|
290
|
+
dump_panes >> "$resurrect_file_path"
|
|
291
|
+
dump_windows >> "$resurrect_file_path"
|
|
292
|
+
dump_state >> "$resurrect_file_path"
|
|
293
|
+
execute_hook "post-save-layout" "$resurrect_file_path"
|
|
294
|
+
if files_differ "$resurrect_file_path" "$last_resurrect_file"; then
|
|
295
|
+
ln -fs "$(basename "$resurrect_file_path")" "$last_resurrect_file"
|
|
296
|
+
else
|
|
297
|
+
rm "$resurrect_file_path"
|
|
298
|
+
fi
|
|
299
|
+
if capture_pane_contents_option_on; then
|
|
300
|
+
mkdir -p "$(pane_contents_dir "save")"
|
|
301
|
+
dump_pane_contents
|
|
302
|
+
pane_contents_create_archive
|
|
303
|
+
rm "$(pane_contents_dir "save")"/*
|
|
304
|
+
fi
|
|
305
|
+
if save_shell_history_option_on; then
|
|
306
|
+
dump_shell_history
|
|
307
|
+
fi
|
|
308
|
+
remove_old_backups
|
|
309
|
+
execute_hook "post-save-all"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
show_output() {
|
|
313
|
+
[ "$SCRIPT_OUTPUT" != "quiet" ]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
main() {
|
|
317
|
+
if supported_tmux_version_ok; then
|
|
318
|
+
if show_output; then
|
|
319
|
+
start_spinner "Saving..." "Tmux environment saved!"
|
|
320
|
+
fi
|
|
321
|
+
save_all
|
|
322
|
+
if show_output; then
|
|
323
|
+
stop_spinner
|
|
324
|
+
display_message "Tmux environment saved!"
|
|
325
|
+
fi
|
|
326
|
+
fi
|
|
327
|
+
}
|
|
328
|
+
main
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# This script shows tmux spinner with a message. It is intended to be running
|
|
4
|
+
# as a background process which should be `kill`ed at the end.
|
|
5
|
+
#
|
|
6
|
+
# Example usage:
|
|
7
|
+
#
|
|
8
|
+
# ./tmux_spinner.sh "Working..." "End message!" &
|
|
9
|
+
# SPINNER_PID=$!
|
|
10
|
+
# ..
|
|
11
|
+
# .. execute commands here
|
|
12
|
+
# ..
|
|
13
|
+
# kill $SPINNER_PID # Stops spinner and displays 'End message!'
|
|
14
|
+
|
|
15
|
+
MESSAGE="$1"
|
|
16
|
+
END_MESSAGE="$2"
|
|
17
|
+
SPIN='-\|/'
|
|
18
|
+
|
|
19
|
+
trap "tmux display-message '$END_MESSAGE'; exit" SIGINT SIGTERM
|
|
20
|
+
|
|
21
|
+
main() {
|
|
22
|
+
local i=0
|
|
23
|
+
while true; do
|
|
24
|
+
i=$(( (i+1) %4 ))
|
|
25
|
+
tmux display-message " ${SPIN:$i:1} $MESSAGE"
|
|
26
|
+
sleep 0.1
|
|
27
|
+
done
|
|
28
|
+
}
|
|
29
|
+
main
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# key bindings
|
|
2
|
+
default_save_key="C-s"
|
|
3
|
+
save_option="@resurrect-save"
|
|
4
|
+
save_path_option="@resurrect-save-script-path"
|
|
5
|
+
|
|
6
|
+
default_restore_key="C-r"
|
|
7
|
+
restore_option="@resurrect-restore"
|
|
8
|
+
restore_path_option="@resurrect-restore-script-path"
|
|
9
|
+
|
|
10
|
+
# default processes that are restored
|
|
11
|
+
default_proc_list_option="@resurrect-default-processes"
|
|
12
|
+
default_proc_list='vi vim view nvim emacs man less more tail top htop irssi weechat mutt'
|
|
13
|
+
|
|
14
|
+
# User defined processes that are restored
|
|
15
|
+
# 'false' - nothing is restored
|
|
16
|
+
# ':all:' - all processes are restored
|
|
17
|
+
#
|
|
18
|
+
# user defined list of programs that are restored:
|
|
19
|
+
# 'my_program foo another_program'
|
|
20
|
+
restore_processes_option="@resurrect-processes"
|
|
21
|
+
restore_processes=""
|
|
22
|
+
|
|
23
|
+
# Defines part of the user variable. Example usage:
|
|
24
|
+
# set -g @resurrect-strategy-vim "session"
|
|
25
|
+
restore_process_strategy_option="@resurrect-strategy-"
|
|
26
|
+
|
|
27
|
+
inline_strategy_token="->"
|
|
28
|
+
inline_strategy_arguments_token="*"
|
|
29
|
+
|
|
30
|
+
save_command_strategy_option="@resurrect-save-command-strategy"
|
|
31
|
+
default_save_command_strategy="ps"
|
|
32
|
+
|
|
33
|
+
# Pane contents capture options.
|
|
34
|
+
# @resurrect-pane-contents-area option can be:
|
|
35
|
+
# 'visible' - capture only the visible pane area
|
|
36
|
+
# 'full' - capture the full pane contents
|
|
37
|
+
pane_contents_option="@resurrect-capture-pane-contents"
|
|
38
|
+
pane_contents_area_option="@resurrect-pane-contents-area"
|
|
39
|
+
default_pane_contents_area="full"
|
|
40
|
+
|
|
41
|
+
bash_history_option="@resurrect-save-bash-history" # deprecated
|
|
42
|
+
shell_history_option="@resurrect-save-shell-history" # deprecated
|
|
43
|
+
|
|
44
|
+
# set to 'on' to ensure panes are never ever overwritten
|
|
45
|
+
overwrite_option="@resurrect-never-overwrite"
|
|
46
|
+
|
|
47
|
+
# Hooks are set via ${hook_prefix}${name}, i.e. "@resurrect-hook-post-save-all"
|
|
48
|
+
hook_prefix="@resurrect-hook-"
|
|
49
|
+
|
|
50
|
+
delete_backup_after_option="@resurrect-delete-backup-after"
|
|
51
|
+
default_delete_backup_after="30" # days
|