22
33# Display help for run_with_exit_prompt
44run_with_exit_prompt_help () {
5- cat << 'EOF '
5+ cat << 'EOF '
66run_with_exit_prompt: show a spinner while running a command, optionally allowing cancel with Esc/ctrl+c.
77
88Usage:
@@ -15,71 +15,133 @@ Flags:
1515 Run the command in the foreground so it can read from the TTY (passphrases, prompts, etc.).
1616 Note: Esc-to-cancel is disabled in interactive mode; use Ctrl+C.
1717
18+ --shell
19+ Run the command in the current shell (no background). Needed for bash builtins / job control:
20+ wait, jobs, fg, bg, disown, cd, export, set, trap, etc.
21+ Note: Esc-to-cancel is disabled in --shell mode; use Ctrl+C.
22+
1823 -h, --help
1924 Show this help.
2025
2126Notes:
2227 - Default (non-interactive) mode runs the command in the background and buffers stdout to a temp file.
2328 This allows Esc-to-cancel, but commands that need stdin will not work properly (use -i).
29+ - If the command is a bash builtin/keyword, run_with_exit_prompt automatically switches to --shell.
30+ Use --pid for cancellable waiting on a child process.
2431 - Use "--" to separate the message from the command if you want.
2532EOF
2633}
2734
2835function run_with_exit_prompt() {
29- local interactive=0
30-
31- # parse flags
32- while [[ $# -gt 0 ]]; do
33- case " $1 " in
34- -i|--interactive) interactive=1; shift ;;
35- -h|--help) run_with_exit_prompt_help; return 0 ;;
36- --) shift ; break ;;
37- * ) break ;;
38- esac
39- done
40-
41- local wait_message=" $1 "
42- shift
43-
44- # interactive path: foreground (stdin preserved)
45- if [[ $interactive -eq 1 ]]; then
46- gum spin --spinner minidot \
47- --title " $wait_message (Ctrl+C to cancel)" \
48- -- " $@ "
49- return $?
50- fi
51-
52- # (rest of your existing non-interactive implementation stays the same,
53- # but make tmpfile local)
54- local tmpfile
55- tmpfile=$( mktemp)
56- " $@ " > " $tmpfile " &
57- local cmd_pid=$!
58- local spinner_pid=" "
59-
60- gum spin --spinner minidot --title " $wait_message (Press Esc or ctrl+c to exit)" -- sleep 999999 >&2 &
61- spinner_pid=$!
62-
63- while kill -0 " $cmd_pid " 2> /dev/null; do
64- if read -rsn1 -t 0.1 key; then
65- if [[ $key == $' \e ' ]]; then
66- printf " \nExiting...\n" >&2
67- kill " $cmd_pid " 2> /dev/null || true
68- [[ -n ${spinner_pid:- } ]] && kill " $spinner_pid " 2> /dev/null || true
69- rm -f " $tmpfile "
70- return 130
71- fi
72- fi
73- done
74-
75- [[ -n ${spinner_pid:- } ]] && kill " $spinner_pid " 2> /dev/null || true
76- [[ -n ${spinner_pid:- } ]] && wait " $spinner_pid " 2> /dev/null || true
77-
78- if wait " $cmd_pid " ; then
79- cat " $tmpfile "
80- rm -f " $tmpfile "
81- else
82- rm -f " $tmpfile "
83- return 1
84- fi
36+ local interactive=0
37+ local shell_mode=0
38+
39+ # parse flags
40+ # # -- separates flags from positional args
41+ while [[ $# -gt 0 ]]; do
42+ case " $1 " in
43+ -i | --interactive)
44+ interactive=1
45+ shift
46+ ;;
47+ --shell)
48+ shell_mode=1
49+ shift
50+ ;;
51+ -h | --help)
52+ run_with_exit_prompt_help
53+ return 0
54+ ;;
55+ --)
56+ shift
57+ break
58+ ;;
59+ * ) break ;;
60+ esac
61+ done
62+
63+ # ensure we have at least 2 args left: message + command
64+ local wait_message=" $1 "
65+ shift
66+
67+ # If the command is a bash builtin/keyword, it must run in this shell.
68+ if [[ $# -gt 0 ]] && [[ $shell_mode -eq 0 ]]; then
69+ local t
70+ t=$( type -t -- " $1 " 2> /dev/null || true)
71+ if [[ " $t " == " builtin" || " $t " == " keyword" ]]; then
72+ shell_mode=1
73+ fi
74+ fi
75+
76+ # same-shell path: foreground (stdin preserved)
77+ # Ctrl+C to cancel; Esc-to-cancel disabled
78+ if [[ $shell_mode -eq 1 ]]; then
79+ local spinner_pid=" "
80+ gum spin --spinner minidot \
81+ --title " $wait_message (Ctrl+C to cancel)" \
82+ -- sleep 999999 >&2 &
83+ spinner_pid=$!
84+
85+ " $@ "
86+ local rc=$?
87+
88+ kill " $spinner_pid " 2> /dev/null || true
89+ wait " $spinner_pid " 2> /dev/null || true
90+ return $rc
91+ fi
92+
93+ # interactive path: foreground (stdin preserved)
94+ # Ctrl+C to cancel; Esc-to-cancel disabled
95+ if [[ $interactive -eq 1 ]]; then
96+ gum spin --spinner minidot \
97+ --title " $wait_message (Ctrl+C to cancel)" \
98+ -- " $@ " # Run command in foreground with spinner
99+ return $? # Return command exit status
100+ fi
101+
102+ # Non-interactive path: background (stdin not preserved)
103+ # Esc-to-cancel enabled
104+ # Buffer stdout to temp file to avoid interleaving with spinner
105+ local tmpfile
106+ tmpfile=$( mktemp) # safely create unique temp file
107+ " $@ " > " $tmpfile " & # Run command in background, redirecting stdout to temp file
108+ local cmd_pid=$! # Capture command PID
109+ local spinner_pid=" " # Initialize spinner PID variable
110+
111+ # Start spinner in background redirected to stderr (to avoid mixing with command output)
112+ gum spin --spinner minidot --title " $wait_message (Press Esc or ctrl+c to exit)" -- sleep 999999 >&2 &
113+ spinner_pid=$! # Capture spinner PID
114+
115+ # Loop to check if command is done, and read for Esc keypress to kill command if needed
116+ while kill -0 " $cmd_pid " 2> /dev/null; do
117+ # -r: raw mode (no echo), to avoid interpreting backslashes
118+ # -s: silent (no echo), do not print input to terminal
119+ # -n1: read 1 character
120+ # -t 0.1: timeout after 0.1 seconds, so we can loop and check if command is done
121+ local key=" "
122+ if read -rsn1 -t 0.1 key; then
123+ if [[ $key == $' \e ' ]]; then # Esc key pressed
124+ printf " \nExiting...\n" >&2
125+ kill " $cmd_pid " 2> /dev/null || true
126+ [[ -n ${spinner_pid:- } ]] && kill " $spinner_pid " 2> /dev/null || true
127+ rm -f " $tmpfile "
128+ return 130
129+ fi
130+ fi
131+ done
132+
133+ # Clean up spinner after command completes
134+ [[ -n ${spinner_pid:- } ]] && kill " $spinner_pid " 2> /dev/null || true
135+ [[ -n ${spinner_pid:- } ]] && wait " $spinner_pid " 2> /dev/null || true
136+
137+ # Check command exit status
138+ if wait " $cmd_pid " ; then
139+ # On success, output buffered stdout
140+ cat " $tmpfile "
141+ rm -f " $tmpfile "
142+ else
143+ # On failure, clean up and return error
144+ rm -f " $tmpfile "
145+ return 1
146+ fi
85147}
0 commit comments