@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.
@@ -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,8 @@
1
+ start_spinner() {
2
+ $CURRENT_DIR/tmux_spinner.sh "$1" "$2" &
3
+ export SPINNER_PID=$!
4
+ }
5
+
6
+ stop_spinner() {
7
+ kill $SPINNER_PID
8
+ }
@@ -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