[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/bash-completion d99ad71050 199/313: Add a new option to di
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/bash-completion d99ad71050 199/313: Add a new option to disable the use of separate processes to perform completion |
Date: |
Sat, 3 Dec 2022 10:59:30 -0500 (EST) |
branch: elpa/bash-completion
commit d99ad7105059dd4211dd89b009f79333a086c12c
Author: montag451 <montag451@laposte.net>
Commit: montag451 <montag451@laposte.net>
Add a new option to disable the use of separate processes to perform
completion
The drawback of using separate processes to do the completion is that
the completion process is not aware of any changes made to bash in the
current buffer. Setting the new option
`bash-completion-use-separate-processes' to nil, remove this
limitation by using the bash associated with the current buffer to
perform the completion.
---
README.md | 14 ++-
bash-completion.el | 274 +++++++++++++++++++++++++++++++++++++----------------
2 files changed, 203 insertions(+), 85 deletions(-)
diff --git a/README.md b/README.md
index 1d8ee49772..ae3dbbd1be 100644
--- a/README.md
+++ b/README.md
@@ -21,15 +21,17 @@ running in term mode. Also, term mode is not available in
shell-command prompts.
Bash completion can also be run programatically, outside of a
-shell-mode command, by calling
-`bash-completion-dynamic-complete-nocomint'
+shell-mode command, by setting
+`bash-completion-use-separate-processes` to a non-nil value (which is
+the default) and by calling
+`bash-completion-dynamic-complete-nocomint`.
## INSTALLATION
1. copy bash-completion.el into a directory that's on Emacs load-path
2. add this into your .emacs file:
- (autoload 'bash-completion-dynamic-complete
+ (autoload 'bash-completion-dynamic-complete
"bash-completion"
"BASH completion hook")
(add-hook 'shell-dynamic-complete-functions
@@ -56,6 +58,9 @@ and then adding this to your .bashrc:
. /etc/bash_completion
+NOTE: The following paragraph are not relevant when
+`bash-completion-use-separate-processes` is nil.
+
Right after enabling programmable bash completion, and whenever you
make changes to you .bashrc, call `bash-completion-reset' to make
sure bash completion takes your new settings into account.
@@ -78,6 +83,9 @@ the environment variable EMACS_BASH_COMPLETE set to t.
## CAVEATS
+NOTE: This section is not relevant when
+`bash-completion-use-separate-processes` is nil.
+
Using a separate process for doing the completion has several
important disadvantages:
diff --git a/bash-completion.el b/bash-completion.el
index eb93f99876..881810df2c 100644
--- a/bash-completion.el
+++ b/bash-completion.el
@@ -145,12 +145,21 @@ BASH completion is only available in the environment for
which
:type '(boolean)
:group 'bash-completion)
+(defcustom bash-completion-use-separate-processes t
+ "Enable/disable the use of separate processes to do the completion."
+ :type 'boolean
+ :group 'bash-completion)
+
(defcustom bash-completion-prog (executable-find "bash")
"Name or path of the BASH executable to run for command-line completion.
+
This should be either an absolute path to the BASH executable or
the name of the bash command if it is on Emacs' PATH. This should
point to a recent version of BASH, 3 or 4, with support for
-command-line completion."
+command-line completion.
+
+This variable is not used if
+`bash-completion-use-separate-processes' is nil."
:type '(file :must-match t)
:group 'bash-completion)
@@ -159,17 +168,24 @@ command-line completion."
This is the path of an BASH executable available on the remote machine.
Best is to just specify \"bash\" and rely on the PATH being set correctly
-for the remote connection."
+for the remote connection.
+
+This variable is not used if
+`bash-completion-use-separate-processes' is nil."
:type '(string)
:group 'bash-completion)
(defcustom bash-completion-args '("--noediting")
- "Args passed to the BASH shell."
+ "Args passed to the BASH shell.
+
+This variable is not used if
+`bash-completion-use-separate-processes' is nil."
:type '(repeat (string :tag "Argument"))
:group 'bash-completion)
(defcustom bash-completion-process-timeout 2.5
"Number of seconds to wait for an answer from bash.
+
If bash takes longer than that to answer, the answer will be
ignored."
:type '(float)
@@ -189,8 +205,12 @@ Only relevant when using bash completion in a shell,
through
(defcustom bash-completion-initial-timeout 30
"Timeout value to apply when talking to bash for the first time.
+
The first thing bash is supposed to do is process /etc/bash_complete,
-which typically takes a long time."
+which typically takes a long time.
+
+This variable is not used if
+`bash-completion-use-separate-processes' is nil."
:type '(float)
:group 'bash-completion)
@@ -209,11 +229,19 @@ to remove the extra space bash adds after a completion."
(defvar bash-completion-start-files
'("~/.emacs_bash.sh" "~/.emacs.d/init_bash.sh")
- "Shell files that, if they exist, will be sourced at the
-beginning of a bash completion subprocess.")
+ "Shell files that, if they exist, will be sourced at the beginning of a bash
completion subprocess.
+
+This variable is not used if
+`bash-completion-use-separate-processes' is nil.")
(defvar bash-completion-wordbreaks ""
- "Extra wordbreaks to use when tokenizing, in `bash-completion-tokenize'")
+ "Extra wordbreaks to use when tokenizing, in `bash-completion-tokenize'.")
+
+(defvar bash-completion-output-buffer " *bash-completion*"
+ "Buffer storing completion results.
+
+This variable is not used when
+`bash-completion-use-separate-processes' is non-nil.")
;;; ---------- Internal variables and constants
@@ -221,7 +249,10 @@ beginning of a bash completion subprocess.")
"Bash processes alist.
Mapping between remote paths as returned by `file-remote-p' and
-Bash processes")
+Bash processes.
+
+This variable is not used if
+`bash-completion-use-separate-processes' is nil.")
(defconst bash-completion-special-chars "[^-0-9a-zA-Z_./\n=]"
"Regexp of characters that must be escaped or quoted.")
@@ -289,6 +320,77 @@ The option can be:
"Return the current command for the completion, if there is one."
(file-name-nondirectory (car (bash-completion--words comp))))
+(defun bash-completion--get-buffer (process)
+ "Return the buffer used to store completion results.
+
+PROCESS is the bash process from which completions are
+retrieved. When `bash-completion-use-separate-processes' is nil,
+PROCESS is not used and `bash-completion-output-buffer' is
+returned."
+ (if bash-completion-use-separate-processes
+ (process-buffer process)
+ (get-buffer-create bash-completion-output-buffer)))
+
+(defun bash-completion--setup-bash-common (process)
+ "Setup PROCESS to be ready for completion."
+ (let (bash-major-version)
+ (bash-completion-send "complete -p" process)
+ (process-put process 'complete-p
+ (bash-completion-build-alist (bash-completion--get-buffer
process)))
+ (bash-completion-send "echo -n ${BASH_VERSINFO[0]}" process)
+ (setq bash-major-version
+ (with-current-buffer (bash-completion--get-buffer process)
+ (string-to-number (buffer-substring-no-properties
+ (point-min) (point-max)))))
+ (bash-completion-send
+ (concat "function __bash_complete_wrapper {"
+ (if (>= bash-major-version 4)
+ " COMP_TYPE=9; COMP_KEY=9; _EMACS_COMPOPT=\"\";"
+ "")
+ " eval $__BASH_COMPLETE_WRAPPER;"
+ " n=$?;"
+ " if [[ $n = 124 ]]; then"
+ (bash-completion--side-channel-data
+ "wrapped-status" "124")
+ " return 1; "
+ " fi; "
+ (when (>= bash-major-version 4)
+ (concat " if [[ -n \"${_EMACS_COMPOPT}\" ]]; then"
+ (bash-completion--side-channel-data
+ "compopt" "${_EMACS_COMPOPT}")
+ " fi;"))
+ " return $n;"
+ "}")
+ process)
+ (if (>= bash-major-version 4)
+ (bash-completion-send
+ (concat
+ "function compopt {"
+ " command compopt \"$@\" 2>/dev/null;"
+ " ret=$?; "
+ " if [[ $ret == 1 && \"$*\" = *\"-o nospace\"* ]]; then"
+ " _EMACS_COMPOPT='-o nospace';"
+ " return 0;"
+ " fi;"
+ " if [[ $ret == 1 && \"$*\" = *\"+o nospace\"* ]]; then"
+ " _EMACS_COMPOPT='+o nospace';"
+ " return 0;"
+ " fi;"
+ " return $ret; "
+ "}")
+ process))
+ (bash-completion-send "echo -n ${COMP_WORDBREAKS}" process)
+ (process-put process 'wordbreaks
+ (with-current-buffer (bash-completion--get-buffer process)
+ (buffer-substring-no-properties
+ (point-min) (point-max))))
+ (process-put process 'bash-major-version bash-major-version)))
+
+(defun bash-completion--output-filter (output)
+ (with-current-buffer (bash-completion--get-buffer nil)
+ (insert output)
+ ""))
+
;;; ---------- Inline functions
(defsubst bash-completion-tokenize-get-range (token)
@@ -695,7 +797,7 @@ up the completion environment (COMP_LINE, COMP_POINT,
COMP_WORDS,
COMP_CWORD) and calls compgen.
The result is a list of candidates, which might be empty."
- (let* ((buffer (process-buffer process))
+ (let* ((buffer (bash-completion--get-buffer process))
(completion-status))
(setq completion-status (bash-completion-send
(bash-completion-generate-line comp) process))
(when (eq 124 completion-status)
@@ -903,7 +1005,7 @@ Return a CONS containing (before . after)."
;;; ---------- Functions: bash subprocess
-(defun bash-completion-require-process ()
+(defun bash-completion--require-separate-process ()
"Return the bash completion process or start it.
If a bash completion process is already running, return it.
@@ -930,7 +1032,7 @@ is set to t."
(if (bash-completion-is-running)
(cdr (assoc remote bash-completion-processes))
;; start process
- (let ((process) (oldterm (getenv "TERM")) (cleanup t)
(bash-major-version))
+ (let (process (oldterm (getenv "TERM")) (cleanup t))
(unwind-protect
(progn
(setenv "TERM" "dumb")
@@ -984,59 +1086,8 @@ is set to t."
;; business to get a saner way of handling spaces.
;; Noticed in bash_completion v1.872.
"function quote_readline { echo \"$1\"; }\n"))
-
(bash-completion-send "PROMPT_COMMAND='';PS1='\t$?\v'" process
bash-completion-initial-timeout)
- (bash-completion-send "complete -p" process)
- (process-put process 'complete-p
- (bash-completion-build-alist (process-buffer
process)))
- (bash-completion-send "echo -n ${BASH_VERSINFO[0]}" process)
- (setq bash-major-version
- (with-current-buffer (process-buffer process)
- (string-to-number (buffer-substring-no-properties
- (point-min) (point-max)))))
- (bash-completion-send
- (concat "function __bash_complete_wrapper {"
- (if (>= bash-major-version 4)
- " COMP_TYPE=9; COMP_KEY=9; _EMACS_COMPOPT=\"\";"
- "")
- " eval $__BASH_COMPLETE_WRAPPER;"
- " n=$?;"
- " if [[ $n = 124 ]]; then"
- (bash-completion--side-channel-data
- "wrapped-status" "124")
- " return 1; "
- " fi; "
- (when (>= bash-major-version 4)
- (concat " if [[ -n \"${_EMACS_COMPOPT}\" ]]; then"
- (bash-completion--side-channel-data
- "compopt" "${_EMACS_COMPOPT}")
- " fi;"))
- " return $n;"
- "}")
- process)
- (if (>= bash-major-version 4)
- (bash-completion-send
- (concat
- "function compopt {"
- " command compopt \"$@\" 2>/dev/null;"
- " ret=$?; "
- " if [[ $ret == 1 && \"$*\" = *\"-o nospace\"* ]]; then"
- " _EMACS_COMPOPT='-o nospace';"
- " return 0;"
- " fi;"
- " if [[ $ret == 1 && \"$*\" = *\"+o nospace\"* ]]; then"
- " _EMACS_COMPOPT='+o nospace';"
- " return 0;"
- " fi;"
- " return $ret; "
- "}")
- process))
- (bash-completion-send "echo -n ${COMP_WORDBREAKS}" process)
- (process-put process 'wordbreaks
- (with-current-buffer (process-buffer process)
- (buffer-substring-no-properties
- (point-min) (point-max))))
- (process-put process 'bash-major-version bash-major-version)
+ (bash-completion--setup-bash-common process)
(push (cons remote process) bash-completion-processes)
(setq cleanup nil)
process)
@@ -1049,6 +1100,24 @@ is set to t."
(bash-completion-kill process)
(error nil)))))))))
+(defun bash-completion--require-same-process ()
+ "Setup the process associated with the current buffer and return it."
+ (let ((process (get-buffer-process (current-buffer))))
+ (unless (process-get process 'complete-p)
+ (bash-completion--setup-bash-common process))
+ process))
+
+(defun bash-completion-require-process ()
+ "Setup and return a bash completion process.
+
+If `bash-completion-use-separate-processes' is non-nil,
+`bash-completion--require-separate-process' is called to get the
+process, otherwise `bash-completion--require-same-process' is
+used. "
+ (if bash-completion-use-separate-processes
+ (bash-completion--require-separate-process)
+ (bash-completion--require-same-process)))
+
(defun bash-completion-cd-command-prefix ()
"Build a command-line that CD to default-directory.
@@ -1123,7 +1192,9 @@ completion candidates."
(completion-type (bash-completion--type comp))
(compgen-args (bash-completion--compgen-args comp)))
(concat
- (bash-completion-cd-command-prefix)
+ (if bash-completion-use-separate-processes
+ (bash-completion-cd-command-prefix)
+ "")
(cond
((eq 'command completion-type)
(concat "compgen -b -c -a -A function -- " quoted-stub))
@@ -1194,13 +1265,13 @@ and would like bash completion in Emacs to take these
changes into account."
(when process
(when (eq 'run (process-status process))
(kill-process process))
- (let ((buffer (process-buffer process)))
+ (let ((buffer (bash-completion--get-buffer process)))
(when (buffer-live-p buffer)
(kill-buffer buffer)))))
(defun bash-completion-buffer ()
"Return the buffer of the BASH process, create the BASH process if
necessary."
- (process-buffer (bash-completion-require-process)))
+ (bash-completion--get-buffer (bash-completion-require-process)))
(defun bash-completion-is-running ()
"Check whether the bash completion process is running."
@@ -1212,25 +1283,12 @@ and would like bash completion in Emacs to take these
changes into account."
(setq bash-completion-processes (delq entry bash-completion-processes)))
running))
-(defun bash-completion-send (commandline &optional process timeout)
- "Send a command to the bash completion process.
-
-COMMANDLINE should be a bash command, without the final newline.
-
-PROCESS should be the bash process, if nil this function calls
-`bash-completion-require-process' which might start a new process.
-
-TIMEOUT is the timeout value for this operation, if nil the value of
-`bash-completion-process-timeout' is used.
+(defun bash-completion--send-separate-process (commandline &optional process
timeout)
-Once this command has run without errors, you will find the result
-of the command in the bash completion process buffer.
-
-Return the status code of the command, as a number."
;; (message commandline)
(let ((process (or process (bash-completion-require-process)))
(timeout (or timeout bash-completion-process-timeout)))
- (with-current-buffer (process-buffer process)
+ (with-current-buffer (bash-completion--get-buffer process)
(erase-buffer)
(process-send-string process (concat commandline "\n"))
(while (not (progn (goto-char 1) (search-forward "\v" nil t)))
@@ -1255,9 +1313,61 @@ Return the status code of the command, as a number."
124
status-code)))))
+(defun bash-completion--send-same-process (commandline &optional process
timeout)
+ (let ((process (or process (bash-completion-require-process)))
+ (timeout (or timeout bash-completion-process-timeout))
+ (prompt-regex comint-prompt-regexp)
+ (comint-preoutput-filter-functions '(bash-completion--output-filter)))
+ (with-current-buffer (bash-completion--get-buffer process)
+ (erase-buffer)
+ (comint-send-string process (concat commandline "; echo -e \"\v$?\""
"\n"))
+ (while (not (save-excursion
+ (forward-line 0)
+ (re-search-forward prompt-regex nil t)))
+ (unless (accept-process-output process timeout)
+ (error (concat
+ "Timeout while waiting for an answer from "
+ "bash-completion process.\nProcess output: <<<EOF\n%sEOF")
+ (buffer-string))))
+ (forward-line 0)
+ (delete-region (point) (point-max))
+ (search-backward "\v")
+ (let* ((status-code (string-to-number
+ (buffer-substring-no-properties
+ (point) (line-end-position)))))
+ (delete-region (point) (point-max))
+ (if (string=
+ "124"
+ (bash-completion--parse-side-channel-data "wrapped-status"))
+ 124
+ status-code)))))
+
+(defun bash-completion-send (commandline &optional process timeout)
+ "Send a command to the bash completion process.
+
+COMMANDLINE should be a bash command, without the final newline.
+
+PROCESS should be the bash process, if nil this function calls
+`bash-completion-require-process' which might start a new process
+depending on the value of
+`bash-completion-use-separate-processes'.
+
+TIMEOUT is the timeout value for this operation, if nil the value of
+`bash-completion-process-timeout' is used.
+
+Once this command has run without errors, you will find the
+result of the command in the bash completion process buffer or
+`bash-completion-output-buffer' is
+`bash-completion-use-separate-processes' is nil.
+
+Return the status code of the command, as a number."
+ (if bash-completion-use-separate-processes
+ (bash-completion--send-separate-process commandline process timeout)
+ (bash-completion--send-same-process commandline process timeout)))
+
(defun bash-completion--get-output (process)
"Return the output of the last command sent through `bash-completion-send'."
- (with-current-buffer (process-buffer process) (buffer-string)))
+ (with-current-buffer (bash-completion--get-buffer process) (buffer-string)))
(defun bash-completion--expand-file-name (name &optional local-part-only)
(let* ((remote (file-remote-p default-directory))
- [nongnu] elpa/bash-completion aa9bea48ba 022/313: fix position, (continued)
- [nongnu] elpa/bash-completion aa9bea48ba 022/313: fix position, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 6aedd69000 164/313: Delete duplicates when extracting candidates. fixes #26, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 58447c67bf 206/313: Merge the send functions, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion d6c28d3132 302/313: Add a troubleshooting section to README.md, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion d550256e3f 306/313: Disable flakey test for now., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion a8f7de1e91 173/313: Merge remote-tracking branch 'montag451/remote-shell-support-rebase', ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 9521f79b33 176/313: Display the emacs command run by the tests., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion fc3f762d16 177/313: Track and support options -o default, filenames and nospace., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion c6df9be78d 182/313: Store the alist as process property., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion fefe5ae88e 191/313: Introduce bash-completion-remote-prog., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion d99ad71050 199/313: Add a new option to disable the use of separate processes to perform completion,
ELPA Syncer <=
- [nongnu] elpa/bash-completion ce59fb017a 202/313: Fallback to the use of a separate process if an error occurs, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 24f78df106 203/313: Improve output handling, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion cc9a3dcab4 204/313: Stop cluttering the Bash history, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 0a6fa499ad 207/313: Fix one parameter name of bash-completion--wait-for-prompt, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 3bd30eea3f 208/313: Use the correct function to send string to completion process, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion fd2f8e277e 211/313: Improve prompt detection, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion ac3bd404e8 220/313: Fix typos (#40), ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion f31bb46ff4 222/313: Making sure that line editing is turned off (#42), ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 3210a44081 224/313: Makefile allows specifying the path to bash., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 2178030cdf 225/313: Allow running unit and integration tests separately., ELPA Syncer, 2022/12/03