Skip to content

Commit b2c976c

Browse files
committed
feat: Support functions calling compopt -o default and others.
Before this change, calling compopt from a running completion function only worked for setting the nospace option. However, Bash supports setting option for fallbacks, such as -o default -o bashdefault and others. This change introduces supports for such use of the compopt command from inside completion functions. This fixes issue #75, where bash-completion scripts 2.13 _comp_complete_minimal started relying on this Bash feature.
1 parent e6e82ae commit b2c976c

File tree

3 files changed

+115
-47
lines changed

3 files changed

+115
-47
lines changed

bash-completion.el

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,8 @@ Created by `bash-completion-send' and printed by
329329
stub ; parsed version of the stub
330330
open-quote ; quote open at stub end: nil, ?' or ?\""
331331
compgen-args ; compgen arguments for this command (list of strings)
332+
compopt-args ; additional compgen arguments forced with compopt
332333
wordbreaks ; value of COMP_WORDBREAKS active for this completion
333-
compopt ; options forced with compopt nil or `(nospace . ,bool)
334334
)
335335

336336
(defun bash-completion--type (comp)
@@ -351,14 +351,15 @@ customized, usually by `bash-completion--customize'."
351351
The option can be:
352352
- set globally, by setting `bash-completion-nospace' to t
353353
- set for a customized completion, in bash, with
354-
\"-o nospace\"."
355-
(let ((cell))
356-
(cond
357-
(bash-completion-nospace t) ; set globally
358-
((setq cell (assq 'nospace (bash-completion--compopt comp)))
359-
(cdr cell))
360-
(t (bash-completion--has-compgen-option
361-
(bash-completion--compgen-args comp) "nospace")))))
354+
\"-o nospace\".
355+
- set during completion, using compopt"
356+
(or
357+
bash-completion-nospace ; set globally
358+
(eq 'set
359+
(or (bash-completion--get-compgen-option
360+
(bash-completion--compopt-args comp) "nospace")
361+
(bash-completion--get-compgen-option
362+
(bash-completion--compgen-args comp) "nospace")))))
362363

363364
(defun bash-completion--command (comp)
364365
"Return the current command for the completion, if there is one.
@@ -424,13 +425,9 @@ returned."
424425
(concat "function compopt {"
425426
" command compopt \"$@\" 2>/dev/null;"
426427
" local ret=$?; "
427-
" if [[ $ret == 1 && \"$*\" = *\"-o nospace\"* ]]; then"
428-
" _EMACS_COMPOPT='-o nospace';"
429-
" return 0;"
430-
" fi;"
431-
" if [[ $ret == 1 && \"$*\" = *\"+o nospace\"* ]]; then"
432-
" _EMACS_COMPOPT='+o nospace';"
433-
" return 0;"
428+
" if [[ $ret == 1 ]]; then"
429+
" _EMACS_COMPOPT=\"$EMACS_COMPOPT $*\";"
430+
" return 0;"
434431
" fi;"
435432
" return $ret; "
436433
"}")
@@ -934,8 +931,27 @@ The result is a list of candidates, which might be empty."
934931
(setq completion-status (bash-completion-send
935932
(bash-completion-generate-line comp)
936933
process cmd-timeout comp)))
937-
(when (eq 0 completion-status)
938-
(bash-completion-extract-candidates comp buffer))))
934+
(let ((candidates (when (eq 0 completion-status)
935+
(bash-completion-extract-candidates comp buffer)))
936+
(compopt (bash-completion--compopt-args comp)))
937+
938+
;; Possibly run compgen as instructed by a call to compopt
939+
;; inside of a function.
940+
(when (or (member "plusdir" compopt)
941+
(and (null candidates)
942+
(or (member "default" compopt)
943+
(member "bashdefault" compopt)
944+
(member "dirnames" compopt)
945+
(member "filenames" compopt))))
946+
(setf (bash-completion--compgen-args comp)
947+
(bash-completion--compopt-args comp))
948+
(when (eq 0 (bash-completion-send
949+
(bash-completion-generate-line comp)
950+
process cmd-timeout comp))
951+
(setq candidates (append candidates
952+
(bash-completion-extract-candidates
953+
comp buffer)))))
954+
candidates)))
939955

