aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Cournoyer <maxim.cournoyer@gmail.com>2025-04-26 11:00:51 +0900
committerMaxim Cournoyer <maxim.cournoyer@gmail.com>2025-04-26 20:40:02 +0900
commit2e25cd62cba2ad6edffaceecec89d4a6fce76bd1 (patch)
tree085c3048a9f220f9a6a071e1e733321647d91ebb
parente018fb6f61ad5746e845721e2850ae2f97e328c7 (diff)
downloadguix-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.mk2
-rw-r--r--gnu/packages/emacs-xyz.scm5
-rw-r--r--gnu/packages/patches/emacs-bash-completion-preserve-exit-code.patch131
-rw-r--r--gnu/packages/patches/emacs-bash-completion-preserve-ps1.patch134
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