Skip to content

Commit a96525a

Browse files
committed
fix: Recover $? after completion.
Before this change, the value of $? was lost when doing a completion as it required running a command, so $? became the status code of the completion command. So if you typed: > false > ech<TAB> $? You would get 0 instead of 1, set by false. This change stores the value of $? first thing before executing any command, then have __ebcret restore it. The status code that bash completion, the one that's embedded in the next prompt, remains the status code of the completion command, but $? is the status code of the last user command, before completion was run. issue #77
1 parent a79863f commit a96525a

File tree

2 files changed

+45
-11
lines changed

2 files changed

+45
-11
lines changed

bash-completion.el

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ Bash processes.")
294294
(defconst bash-completion-special-chars "[ -$&-*,:-<>?[-^`{-}]"
295295
"Regexp of characters that must be escaped or quoted.")
296296

297-
(defconst bash-completion--ps1 "'==emacs==ret=$?==.'"
297+
(defconst bash-completion--ps1 "'==emacs==ret=${__ebcret:-$?}==.'"
298298
"Value for the special PS1 prompt set for completions, quoted.")
299299

300300
(eval-when-compile
@@ -1532,12 +1532,12 @@ Return the status code of the command, as a number."
15321532
;; single process, assume __ebcpre is already defined
15331533
((not define-functions)
15341534
(concat
1535-
"if type __ebcpre &>/dev/null; then "
1535+
"__ebcor=$?; if type __ebcpre &>/dev/null; then "
15361536
" __ebcpre; %s; __ebcret $?; "
15371537
"else "
15381538
" echo ==emacs==nopre=${BASH_VERSION}==.; "
1539-
" __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\");"
1540-
" unset PS1 PROMPT_COMMAND;"
1539+
" __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\" $__ebcor);"
1540+
" unset PS1 PROMPT_COMMAND __ebcor;"
15411541
"fi;\n"))
15421542
;; single process, define __ebcpre
15431543
(t
@@ -1549,23 +1549,23 @@ Return the status code of the command, as a number."
15491549
" fi;"
15501550
" history -d $c &>/dev/null || true;"
15511551
"} ; function __ebcret {"
1552-
" __ebcret=t;"
1553-
" return $1;"
1552+
" __ebcret=$1;"
1553+
" return ${__ebcp[2]};"
15541554
"} ; function __ebctrap {"
1555-
" if [[ \"$__ebcret\" = \"t\" && ${#__ebcp[@]} -gt 0 ]]; then"
1555+
" if [[ -n \"$__ebcret\" && ${#__ebcp[@]} -gt 0 ]]; then"
15561556
" PS1=\"${__ebcp[0]}\";"
15571557
" PROMPT_COMMAND=\"${__ebcp[1]}\";"
1558-
" unset __ebcp;"
1559-
" unset __ebcret;"
1558+
" unset __ebcp __ebcret;"
15601559
" fi;"
15611560
"} ; trap __ebctrap DEBUG ; function __ebcpre {"
1561+
" __ebcor=${__ebcor:-$?}; "
15621562
" set +x; set +o emacs; set +o vi;"
15631563
" echo \"==emacs==bash=${BASH_VERSION}==.\";"
15641564
" if [[ ${#__ebcp[@]} = 0 ]]; then "
1565-
" __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\");"
1565+
" __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\" $__ebcor);"
15661566
" fi;"
15671567
" PS1=" bash-completion--ps1 ";"
1568-
" unset PROMPT_COMMAND;"
1568+
" unset PROMPT_COMMAND __ebcor;"
15691569
" __ebcnohistory 1;"
15701570
"} ; { __ebcpre; %s; __ebcret $?; }\n")))
15711571
commandline)))

test/bash-completion-integration-test.el

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,4 +926,38 @@ $ ")))))
926926
(should (equal (bash-completion_test-buffer-string)
927927
"$ dummy dummy\n--$ --\n$ dummy dummy\n--$ --\n$ "))))
928928

929+
(ert-deftest bash-completion-integration-recover-status-code ()
930+
(bash-completion_test-with-shell-harness
931+
(concat ; .bashrc
932+
"function failwith { return $1; }\n"
933+
"function dummy { echo $?; }\n"
934+
"function _dummy {\n"
935+
" COMPREPLY=( dummy )\n"
936+
"}\n"
937+
"complete -F _dummy dummy\n"
938+
"PS1='\$ '")
939+
nil
940+
;; The first time initializes completion, the second time executes
941+
;; an already initialized completion. The two cases behave very
942+
;; differently, so we test both.
943+
(dotimes (i 2)
944+
(bash-completion_test-send (format "failwith %s" (+ 100 i)))
945+
(should (equal
946+
"dummy dummy "
947+
(bash-completion_test-complete "dummy dum")))
948+
(let ((start (line-beginning-position)))
949+
(comint-send-input)
950+
(bash-completion_test-wait-for-prompt start)))
951+
;; The status code printed by the dummy function should be the one
952+
;; from testfail, so 123, and not the one from the completion
953+
;; command executed to do completion for the dummy function.
954+
(should (equal (bash-completion_test-buffer-string)
955+
(concat "$ failwith 100\n"
956+
"$ dummy dummy\n"
957+
"100\n"
958+
"$ failwith 101\n"
959+
"$ dummy dummy\n"
960+
"101\n"
961+
"$ ")))))
962+
929963
;;; bash-completion-integration-test.el ends here

0 commit comments

Comments
 (0)