940956
(defun bash-completion-extract-candidates (comp buffer)
941957
"Extract the completion candidates for COMP form BUFFER.
@@ -950,16 +966,11 @@ for directory name detection to work.
950966
Post-processing includes escaping special characters, adding a /
951967
to directory names, replacing STUB with UNPARSED-STUB in the
952968
result. See `bash-completion-fix' for more details."
953-
(let ((output) (candidates))
954-
(with-current-buffer buffer
955-
(let ((compopt (bash-completion--parse-side-channel-data "compopt")))
956-
(cond
957-
((string= "-o nospace" compopt)
958-
(setf (bash-completion--compopt comp) '((nospace . t))))
959-
((string= "+o nospace" compopt)
960-
(setf (bash-completion--compopt comp) '((nospace . nil))))))
961-
(setq output (buffer-string)))
962-
(setq candidates (delete-dups (split-string output "\n" t)))
969+
(with-current-buffer buffer
970+
(setf (bash-completion--compopt-args comp)
971+
(bash-completion--parse-side-channel-data "compopt" 'tokenize)))
972+
(let* ((output (with-current-buffer buffer (buffer-string)))
973+
(candidates (delete-dups (split-string output "\n" t))))
963974
(if (eq 1 (length candidates))
964975
(list (bash-completion-fix (car candidates) comp t))
965976
;; multiple candidates
@@ -1689,14 +1700,23 @@ characters. These are output as-is."
16891700
(file-remote-p expanded 'localname)
16901701
expanded)))
16911702

1692-
(defun bash-completion--has-compgen-option (compgen-args option-name)
1693-
"Check whether COMPGEN-ARGS contains -o OPTION-NAME."
1694-
(let ((rest compgen-args) (found))
1695-
(while (and (not found)
1696-
(setq rest (cdr (member "-o" rest))))
1697-
(when (string= option-name (car rest))
1698-
(setq found t))
1703+
(defun bash-completion--get-compgen-option (compgen-args option-name)
1704+
"Check whether COMPGEN-ARGS contains -o or +o OPTION-NAME.
1705+
1706+
Returns nil if the option was unspecified, \\='set if it was
1707+
specified with -o and \\='unset if it was specified with +o."
1708+
(let ((rest compgen-args)
1709+
found)
1710+
(while rest
1711+
(cond
1712+
((and (string= "-o" (car rest))
1713+
(equal option-name (car (cdr rest))))
1714+
(setq found 'set))
1715+
((and (string= "+o" (car rest))
1716+
(equal option-name (car (cdr rest))))
1717+
(setq found 'unset)))
16991718
(setq rest (cdr rest)))
1719+
17001720
found))
17011721

17021722
(defun bash-completion--side-channel-data (name value)
@@ -1706,7 +1726,7 @@ Parse that data from the buffer output using
17061726
`bash-completion--side-channel-data'."
17071727
(format " echo \"\e\e%s=%s\e\e\";" name value))
17081728

