diff options
author | Maxim Cournoyer <maxim.cournoyer@gmail.com> | 2025-04-26 11:00:51 +0900 |
---|---|---|
committer | Maxim Cournoyer <maxim.cournoyer@gmail.com> | 2025-04-26 20:40:02 +0900 |
commit | 2e25cd62cba2ad6edffaceecec89d4a6fce76bd1 (patch) | |
tree | 085c3048a9f220f9a6a071e1e733321647d91ebb | |
parent | e018fb6f61ad5746e845721e2850ae2f97e328c7 (diff) | |
download | guix-2e25cd62cba2ad6edffaceecec89d4a6fce76bd1.tar.gz guix-2e25cd62cba2ad6edffaceecec89d4a6fce76bd1.zip |
gnu: emacs-bash-completion: Preserve PS1 prompt, exit code.
* gnu/packages/patches/emacs-bash-completion-preserve-exit-code.patch: New patch.
* gnu/packages/patches/emacs-bash-completion-preserve-ps1.patch: Likewise.
* gnu/local.mk (dist_patch_DATA): Register them.
* gnu/packages/emacs-xyz.scm (emacs-bash-completion) [source]: Apply them.
Change-Id: Id179f4ee976efdbe52f05dcb0919ad586dc8bcad
-rw-r--r-- | gnu/local.mk | 2 | ||||
-rw-r--r-- | gnu/packages/emacs-xyz.scm | 5 | ||||
-rw-r--r-- | gnu/packages/patches/emacs-bash-completion-preserve-exit-code.patch | 131 | ||||
-rw-r--r-- | gnu/packages/patches/emacs-bash-completion-preserve-ps1.patch | 134 |
4 files changed, 271 insertions, 1 deletions
diff --git a/gnu/local.mk b/gnu/local.mk index c4bc084b7e..3f5e4cec38 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -1216,6 +1216,8 @@ dist_patch_DATA = \ %D%/packages/patches/elm-offline-package-registry.patch \ %D%/packages/patches/elm-reactor-static-files.patch \ %D%/packages/patches/emacs-all-the-icons-remove-duplicate-rs.patch \ + %D%/packages/patches/emacs-bash-completion-preserve-exit-code.patch \ + %D%/packages/patches/emacs-bash-completion-preserve-ps1.patch \ %D%/packages/patches/emacs-deferred-fix-number-of-arguments.patch \ %D%/packages/patches/emacs-elpy-dup-test-name.patch \ %D%/packages/patches/emacs-disable-jit-compilation.patch \ diff --git a/gnu/packages/emacs-xyz.scm b/gnu/packages/emacs-xyz.scm index ca21a1a5f1..6d10fb7737 100644 --- a/gnu/packages/emacs-xyz.scm +++ b/gnu/packages/emacs-xyz.scm @@ -24356,7 +24356,10 @@ Slack client.") (commit version))) (file-name (git-file-name name version)) (sha256 - (base32 "0664dihdfvrbxqxy00fw0skdg454njm673ip54qrgkh38vyv5432")))) + (base32 "0664dihdfvrbxqxy00fw0skdg454njm673ip54qrgkh38vyv5432")) + (patches + (search-patches "emacs-bash-completion-preserve-ps1.patch" + "emacs-bash-completion-preserve-exit-code.patch")))) (build-system emacs-build-system) (arguments (list diff --git a/gnu/packages/patches/emacs-bash-completion-preserve-exit-code.patch b/gnu/packages/patches/emacs-bash-completion-preserve-exit-code.patch new file mode 100644 index 0000000000..a64f80bb8d --- /dev/null +++ b/gnu/packages/patches/emacs-bash-completion-preserve-exit-code.patch @@ -0,0 +1,131 @@ +From a96525afd9077c06d781c59e78bfc6620e41be8f Mon Sep 17 00:00:00 2001 +From: Stephane Zermatten <szermatt@gmx.net> +Date: Fri, 25 Apr 2025 18:08:01 +0300 +Subject: [PATCH] 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 +--- + bash-completion.el | 22 +++++++-------- + test/bash-completion-integration-test.el | 34 ++++++++++++++++++++++++ + 2 files changed, 45 insertions(+), 11 deletions(-) + +diff --git a/bash-completion.el b/bash-completion.el +index 130152f..5a7d9ff 100644 +--- a/bash-completion.el ++++ b/bash-completion.el +@@ -294,7 +294,7 @@ Bash processes.") + (defconst bash-completion-special-chars "[ -$&-*,:-<>?[-^`{-}]" + "Regexp of characters that must be escaped or quoted.") + +-(defconst bash-completion--ps1 "'==emacs==ret=$?==.'" ++(defconst bash-completion--ps1 "'==emacs==ret=${__ebcret:-$?}==.'" + "Value for the special PS1 prompt set for completions, quoted.") + + (eval-when-compile +@@ -1532,12 +1532,12 @@ Return the status code of the command, as a number." + ;; single process, assume __ebcpre is already defined + ((not define-functions) + (concat +- "if type __ebcpre &>/dev/null; then " ++ "__ebcor=$?; if type __ebcpre &>/dev/null; then " + " __ebcpre; %s; __ebcret $?; " + "else " + " echo ==emacs==nopre=${BASH_VERSION}==.; " +- " __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\");" +- " unset PS1 PROMPT_COMMAND;" ++ " __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\" $__ebcor);" ++ " unset PS1 PROMPT_COMMAND __ebcor;" + "fi;\n")) + ;; single process, define __ebcpre + (t +@@ -1549,23 +1549,23 @@ Return the status code of the command, as a number." + " fi;" + " history -d $c &>/dev/null || true;" + "} ; function __ebcret {" +- " __ebcret=t;" +- " return $1;" ++ " __ebcret=$1;" ++ " return ${__ebcp[2]};" + "} ; function __ebctrap {" +- " if [[ \"$__ebcret\" = \"t\" && ${#__ebcp[@]} -gt 0 ]]; then" ++ " if [[ -n \"$__ebcret\" && ${#__ebcp[@]} -gt 0 ]]; then" + " PS1=\"${__ebcp[0]}\";" + " PROMPT_COMMAND=\"${__ebcp[1]}\";" +- " unset __ebcp;" +- " unset __ebcret;" ++ " unset __ebcp __ebcret;" + " fi;" + "} ; trap __ebctrap DEBUG ; function __ebcpre {" ++ " __ebcor=${__ebcor:-$?}; " + " set +x; set +o emacs; set +o vi;" + " echo \"==emacs==bash=${BASH_VERSION}==.\";" + " if [[ ${#__ebcp[@]} = 0 ]]; then " +- " __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\");" ++ " __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\" $__ebcor);" + " fi;" + " PS1=" bash-completion--ps1 ";" +- " unset PROMPT_COMMAND;" ++ " unset PROMPT_COMMAND __ebcor;" + " __ebcnohistory 1;" + "} ; { __ebcpre; %s; __ebcret $?; }\n"))) + commandline))) +diff --git a/test/bash-completion-integration-test.el b/test/bash-completion-integration-test.el +index f57ddc7..02c24ec 100644 +--- a/test/bash-completion-integration-test.el ++++ b/test/bash-completion-integration-test.el +@@ -926,4 +926,38 @@ $ "))))) + (should (equal (bash-completion_test-buffer-string) + "$ dummy dummy\n--$ --\n$ dummy dummy\n--$ --\n$ ")))) + ++(ert-deftest bash-completion-integration-recover-status-code () ++ (bash-completion_test-with-shell-harness ++ (concat ; .bashrc ++ "function failwith { return $1; }\n" ++ "function dummy { echo $?; }\n" ++ "function _dummy {\n" ++ " COMPREPLY=( dummy )\n" ++ "}\n" ++ "complete -F _dummy dummy\n" ++ "PS1='\$ '") ++ nil ++ ;; The first time initializes completion, the second time executes ++ ;; an already initialized completion. The two cases behave very ++ ;; differently, so we test both. ++ (dotimes (i 2) ++ (bash-completion_test-send (format "failwith %s" (+ 100 i))) ++ (should (equal ++ "dummy dummy " ++ (bash-completion_test-complete "dummy dum"))) ++ (let ((start (line-beginning-position))) ++ (comint-send-input) ++ (bash-completion_test-wait-for-prompt start))) ++ ;; The status code printed by the dummy function should be the one ++ ;; from testfail, so 123, and not the one from the completion ++ ;; command executed to do completion for the dummy function. ++ (should (equal (bash-completion_test-buffer-string) ++ (concat "$ failwith 100\n" ++ "$ dummy dummy\n" ++ "100\n" ++ "$ failwith 101\n" ++ "$ dummy dummy\n" ++ "101\n" ++ "$ "))))) ++ + ;;; bash-completion-integration-test.el ends here diff --git a/gnu/packages/patches/emacs-bash-completion-preserve-ps1.patch b/gnu/packages/patches/emacs-bash-completion-preserve-ps1.patch new file mode 100644 index 0000000000..0320566f4c --- /dev/null +++ b/gnu/packages/patches/emacs-bash-completion-preserve-ps1.patch @@ -0,0 +1,134 @@ +From a79863f9c2080d450aa63dbda872b8ccee8ac790 Mon Sep 17 00:00:00 2001 +From: Stephane Zermatten <szermatt@gmx.net> +Date: Fri, 25 Apr 2025 18:08:54 +0300 +Subject: [PATCH] fix: Recover PS1 before command execution. + +Before this change, when doing completion in the same process PS1 was +modified to be able to detect the end of the completion command output +and it was recovered later on from PROMPT_COMMAND, just *after* +executing the user command, before building the next prompt. + +The effect was not visible, unless the user command included $PS1, so if +you did: + +> echo $PS1 + +and ran completion while editing that command, the PS1 that would be +output would be the one from bash-completion. + +This change switches to another approach for recovering the prompt that +allows recovering it before executing the user command: a DEBUG trap is +registered which restores PS1 and PROMPT_COMMAND if the previous command +included __ebrcet. This change also adds __ebcret after the last command +issued by completion. + +This way, the last command issued by completion uses the fake prompt, +but the command run just after that sees the real prompt in its +variable. + +issue #77 +--- + bash-completion.el | 34 +++++++++++------------- + test/bash-completion-integration-test.el | 26 ++++++++++++++++++ + 2 files changed, 42 insertions(+), 18 deletions(-) + +diff --git a/bash-completion.el b/bash-completion.el +index e3bebeb..130152f 100644 +--- a/bash-completion.el ++++ b/bash-completion.el +@@ -1533,7 +1533,7 @@ Return the status code of the command, as a number." + ((not define-functions) + (concat + "if type __ebcpre &>/dev/null; then " +- " __ebcpre; %s; " ++ " __ebcpre; %s; __ebcret $?; " + "else " + " echo ==emacs==nopre=${BASH_VERSION}==.; " + " __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\");" +@@ -1548,28 +1548,26 @@ Return the status code of the command, as a number." + " c=$((c+1));" + " fi;" + " history -d $c &>/dev/null || true;" +- "}; function __ebcpre {" ++ "} ; function __ebcret {" ++ " __ebcret=t;" ++ " return $1;" ++ "} ; function __ebctrap {" ++ " if [[ \"$__ebcret\" = \"t\" && ${#__ebcp[@]} -gt 0 ]]; then" ++ " PS1=\"${__ebcp[0]}\";" ++ " PROMPT_COMMAND=\"${__ebcp[1]}\";" ++ " unset __ebcp;" ++ " unset __ebcret;" ++ " fi;" ++ "} ; trap __ebctrap DEBUG ; function __ebcpre {" + " set +x; set +o emacs; set +o vi;" + " echo \"==emacs==bash=${BASH_VERSION}==.\";" + " if [[ ${#__ebcp[@]} = 0 ]]; then " + " __ebcp=(\"$PS1\" \"$PROMPT_COMMAND\");" + " fi;" +- " PROMPT_COMMAND=" ;; set a temporary prompt +- (bash-completion-quote +- (concat "PS1=" bash-completion--ps1 ";" +- "PROMPT_COMMAND=" ;; recover prompt +- (bash-completion-quote +- (concat +- "__ebcr=$?;" +- "PS1=\"${__ebcp[0]}\";" +- "PROMPT_COMMAND=\"${__ebcp[1]}\";" +- "unset __ebcp;" +- "if [[ -n \"$PROMPT_COMMAND\" ]]; then" +- " (exit $__ebcr); eval \"$PROMPT_COMMAND\";" +- "fi;")))) +- ";" ++ " PS1=" bash-completion--ps1 ";" ++ " unset PROMPT_COMMAND;" + " __ebcnohistory 1;" +- "} && { __ebcpre; %s; }\n"))) ++ "} ; { __ebcpre; %s; __ebcret $?; }\n"))) + commandline))) + (setq bash-completion--debug-info + (list (cons 'commandline complete-command) +@@ -1591,7 +1589,7 @@ Return the status code of the command, as a number." + ;; common initialization, then retry. + (bash-completion-send "__ebcnohistory" process timeout debug-context 'define-functions) + (bash-completion--setup-bash-common process) +- (funcall send-string process (concat "__ebcpre; " commandline ";\n")) ++ (funcall send-string process (concat "__ebcpre; " commandline "; __ebcret $?\n")) + (bash-completion--wait-for-regexp + "short-timeout" process "==emacs==bash=[0-9].*?==." + bash-completion-short-command-timeout)) +diff --git a/test/bash-completion-integration-test.el b/test/bash-completion-integration-test.el +index e227165..f57ddc7 100644 +--- a/test/bash-completion-integration-test.el ++++ b/test/bash-completion-integration-test.el +@@ -900,4 +900,30 @@ $ "))))) + "dummy moretestfile " + (bash-completion_test-complete "dummy moret"))))) + ++(ert-deftest bash-completion-integration-recover-status-ps1 () ++ (bash-completion_test-with-shell-harness ++ (concat ; .bashrc ++ "function dummy { echo --$PS1--; }\n" ++ "function _dummy {\n" ++ " COMPREPLY=( dummy )\n" ++ "}\n" ++ "complete -F _dummy dummy\n" ++ "PS1='$ '") ++ nil ++ ;; The first time initializes completion, the second time executes ++ ;; an already initialized completion. The two cases behave very ++ ;; differently, so we test both. ++ (dotimes (i 2) ++ (should (equal ++ "dummy dummy " ++ (bash-completion_test-complete "dummy dum"))) ++ (let ((start (line-beginning-position))) ++ (comint-send-input) ++ (bash-completion_test-wait-for-prompt start))) ++ ++ ;; The PS1 printed by the dummy function should be the one set in ++ ;; the init section, and not the one set by bash completion. ++ (should (equal (bash-completion_test-buffer-string) ++ "$ dummy dummy\n--$ --\n$ dummy dummy\n--$ --\n$ ")))) ++ + ;;; bash-completion-integration-test.el ends here |