[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/bash-completion fbdc78b877 198/313: Support compopt when u
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/bash-completion fbdc78b877 198/313: Support compopt when using Bash 4. |
Date: |
Sat, 3 Dec 2022 10:59:30 -0500 (EST) |
branch: elpa/bash-completion
commit fbdc78b8770833752ac4bda28e2c3ea764bf8e76
Author: Stephane Zermatten <szermatt@gmx.net>
Commit: Stephane Zermatten <szermatt@gmx.net>
Support compopt when using Bash 4.
This commit allows bash 4 completion functions to manipulate -o
nospace option using compopt. This is done by generalizing the
approach taken for supporting 124 status: introducing extra "side
channel" information into the output, captured later by
emacs-bash-completion.
This seems the only compot option that's useful to intercept for
bash-completion, so this commit fixes #32
---
bash-completion.el | 126 +++++++++++++++++++++++--------
test/bash-completion-integration-test.el | 45 +++++++++++
test/bash-completion-test.el | 61 ++++++++++++++-
3 files changed, 198 insertions(+), 34 deletions(-)
diff --git a/bash-completion.el b/bash-completion.el
index ba4895a5b4..eb93f99876 100644
--- a/bash-completion.el
+++ b/bash-completion.el
@@ -226,13 +226,6 @@ Bash processes")
(defconst bash-completion-special-chars "[^-0-9a-zA-Z_./\n=]"
"Regexp of characters that must be escaped or quoted.")
-(defconst bash-completion-wrapped-status
- "\e\ebash-completion-wrapped-status=124\e\e"
- "String output by __bash_complete_wrapper when the wrapped
-function returns status code 124, meaning that the completion
-should be retried. This should be a string that's unlikely
-to be included into a completion output.")
-
(eval-when-compile
(unless (or (and (= emacs-major-version 24) (>= emacs-minor-version 1))
(>= emacs-major-version 25))
@@ -261,6 +254,7 @@ to be included into a completion output.")
open-quote ; quote open at stub end: nil, ?' or ?\""
compgen-args ; compgen arguments for this command (list of strings)
wordbreaks ; value of COMP_WORDBREAKS active for this completion
+ compopt ; options forced with compopt nil or `(nospace . ,bool)
)
(defun bash-completion--type (comp)
@@ -283,10 +277,13 @@ The option can be:
- set globally, by setting `bash-completion-nospace' to t
- set for a customized completion, in bash, with
'-o' 'nospace'."
- (if bash-completion-nospace
- t ; set globally
- (bash-completion--has-compgen-option
- (bash-completion--compgen-args comp) "nospace")))
+ (let ((cell))
+ (cond
+ (bash-completion-nospace t) ; set globally
+ ((setq cell (assq 'nospace (bash-completion--compopt comp)))
+ (cdr cell))
+ (t (bash-completion--has-compgen-option
+ (bash-completion--compgen-args comp) "nospace")))))
(defun bash-completion--command (comp)
"Return the current command for the completion, if there is one."
@@ -726,15 +723,23 @@ for directory name detection to work.
Post-processing includes escaping special characters, adding a /
to directory names, replacing STUB with UNPARSED-STUB in the
result. See `bash-completion-fix' for more details."
- (let ((candidates) (result (list)))
- (setq candidates (delete-dups
- (with-current-buffer buffer
- (split-string (buffer-string) "\n" t))))
+ (let ((output) (candidates))
+ (with-current-buffer buffer
+ (let ((compopt (bash-completion--parse-side-channel-data "compopt")))
+ (cond
+ ((string= "-o nospace" compopt)
+ (setf (bash-completion--compopt comp) '((nospace . t))))
+ ((string= "+o nospace" compopt)
+ (setf (bash-completion--compopt comp) '((nospace . nil))))))
+ (setq output (buffer-string)))
+ (setq candidates (delete-dups (split-string output "\n" t)))
(if (eq 1 (length candidates))
(list (bash-completion-fix (car candidates) comp t))
- (dolist (completion candidates)
- (push (bash-completion-fix completion comp nil) result))
- (delete-dups (nreverse result)))))
+ ;; multiple candidates
+ (let ((result (list)))
+ (dolist (completion candidates)
+ (push (bash-completion-fix completion comp nil) result))
+ (delete-dups (nreverse result))))))
(defun bash-completion-fix (str comp single)
"Fix completion candidate in STR for COMP
@@ -989,15 +994,43 @@ is set to t."
(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;" "")
- " eval $__BASH_COMPLETE_WRAPPER;"
- " n=$?; if [[ $n = 124 ]]; then"
- " echo -n \""
- bash-completion-wrapped-status
- "\"; return 1; "
- " fi; }") process)
+ (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)
@@ -1212,16 +1245,19 @@ Return the status code of the command, as a number."
(buffer-substring-no-properties
(1+ control-t-position) (1- control-v-position)))))
(delete-region control-t-position (point-max))
- (goto-char (point-min))
- (let ((case-fold-search nil))
- (when (search-forward bash-completion-wrapped-status nil t)
- (setq status-code 124)
- (delete-region (match-beginning 0) (match-end 0))))
;; (message "status: %d content: \"%s\""
;; status-code
;; (buffer-substring-no-properties
;; (point-min) (point-max)))
- status-code))))
+ (if (string=
+ "124"
+ (bash-completion--parse-side-channel-data "wrapped-status"))
+ 124
+ status-code)))))
+
+(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)))
(defun bash-completion--expand-file-name (name &optional local-part-only)
(let* ((remote (file-remote-p default-directory))
@@ -1244,5 +1280,29 @@ Return the status code of the command, as a number."
(setq rest (cdr rest)))
found))
+(defun bash-completion--side-channel-data (name value)
+ "Return an echo command that outputs NAME=VALUE as side-channel data.
+
+Parse that data from the buffer output using
+`bash-completion--side-channel-data'."
+ (format " echo -n \"\e\e%s=%s\e\e\";" name value))
+
+(defun bash-completion--parse-side-channel-data (name)
+ "Parse side-channel data NAME from the current buffer.
+
+This parses data added by `bash-completion--side-channel-data'
+being run by the shell and removes it from the buffer.
+
+Return the parsed value, as a string or nil."
+ (save-excursion
+ (goto-char (point-min))
+ (let ((case-fold-search nil))
+ (when (search-forward-regexp
+ (format "\e\e%s=\\([^\e]*\\)\e\e"
+ (regexp-quote name))
+ nil 'noerror)
+ (prog1 (match-string 1)
+ (delete-region (match-beginning 0) (match-end 0)))))))
+
(provide 'bash-completion)
;;; bash-completion.el ends here
diff --git a/test/bash-completion-integration-test.el
b/test/bash-completion-integration-test.el
index 6f36ec1b1d..ae3751d415 100644
--- a/test/bash-completion-integration-test.el
+++ b/test/bash-completion-integration-test.el
@@ -166,4 +166,49 @@ for testing completion."
(should (equal "dosomethingelse du"
(bash-completion_test-complete "dosomethingelse du"))))))
+(ert-deftest bash-completion-integration-bash-4-compopt ()
+ (bash-completion_test-with-shell-harness
+ (concat ; .bashrc
+ "function _sometimes_nospace {\n"
+ " if [[ ${COMP_WORDS[COMP_CWORD]} == du ]]; then\n"
+ " COMPREPLY=(dummy)\n"
+ " fi\n"
+ " if [[ ${COMP_WORDS[COMP_CWORD]} == dum ]]; then\n"
+ " COMPREPLY=(dummyo)\n"
+ " compopt -o nospace\n"
+ " fi\n"
+ "}\n"
+ "function _sometimes_not_nospace {\n"
+ " if [[ ${COMP_WORDS[COMP_CWORD]} == du ]]; then\n"
+ " COMPREPLY=(dummy)\n"
+ " fi\n"
+ " if [[ ${COMP_WORDS[COMP_CWORD]} == dum ]]; then \n"
+ " COMPREPLY=(dummyo)\n"
+ " compopt +o nospace\n"
+ " fi\n"
+ "}\n"
+ "complete -F _sometimes_nospace sometimes_nospace\n"
+ "complete -F _sometimes_not_nospace -o nospace sometimes_not_nospace\n")
+ (when (>= bash-major-version 4)
+ (should (equal
+ "sometimes_nospace dummy "
+ (bash-completion_test-complete "sometimes_nospace du")))
+ (should (equal
+ "sometimes_nospace dummyo"
+ (bash-completion_test-complete "sometimes_nospace dum")))
+ (should (equal
+ "sometimes_not_nospace dummy"
+ (bash-completion_test-complete "sometimes_not_nospace du")))
+ (should (equal
+ "sometimes_not_nospace dummyo "
+ (bash-completion_test-complete "sometimes_not_nospace dum")))
+ (let ((bash-completion-nospace t)) ;; never nospace
+ (should (equal
+ "sometimes_nospace dummy"
+ (bash-completion_test-complete "sometimes_nospace du")))
+ (should (equal
+ "sometimes_not_nospace dummyo"
+ (bash-completion_test-complete "sometimes_not_nospace
dum")))))))
+
+
;;; bash-completion-integration-test.el ends here
diff --git a/test/bash-completion-test.el b/test/bash-completion-test.el
index c3f3b78974..b0a1cbaeee 100644
--- a/test/bash-completion-test.el
+++ b/test/bash-completion-test.el
@@ -541,7 +541,7 @@ Return (const return-value new-buffer-content)"
(should (equal
(cons 124 "line1\nline2\n")
(bash-completion-test-send
- (concat "line1\nli" bash-completion-wrapped-status "ne2\n\t0\v")))))
+ (concat "line1\nli\e\ewrapped-status=124\e\ene2\n\t0\v")))))
(ert-deftest bash-completion-cd-command-prefix-test ()
;; no current dir
@@ -793,6 +793,47 @@ Return (const return-value new-buffer-content)"
:cword 1)
(current-buffer)))))))
+(ert-deftest bash-completion-extract-candidates-compopt-test ()
+ (let ((bash-completion-nospace nil))
+ (should
+ (equal
+ '("hello")
+ (bash-completion-test-with-buffer
+ "\e\ecompopt=-o nospace\e\ehello\n"
+ (bash-completion-extract-candidates
+ (bash-completion--make :stub "hell"
+ :unparsed-stub "hell"
+ :wordbreaks ""
+ :cword 1)
+ (current-buffer)))))
+
+ (should
+ (equal
+ '("hello ")
+ (bash-completion-test-with-buffer
+ "\e\ecompopt=+o nospace\e\ehello\n"
+ (bash-completion-extract-candidates
+ (bash-completion--make :stub "hell"
+ :unparsed-stub "hell"
+ :wordbreaks ""
+ :cword 1
+ :compgen-args '("-o" "nospace"))
+ (current-buffer)))))))
+
+(ert-deftest bash-completion-extract-candidates-ignore-compopt-test ()
+ (let ((bash-completion-nospace t))
+ (should
+ (equal
+ '("hello")
+ (bash-completion-test-with-buffer
+ "hello\n\e\ecompopt=+o nospace\e\e"
+ (bash-completion-extract-candidates
+ (bash-completion--make :stub "hell"
+ :unparsed-stub "hell"
+ :wordbreaks ""
+ :cword 1)
+ (current-buffer)))))))
+
(ert-deftest bash-completion-nonsep-test ()
(should (equal "^ \t\n\r;&|'\""
(bash-completion-nonsep nil "")))
@@ -1198,4 +1239,22 @@ before calling
`bash-completion-dynamic-complete-nocomint'.
'("-o") "any")))
(should (equal nil (bash-completion--has-compgen-option '() "any"))))
+(ert-deftest bash-completion--parse-side-channel-data ()
+ (bash-completion-test-with-buffer
+ "test\ntest\e\ename=value\e\e\ntest"
+ (should (equal
+ "value"
+ (bash-completion--parse-side-channel-data "name")))
+ (should (equal "test\ntest\ntest" (buffer-string))))
+ ;; leave other data alone
+ (bash-completion-test-with-buffer
+ "test\ntest\e\eothername=value\e\e\ntest"
+ (should (null (bash-completion--parse-side-channel-data "name")))
+ (should (equal "test\ntest\e\eothername=value\e\e\ntest" (buffer-string))))
+ ;; name can contain chars special for regexps
+ (bash-completion-test-with-buffer
+ "\e\ename*=value\e\etest"
+ (should (equal "value" (bash-completion--parse-side-channel-data "name*")))
+ (should (equal "test" (buffer-string)))))
+
;;; bash-completion_test.el ends here
- [nongnu] elpa/bash-completion b281e1e181 008/313: bash-complete-add-to-alist, (continued)
- [nongnu] elpa/bash-completion b281e1e181 008/313: bash-complete-add-to-alist, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 1144ae69a9 056/313: documented the tokenizer, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 376dd532e2 057/313: modre documentation, up until wordbreak, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion fd7e0e6fbe 078/313: added documentation, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion dd34b278bf 069/313: stopping at point; the easy way, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 8756e80737 184/313: Replace 'options' in the completion struct with 'nospace'., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 2c4cb6edd2 186/313: Replace bash-completion-integration-test with a higher-level test for, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 0aed19067b 188/313: When using Bash 4, split words as Bash 4 does., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 4581a7823f 189/313: Bash 4: set COMP_TYPE and COMP_KEY to TAB, to let completion functions, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 802eb4e5a8 194/313: Set EMACS_BASH_COMPLETE even on remote (tramp) completion., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion fbdc78b877 198/313: Support compopt when using Bash 4.,
ELPA Syncer <=
- [nongnu] elpa/bash-completion f1bd3d528f 201/313: Improve some docstrings, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion fa49dbb382 209/313: Improve status code retrieval, ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 7822d84812 223/313: Added Cask with ert-runner to compile and run test., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 31a01859b2 227/313: Do not run tests under Emacs 24.1., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 21471cc542 228/313: Report result of running the test workflow on README.md., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 98a2a21be7 234/313: Provide a way of refreshing the completion table., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 4fcddf83c9 242/313: Make /etc/bash_completion scripts work with escaped spaces., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 2dbc7e61f6 248/313: Extend bash-completion-refresh to refresh everything, test it., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 04393bca0f 250/313: Apply filename post-processing even when prefix doesn't match., ELPA Syncer, 2022/12/03
- [nongnu] elpa/bash-completion 2a937b3763 255/313: Don't let Emacs post-filter completions built by bash., ELPA Syncer, 2022/12/03