1709-
(defun bash-completion--parse-side-channel-data (name)
1729+
(defun bash-completion--parse-side-channel-data (name &optional tokenize)
17101730
"Parse side-channel data NAME from the current buffer.
17111731
17121732
This parses data added by `bash-completion--side-channel-data'
@@ -1720,8 +1740,16 @@ Return the parsed value, as a string or nil."
17201740
(format "\e\e%s=\\([^\e]*\\)\e\e"
17211741
(regexp-quote name))
17221742
nil 'noerror)
1723-
(prog1 (match-string 1)
1724-
(delete-region (match-beginning 0) (1+ (match-end 0))))))))
1743+
(let ((result (match-string 1))
1744+
(start (match-beginning 0))
1745+
(end (1+ (match-end 0))))
1746+
(when tokenize
1747+
(setq result
1748+
(bash-completion-strings-from-tokens
1749+
(bash-completion-tokenize (match-beginning 1) (match-end 1)))))
1750+
(delete-region start end)
1751+
1752+
result)))))
17251753

17261754
(defun bash-completion--completion-table-with-cache (comp process)
17271755
"Build a dynamic completion table for COMP using PROCESS.

test/bash-completion-integration-test.el

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,6 @@ $ ")))))
853853
(should (equal nil (funcall compfunc-nonprefix "babeetai"
854854
(lambda (c) (equal "babeedai" c)) 'lambda))))))
855855

856-
857856
(ert-deftest bash-completion_test-issue-74 ()
858857
(bash-completion_test-with-shell-harness
859858
(concat ; .bashrc
@@ -867,5 +866,38 @@ $ ")))))
867866
(should (equal "mycmd bar "
868867
(bash-completion_test-complete "mycmd b")))))
869868

869+
(ert-deftest bash-completion-integration-filenames-option ()
870+
(bash-completion_test-with-shell-harness
871+
(concat ; .bashrc
872+
"function _dummy {\n"
873+
"}\n"
874+
"complete -F _dummy -o plusdir dummy\n")
875+
nil
876+
(should (equal
877+
"dummy some/other/"
878+
(bash-completion_test-complete "dummy some/ot")))))
879+
880+
(ert-deftest bash-completion-integration-plusdir-option ()
881+
(bash-completion_test-with-shell-harness
882+
(concat ; .bashrc
883+
"function _dummy {\n"
884+
"}\n"
885+
"complete -F _dummy -o filenames dummy\n")
886+
nil
887+
(should (equal
888+
"dummy moretestfile "
889+
(bash-completion_test-complete "dummy moret")))))
890+
891+
(ert-deftest bash-completion-integration-new-compopt-options ()
892+
(bash-completion_test-with-shell-harness
893+
(concat ; .bashrc
894+
"function _dummy {\n"
895+
" compopt -o default -o bashdefault\n"
896+
"}\n"
897+
"complete -F _dummy dummy\n")
898+
nil
899+
(should (equal
900+
"dummy moretestfile "
901+
(bash-completion_test-complete "dummy moret")))))
870902

871903
;;; bash-completion-integration-test.el ends here

test/bash-completion-test.el

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,22 +1140,30 @@ before calling `bash-completion-dynamic-complete-nocomint'.
11401140
'("somedir/")
11411141
(nth 2 (bash-completion-dynamic-complete-nocomint 3 (point))))))))
11421142

1143-
(ert-deftest bash-completion--has-compgen-option ()
1143+
(ert-deftest bash-completion--get-compgen-option ()
11441144
(should (equal nil
1145-
(bash-completion--has-compgen-option
1145+
(bash-completion--get-compgen-option
11461146
'("-F" "boo" "-o" "filenames" "-a" "-o" "default")
11471147
"nospace")))
1148-
(should (equal t
1149-
(bash-completion--has-compgen-option
1148+
(should (equal 'set
1149+
(bash-completion--get-compgen-option
11501150
'("-F" "boo" "-o" "filenames" "-a" "-o" "default")
11511151
"filenames")))
1152-
(should (equal t
1153-
(bash-completion--has-compgen-option
1152+
(should (equal 'set
1153+
(bash-completion--get-compgen-option
11541154
'("-F" "boo" "-o" "filenames" "-a" "-o" "default")
11551155
"default")))
1156-
(should (equal nil (bash-completion--has-compgen-option
1156+
(should (equal 'unset
1157+
(bash-completion--get-compgen-option
1158+
'("-F" "boo" "+o" "nospace" "-o" "default")
1159+
"nospace")))
1160+
(should (equal 'set
1161+
(bash-completion--get-compgen-option
1162+
'("+o" "nospace" "-o" "nospace")
1163+
"nospace")))
1164+
(should (equal nil (bash-completion--get-compgen-option
11571165
'("-o") "any")))
1158-
(should (equal nil (bash-completion--has-compgen-option '() "any"))))
1166+
(should (equal nil (bash-completion--get-compgen-option '() "any"))))
11591167

11601168
(ert-deftest bash-completion--parse-side-channel-data ()
11611169
(bash-completion-test-with-buffer

0 commit comments

Comments
 (0)