aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAugusto Stoffel2022-09-08 11:09:42 +0200
committerLars Ingebrigtsen2022-09-14 21:58:04 +0200
commita9941269683fe50673d0aa81feefb7a9d3d8a6b9 (patch)
tree566c9ecd3afb90b58607c71ad794cee14ba7b823
parent05971d2b8d47e69e9585d0d6066b8a607555aa48 (diff)
downloademacs-a9941269683fe50673d0aa81feefb7a9d3d8a6b9.tar.gz
emacs-a9941269683fe50673d0aa81feefb7a9d3d8a6b9.zip
pcomplete: Generate completions from --help messages
* lisp/pcomplete.el (pcomplete-from-help): New function (and hash table) to get pcomplete candidates from help messages. (pcomplete-here-using-help): Helper function to define pcomplete for simple commands (pcomplete-completions-at-point): Provide annotation-function and company-docsig properties. * lisp/pcmpl-git.el: New file, provides pcomplete for Git. * lisp/pcmpl-gnu.el: Add pcomplete for awk, gpg and gdb, emacs and emacsclient. * lisp/pcmpl-linux.el: Add pcomplete for systemctl and journalctl. * lisp/pcmpl-rpm.el: Add pcomplete for dnf. * lisp/pcmpl-unix.el: Add pcomplete for sudo and most commands found in GNU Coreutils. * lisp/pcmpl-x.el: Add pcomplete for tex, pdftex, latex, pdflatex, rigrep and rclone. * test/lisp/pcomplete-tests.el (pcomplete-test-parse-gpg-help, pcomplete-test-parse-git-help): Tests for the new functions.
-rw-r--r--lisp/pcmpl-git.el110
-rw-r--r--lisp/pcmpl-gnu.el36
-rw-r--r--lisp/pcmpl-linux.el68
-rw-r--r--lisp/pcmpl-rpm.el43
-rw-r--r--lisp/pcmpl-unix.el490
-rw-r--r--lisp/pcmpl-x.el43
-rw-r--r--lisp/pcomplete.el138
-rw-r--r--test/lisp/pcomplete-tests.el100
8 files changed, 1004 insertions, 24 deletions
diff --git a/lisp/pcmpl-git.el b/lisp/pcmpl-git.el
new file mode 100644
index 00000000000..3584fa06732
--- /dev/null
+++ b/lisp/pcmpl-git.el
@@ -0,0 +1,110 @@
1;;; pcmpl-git.el --- Completions for Git -*- lexical-binding: t -*-
2
3;; Copyright (C) 2022 Free Software Foundation, Inc.
4
5;; Package: pcomplete
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software: you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
21
22;;; Commentary:
23
24;; This library provides completion rules for the Git program.
25
26;;; Code:
27
28(require 'pcomplete)
29(require 'vc-git)
30
31(defun pcmpl-git--expand-flags (args)
32 "In the list of ARGS, expand arguments of the form --[no-]flag."
33 (mapcan (lambda (arg) (if (string-search "[no-]" arg)
34 (list (string-replace "[no-]" "" arg)
35 (string-replace "[no-]" "no-" arg))
36 (list arg)))
37 args))
38
39(defun pcmpl-git--tracked-file-predicate (&rest args)
40 "Return a predicate function determining the Git status of a file.
41Files listed by `git ls-files ARGS' satisfy the predicate."
42 (when-let ((files (mapcar #'expand-file-name
43 (ignore-errors
44 (apply #'process-lines
45 vc-git-program "ls-files" args)))))
46 (lambda (file)
47 (setq file (expand-file-name file))
48 (if (string-suffix-p "/" file)
49 (seq-some (lambda (f) (string-prefix-p file f))
50 files)
51 (member file files)))))
52
53(defun pcmpl-git--remote-refs (remote)
54 "List the locally known Git revisions from REMOTE."
55 (delq nil
56 (mapcar
57 (let ((re (concat "\\`" (regexp-quote remote) "/\\(.*\\)")))
58 (lambda (s) (when (string-match re s) (match-string 1 s))))
59 (vc-git-revision-table nil))))
60
61;;;###autoload
62(defun pcomplete/git ()
63 "Completion for the `git' command."
64 (let ((subcommands (pcomplete-from-help `(,vc-git-program "help" "-a")
65 :margin "^\\( +\\)[a-z]"
66 :argument "[[:alnum:]-]+")))
67 (while (not (member (pcomplete-arg 1) subcommands))
68 (if (string-prefix-p "-" (pcomplete-arg))
69 (pcomplete-here (pcomplete-from-help `(,vc-git-program "help")
70 :margin "\\(\\[\\)-"
71 :separator " | "
72 :description "\\`"))
73 (pcomplete-here (completion-table-merge
74 subcommands
75 (when (string-prefix-p "-" (pcomplete-arg 1))
76 (pcomplete-entries))))))
77 (let ((subcmd (pcomplete-arg 1)))
78 (while (pcase subcmd
79 ((guard (string-prefix-p "-" (pcomplete-arg)))
80 (pcomplete-here
81 (pcmpl-git--expand-flags
82 (pcomplete-from-help `(,vc-git-program "help" ,subcmd)
83 :argument
84 "-+\\(?:\\[no-\\]\\)?[a-z-]+=?"))))
85 ;; Complete modified tracked files
86 ((or "add" "commit" "restore")
87 (pcomplete-here
88 (pcomplete-entries
89 nil (pcmpl-git--tracked-file-predicate "-m"))))
90 ;; Complete all tracked files
91 ((or "mv" "rm" "grep" "status")
92 (pcomplete-here
93 (pcomplete-entries nil (pcmpl-git--tracked-file-predicate))))
94 ;; Complete revisions
95 ((or "branch" "merge" "rebase" "switch")
96 (pcomplete-here (vc-git-revision-table nil)))
97 ;; Complete revisions and tracked files
98 ;; TODO: diff and log accept revision ranges
99 ((or "checkout" "reset" "show" "diff" "log")
100 (pcomplete-here
101 (completion-table-in-turn
102 (vc-git-revision-table nil)
103 (pcomplete-entries nil (pcmpl-git--tracked-file-predicate)))))
104 ;; Complete remotes and their revisions
105 ((or "fetch" "pull" "push")
106 (pcomplete-here (process-lines vc-git-program "remote"))
107 (pcomplete-here (pcmpl-git--remote-refs (pcomplete-arg 1)))))))))
108
109(provide 'pcmpl-git)
110;;; pcmpl-git.el ends here
diff --git a/lisp/pcmpl-gnu.el b/lisp/pcmpl-gnu.el
index 3c9bf1ec9d2..cdfde5640a7 100644
--- a/lisp/pcmpl-gnu.el
+++ b/lisp/pcmpl-gnu.el
@@ -394,6 +394,40 @@ Return the new list."
394 (while (pcomplete-here (pcomplete-dirs) nil #'identity)))) 394 (while (pcomplete-here (pcomplete-dirs) nil #'identity))))
395 395
396;;;###autoload 396;;;###autoload
397(defalias 'pcomplete/gdb 'pcomplete/xargs) 397(defun pcomplete/awk ()
398 "Completion for the `awk' command."
399 (pcomplete-here-using-help "awk --help"
400 :margin "\t"
401 :separator " +"
402 :description "\0"
403 :metavar "[=a-z]+"))
404
405;;;###autoload
406(defun pcomplete/gpg ()
407 "Completion for the `gpg` command."
408 (pcomplete-here-using-help "gpg --help" :narrow-end "^ -se"))
409
410;;;###autoload
411(defun pcomplete/gdb ()
412 "Completion for the `gdb' command."
413 (while
414 (cond
415 ((string= "--args" (pcomplete-arg 1))
416 (funcall pcomplete-command-completion-function)
417 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
418 pcomplete-default-completion-function)))
419 ((string-prefix-p "-" (pcomplete-arg 0))
420 (pcomplete-here (pcomplete-from-help "gdb --help")))
421 (t (pcomplete-here (pcomplete-entries))))))
422
423;;;###autoload
424(defun pcomplete/emacs ()
425 "Completion for the `emacs' command."
426 (pcomplete-here-using-help "emacs --help" :margin "^\\(\\)-"))
427
428;;;###autoload
429(defun pcomplete/emacsclient ()
430 "Completion for the `emacsclient' command."
431 (pcomplete-here-using-help "emacsclient --help" :margin "^\\(\\)-"))
398 432
399;;; pcmpl-gnu.el ends here 433;;; pcmpl-gnu.el ends here
diff --git a/lisp/pcmpl-linux.el b/lisp/pcmpl-linux.el
index 7c072f3d40c..023c655a2a8 100644
--- a/lisp/pcmpl-linux.el
+++ b/lisp/pcmpl-linux.el
@@ -30,6 +30,7 @@
30(provide 'pcmpl-linux) 30(provide 'pcmpl-linux)
31 31
32(require 'pcomplete) 32(require 'pcomplete)
33(eval-when-compile (require 'rx))
33 34
34;; Functions: 35;; Functions:
35 36
@@ -111,4 +112,71 @@ Test is done using `equal'."
111 (pcomplete-uniquify-list points) 112 (pcomplete-uniquify-list points)
112 (cons "swap" (pcmpl-linux-mounted-directories)))))) 113 (cons "swap" (pcmpl-linux-mounted-directories))))))
113 114
115;;; systemd
116
117(defun pcmpl-linux--systemd-units (&rest args)
118 "Run `systemd list-units ARGS' and return the output as a list."
119 (with-temp-buffer
120 (apply #'call-process
121 "systemctl" nil '(t nil) nil
122 "list-units" "--full" "--legend=no" "--plain" args)
123 (goto-char (point-min))
124 (let (result)
125 (while (re-search-forward (rx bol (group (+ (not space)))
126 (+ space) (+ (not space))
127 (+ space) (group (+ (not space)))
128 (+ space) (+ (not space))
129 (+ space) (group (* nonl)))
130 nil t)
131 (push (match-string 1) result)
132 (put-text-property 0 1 'pcomplete-annotation
133 (concat " " (match-string 2))
134 (car result))
135 (put-text-property 0 1 'pcomplete-description
136 (match-string 3)
137 (car result)))
138 (nreverse result))))
139
140;;;###autoload
141(defun pcomplete/systemctl ()
142 "Completion for the `systemctl' command."
143 (let ((subcmds (pcomplete-from-help
144 "systemctl --help"
145 :margin (rx bol " " (group) alpha)
146 :argument (rx (+ (any alpha ?-)))
147 :metavar (rx (group (+ " " (>= 2 (any upper "[]|."))))))))
148 (while (not (member (pcomplete-arg 1) subcmds))
149 (if (string-prefix-p "-" (pcomplete-arg 0))
150 (pcomplete-here (pcomplete-from-help "systemctl --help"
151 :metavar "[^ ]+"
152 :separator " \\(\\)-"))
153 (pcomplete-here subcmds)))
154 (let ((subcmd (pcomplete-arg 1))
155 (context (if (member "--user" pcomplete-args) "--user" "--system")))
156 (while (pcase subcmd
157 ((guard (string-prefix-p "-" (pcomplete-arg 0)))
158 (pcomplete-here
159 (pcomplete-from-help "systemctl --help")))
160 ;; TODO: suggest only relevant units to each subcommand
161 ("start"
162 (pcomplete-here
163 (pcmpl-linux--systemd-units context "--state" "inactive,failed")))
164 ((or "restart" "stop")
165 (pcomplete-here
166 (pcmpl-linux--systemd-units context "--state" "active")))
167 (_ (pcomplete-here
168 (completion-table-in-turn
169 (pcmpl-linux--systemd-units context "--all")
170 (pcomplete-entries)))))))))
171
172;;;###autoload
173(defun pcomplete/journalctl ()
174 "Completion for the `journalctl' command."
175 (while (if (string-prefix-p "-" (pcomplete-arg 0))
176 (pcomplete-here (pcomplete-from-help "journalctl --help"
177 :metavar "[^ ]+"
178 :separator " \\(\\)-"))
179 (pcomplete-here (mapcar (lambda (s) (concat s "="))
180 (process-lines "journalctl" "--fields"))))))
181
114;;; pcmpl-linux.el ends here 182;;; pcmpl-linux.el ends here
diff --git a/lisp/pcmpl-rpm.el b/lisp/pcmpl-rpm.el
index f7925d9d9ec..ebb6b72600c 100644
--- a/lisp/pcmpl-rpm.el
+++ b/lisp/pcmpl-rpm.el
@@ -21,7 +21,8 @@
21 21
22;;; Commentary: 22;;; Commentary:
23 23
24;; These functions provide completion rules for the `rpm' command. 24;; These functions provide completion rules for the `rpm' command and
25;; related tools.
25 26
26;;; Code: 27;;; Code:
27 28
@@ -378,6 +379,46 @@
378 (t 379 (t
379 (error "You must select a mode: -q, -i, -U, --verify, etc")))))) 380 (error "You must select a mode: -q, -i, -U, --verify, etc"))))))
380 381
382;;; DNF
383
384(defvar pcmpl-rpm-dnf-cache-file "/var/cache/dnf/packages.db"
385 "Location of the DNF cache.")
386
387(defun pcmpl-rpm--dnf-packages (status)
388 (when (and (file-exists-p pcmpl-rpm-dnf-cache-file)
389 (executable-find "sqlite3"))
390 (with-temp-message
391 "Getting list of packages..."
392 (process-lines "sqlite3" "-batch" "-init" "/dev/null"
393 pcmpl-rpm-dnf-cache-file
394 (pcase-exhaustive status
395 ('available "select pkg from available")
396 ('installed "select pkg from installed")
397 ('not-installed "\
398select pkg from available where pkg not in (select pkg from installed)"))))))
399
400;;;###autoload
401(defun pcomplete/dnf ()
402 "Completion for the `dnf' command."
403 (let ((subcmds (pcomplete-from-help "dnf help"
404 :margin "^\\(\\)[a-z-]+ "
405 :argument "[a-z-]+")))
406 (while (not (member (pcomplete-arg 1) subcmds))
407 (pcomplete-here (completion-table-merge
408 subcmds
409 (pcomplete-from-help "dnf help"))))
410 (let ((subcmd (pcomplete-arg 1)))
411 (while (pcase subcmd
412 ((guard (pcomplete-match "\\`-" 0))
413 (pcomplete-here
414 (pcomplete-from-help `("dnf" "help" ,subcmd))))
415 ((or "downgrade" "reinstall" "remove")
416 (pcomplete-here (pcmpl-rpm--dnf-packages 'installed)))
417 ((or "install" "mark" "reinstall" "upgrade")
418 (pcomplete-here (pcmpl-rpm--dnf-packages 'not-installed)))
419 ((or "builddep" "changelog" "info" "list" "repoquery" "updateinfo")
420 (pcomplete-here (pcmpl-rpm--dnf-packages 'available))))))))
421
381(provide 'pcmpl-rpm) 422(provide 'pcmpl-rpm)
382 423
383;;; pcmpl-rpm.el ends here 424;;; pcmpl-rpm.el ends here
diff --git a/lisp/pcmpl-unix.el b/lisp/pcmpl-unix.el
index 8774f091c83..0c32f814d0e 100644
--- a/lisp/pcmpl-unix.el
+++ b/lisp/pcmpl-unix.el
@@ -25,7 +25,7 @@
25 25
26(require 'pcomplete) 26(require 'pcomplete)
27 27
28;; User Variables: 28;;; User Variables
29 29
30(defcustom pcmpl-unix-group-file "/etc/group" 30(defcustom pcmpl-unix-group-file "/etc/group"
31 "If non-nil, a string naming the group file on your system." 31 "If non-nil, a string naming the group file on your system."
@@ -56,7 +56,7 @@ being via `pcmpl-ssh-known-hosts-file'."
56 :group 'pcmpl-unix 56 :group 'pcmpl-unix
57 :version "24.1") 57 :version "24.1")
58 58
59;; Functions: 59;;; Shell builtins and core utilities
60 60
61;;;###autoload 61;;;###autoload
62(defun pcomplete/cd () 62(defun pcomplete/cd ()
@@ -69,34 +69,38 @@ being via `pcmpl-ssh-known-hosts-file'."
69;;;###autoload 69;;;###autoload
70(defun pcomplete/rmdir () 70(defun pcomplete/rmdir ()
71 "Completion for `rmdir'." 71 "Completion for `rmdir'."
72 (while (pcomplete-here (pcomplete-dirs)))) 72 (while (if (string-prefix-p "-" (pcomplete-arg))
73 (pcomplete-here (pcomplete-from-help "rmdir --help"))
74 (pcomplete-here (pcomplete-dirs)))))
73 75
74;;;###autoload 76;;;###autoload
75(defun pcomplete/rm () 77(defun pcomplete/rm ()
76 "Completion for `rm'." 78 "Completion for the `rm' command."
77 (let ((pcomplete-help "(fileutils)rm invocation")) 79 (pcomplete-here-using-help "rm --help"))
78 (pcomplete-opt "dfirRv")
79 (while (pcomplete-here (pcomplete-all-entries) nil
80 #'expand-file-name))))
81 80
82;;;###autoload 81;;;###autoload
83(defun pcomplete/xargs () 82(defun pcomplete/xargs ()
84 "Completion for `xargs'." 83 "Completion for `xargs'."
85 (while (string-prefix-p "-" (pcomplete-arg 0)) 84 (while (string-prefix-p "-" (pcomplete-arg 0))
86 (pcomplete-here (funcall pcomplete-default-completion-function))) 85 (pcomplete-here (pcomplete-from-help "xargs --help"))
86 (when (pcomplete-match "\\`-[adEIiLnPs]\\'") (pcomplete-here)))
87 (funcall pcomplete-command-completion-function) 87 (funcall pcomplete-command-completion-function)
88 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1)) 88 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
89 pcomplete-default-completion-function))) 89 pcomplete-default-completion-function)))
90 90
91;; FIXME: Add completion of sudo-specific arguments.
92(defalias 'pcomplete/sudo #'pcomplete/xargs)
93
94;;;###autoload 91;;;###autoload
95(defalias 'pcomplete/time 'pcomplete/xargs) 92(defun pcomplete/time ()
93 "Completion for the `time' command."
94 (pcomplete-opt "p")
95 (funcall pcomplete-command-completion-function)
96 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
97 pcomplete-default-completion-function)))
96 98
97;;;###autoload 99;;;###autoload
98(defun pcomplete/which () 100(defun pcomplete/which ()
99 "Completion for `which'." 101 "Completion for `which'."
102 (while (string-prefix-p "-" (pcomplete-arg 0))
103 (pcomplete-here (pcomplete-from-help "which --help")))
100 (while (pcomplete-here (funcall pcomplete-command-completion-function)))) 104 (while (pcomplete-here (funcall pcomplete-command-completion-function))))
101 105
102(defun pcmpl-unix-read-passwd-file (file) 106(defun pcmpl-unix-read-passwd-file (file)
@@ -129,24 +133,454 @@ documentation), this function returns nil."
129 (pcmpl-unix-read-passwd-file pcmpl-unix-passwd-file))) 133 (pcmpl-unix-read-passwd-file pcmpl-unix-passwd-file)))
130 134
131;;;###autoload 135;;;###autoload
136(defun pcomplete/cat ()
137 "Completion for the `cat' command."
138 (pcomplete-here-using-help "cat --help"))
139
140;;;###autoload
141(defun pcomplete/tac ()
142 "Completion for the `tac' command."
143 (pcomplete-here-using-help "tac --help"))
144
145;;;###autoload
146(defun pcomplete/nl ()
147 "Completion for the `nl' command."
148 (pcomplete-here-using-help "nl --help"))
149
150;;;###autoload
151(defun pcomplete/od ()
152 "Completion for the `od' command."
153 (pcomplete-here-using-help "od --help"))
154
155;;;###autoload
156(defun pcomplete/base32 ()
157 "Completion for the `base32' and `base64' commands."
158 (pcomplete-here-using-help "base32 --help"))
159;;;###autoload
160(defalias 'pcomplete/base64 'pcomplete/base32)
161
162;;;###autoload
163(defun pcomplete/basenc ()
164 "Completion for the `basenc' command."
165 (pcomplete-here-using-help "basenc --help"))
166
167;;;###autoload
168(defun pcomplete/fmt ()
169 "Completion for the `fmt' command."
170 (pcomplete-here-using-help "fmt --help"))
171
172;;;###autoload
173(defun pcomplete/pr ()
174 "Completion for the `pr' command."
175 (pcomplete-here-using-help "pr --help"))
176
177;;;###autoload
178(defun pcomplete/fold ()
179 "Completion for the `fold' command."
180 (pcomplete-here-using-help "fold --help"))
181
182;;;###autoload
183(defun pcomplete/head ()
184 "Completion for the `head' command."
185 (pcomplete-here-using-help "head --help"))
186
187;;;###autoload
188(defun pcomplete/tail ()
189 "Completion for the `tail' command."
190 (pcomplete-here-using-help "tail --help"))
191
192;;;###autoload
193(defun pcomplete/split ()
194 "Completion for the `split' command."
195 (pcomplete-here-using-help "split --help"))
196
197;;;###autoload
198(defun pcomplete/csplit ()
199 "Completion for the `csplit' command."
200 (pcomplete-here-using-help "csplit --help"))
201
202;;;###autoload
203(defun pcomplete/wc ()
204 "Completion for the `wc' command."
205 (pcomplete-here-using-help "wc --help"))
206
207;;;###autoload
208(defun pcomplete/sum ()
209 "Completion for the `sum' command."
210 (pcomplete-here-using-help "sum --help"))
211
212;;;###autoload
213(defun pcomplete/cksum ()
214 "Completion for the `cksum' command."
215 (pcomplete-here-using-help "cksum --help"))
216
217;;;###autoload
218(defun pcomplete/b2sum ()
219 "Completion for the `b2sum' command."
220 (pcomplete-here-using-help "b2sum --help"))
221
222;;;###autoload
223(defun pcomplete/md5sum ()
224 "Completion for checksum commands."
225 (pcomplete-here-using-help "md5sum --help"))
226;;;###autoload(defalias 'pcomplete/sha1sum 'pcomplete/md5sum)
227;;;###autoload(defalias 'pcomplete/sha224sum 'pcomplete/md5sum)
228;;;###autoload(defalias 'pcomplete/sha256sum 'pcomplete/md5sum)
229;;;###autoload(defalias 'pcomplete/sha384sum 'pcomplete/md5sum)
230;;;###autoload(defalias 'pcomplete/sha521sum 'pcomplete/md5sum)
231
232;;;###autoload
233(defun pcomplete/sort ()
234 "Completion for the `sort' command."
235 (pcomplete-here-using-help "sort --help"))
236
237;;;###autoload
238(defun pcomplete/shuf ()
239 "Completion for the `shuf' command."
240 (pcomplete-here-using-help "shuf --help"))
241
242;;;###autoload
243(defun pcomplete/uniq ()
244 "Completion for the `uniq' command."
245 (pcomplete-here-using-help "uniq --help"))
246
247;;;###autoload
248(defun pcomplete/comm ()
249 "Completion for the `comm' command."
250 (pcomplete-here-using-help "comm --help"))
251
252;;;###autoload
253(defun pcomplete/ptx ()
254 "Completion for the `ptx' command."
255 (pcomplete-here-using-help "ptx --help"))
256
257;;;###autoload
258(defun pcomplete/tsort ()
259 "Completion for the `tsort' command."
260 (pcomplete-here-using-help "tsort --help"))
261
262;;;###autoload
263(defun pcomplete/cut ()
264 "Completion for the `cut' command."
265 (pcomplete-here-using-help "cut --help"))
266
267;;;###autoload
268(defun pcomplete/paste ()
269 "Completion for the `paste' command."
270 (pcomplete-here-using-help "paste --help"))
271
272;;;###autoload
273(defun pcomplete/join ()
274 "Completion for the `join' command."
275 (pcomplete-here-using-help "join --help"))
276
277;;;###autoload
278(defun pcomplete/tr ()
279 "Completion for the `tr' command."
280 (pcomplete-here-using-help "tr --help"))
281
282;;;###autoload
283(defun pcomplete/expand ()
284 "Completion for the `expand' command."
285 (pcomplete-here-using-help "expand --help"))
286
287;;;###autoload
288(defun pcomplete/unexpand ()
289 "Completion for the `unexpand' command."
290 (pcomplete-here-using-help "unexpand --help"))
291
292;;;###autoload
293(defun pcomplete/ls ()
294 "Completion for the `ls' command."
295 (pcomplete-here-using-help "ls --help"))
296;;;###autoload(defalias 'pcomplete/dir 'pcomplete/ls)
297;;;###autoload(defalias 'pcomplete/vdir 'pcomplete/ls)
298
299;;;###autoload
300(defun pcomplete/cp ()
301 "Completion for the `cp' command."
302 (pcomplete-here-using-help "cp --help"))
303
304;;;###autoload
305(defun pcomplete/dd ()
306 "Completion for the `dd' command."
307 (let ((operands (pcomplete-from-help "dd --help"
308 :argument "[a-z]+="
309 :narrow-start "\n\n"
310 :narrow-end "\n\n")))
311 (while
312 (cond ((pcomplete-match "\\`[io]f=\\(.*\\)" 0)
313 (pcomplete-here (pcomplete-entries)
314 (pcomplete-match-string 1 0)))
315 (t (pcomplete-here operands))))))
316
317;;;###autoload
318(defun pcomplete/install ()
319 "Completion for the `install' command."
320 (pcomplete-here-using-help "install --help"))
321
322;;;###autoload
323(defun pcomplete/mv ()
324 "Completion for the `mv' command."
325 (pcomplete-here-using-help "mv --help"))
326
327;;;###autoload
328(defun pcomplete/shred ()
329 "Completion for the `shred' command."
330 (pcomplete-here-using-help "shred --help"))
331
332;;;###autoload
333(defun pcomplete/ln ()
334 "Completion for the `ln' command."
335 (pcomplete-here-using-help "ln --help"))
336
337;;;###autoload
338(defun pcomplete/mkdir ()
339 "Completion for the `mkdir' command."
340 (pcomplete-here-using-help "mkdir --help"))
341
342;;;###autoload
343(defun pcomplete/mkfifo ()
344 "Completion for the `mkfifo' command."
345 (pcomplete-here-using-help "mkfifo --help"))
346
347;;;###autoload
348(defun pcomplete/mknod ()
349 "Completion for the `mknod' command."
350 (pcomplete-here-using-help "mknod --help"))
351
352;;;###autoload
353(defun pcomplete/readlink ()
354 "Completion for the `readlink' command."
355 (pcomplete-here-using-help "readlink --help"))
356
357;;;###autoload
132(defun pcomplete/chown () 358(defun pcomplete/chown ()
133 "Completion for the `chown' command." 359 "Completion for the `chown' command."
134 (unless (pcomplete-match "\\`-") 360 (while (pcomplete-match "\\`-" 0)
135 (if (pcomplete-match "\\`[^.]*\\'" 0) 361 (pcomplete-here (pcomplete-from-help "chown --help")))
136 (pcomplete-here* (pcmpl-unix-user-names)) 362 (if (pcomplete-match "\\`[^.]*\\'" 0)
137 (if (pcomplete-match "\\.\\([^.]*\\)\\'" 0) 363 (pcomplete-here* (pcmpl-unix-user-names))
138 (pcomplete-here* (pcmpl-unix-group-names) 364 (if (pcomplete-match "\\.\\([^.]*\\)\\'" 0)
139 (pcomplete-match-string 1 0)) 365 (pcomplete-here* (pcmpl-unix-group-names)
140 (pcomplete-here*)))) 366 (pcomplete-match-string 1 0))
367 (pcomplete-here*)))
141 (while (pcomplete-here (pcomplete-entries)))) 368 (while (pcomplete-here (pcomplete-entries))))
142 369
143;;;###autoload 370;;;###autoload
144(defun pcomplete/chgrp () 371(defun pcomplete/chgrp ()
145 "Completion for the `chgrp' command." 372 "Completion for the `chgrp' command."
146 (unless (pcomplete-match "\\`-") 373 (while (pcomplete-match "\\`-" 0)
147 (pcomplete-here* (pcmpl-unix-group-names))) 374 (pcomplete-here (pcomplete-from-help "chgrp --help")))
375 (pcomplete-here* (pcmpl-unix-group-names))
148 (while (pcomplete-here (pcomplete-entries)))) 376 (while (pcomplete-here (pcomplete-entries))))
149 377
378;;;###autoload
379(defun pcomplete/chmod ()
380 "Completion for the `chmod' command."
381 (pcomplete-here-using-help "chmod --help"))
382
383;;;###autoload
384(defun pcomplete/touch ()
385 "Completion for the `touch' command."
386 (pcomplete-here-using-help "touch --help"))
387
388;;;###autoload
389(defun pcomplete/df ()
390 "Completion for the `df' command."
391 (pcomplete-here-using-help "df --help"))
392
393;;;###autoload
394(defun pcomplete/du ()
395 "Completion for the `du' command."
396 (pcomplete-here-using-help "du --help"))
397
398;;;###autoload
399(defun pcomplete/stat ()
400 "Completion for the `stat' command."
401 (pcomplete-here-using-help "stat --help"))
402
403;;;###autoload
404(defun pcomplete/sync ()
405 "Completion for the `sync' command."
406 (pcomplete-here-using-help "sync --help"))
407
408;;;###autoload
409(defun pcomplete/truncate ()
410 "Completion for the `truncate' command."
411 (pcomplete-here-using-help "truncate --help"))
412
413;;;###autoload
414(defun pcomplete/echo ()
415 "Completion for the `echo' command."
416 (pcomplete-here-using-help '("echo" "--help")))
417
418;;;###autoload
419(defun pcomplete/test ()
420 "Completion for the `test' command."
421 (pcomplete-here-using-help '("[" "--help")
422 :margin "^ +\\([A-Z]+1 \\)?"))
423;;;###autoload(defalias (intern "pcomplete/[") 'pcomplete/test)
424
425;;;###autoload
426(defun pcomplete/tee ()
427 "Completion for the `tee' command."
428 (pcomplete-here-using-help "tee --help"))
429
430;;;###autoload
431(defun pcomplete/basename ()
432 "Completion for the `basename' command."
433 (pcomplete-here-using-help "basename --help"))
434
435;;;###autoload
436(defun pcomplete/dirname ()
437 "Completion for the `dirname' command."
438 (pcomplete-here-using-help "dirname --help"))
439
440;;;###autoload
441(defun pcomplete/pathchk ()
442 "Completion for the `pathchk' command."
443 (pcomplete-here-using-help "pathchk --help"))
444
445;;;###autoload
446(defun pcomplete/mktemp ()
447 "Completion for the `mktemp' command."
448 (pcomplete-here-using-help "mktemp --help"))
449
450;;;###autoload
451(defun pcomplete/realpath ()
452 "Completion for the `realpath' command."
453 (pcomplete-here-using-help "realpath --help"))
454
455;;;###autoload
456(defun pcomplete/id ()
457 "Completion for the `id' command."
458 (while (string-prefix-p "-" (pcomplete-arg 0))
459 (pcomplete-here (pcomplete-from-help "id --help")))
460 (while (pcomplete-here (pcmpl-unix-user-names))))
461
462;;;###autoload
463(defun pcomplete/groups ()
464 "Completion for the `groups' command."
465 (while (pcomplete-here (pcmpl-unix-user-names))))
466
467;;;###autoload
468(defun pcomplete/who ()
469 "Completion for the `who' command."
470 (pcomplete-here-using-help "who --help"))
471
472;;;###autoload
473(defun pcomplete/date ()
474 "Completion for the `date' command."
475 (pcomplete-here-using-help "date --help"))
476
477;;;###autoload
478(defun pcomplete/nproc ()
479 "Completion for the `nproc' command."
480 (pcomplete-here-using-help "nproc --help"))
481
482;;;###autoload
483(defun pcomplete/uname ()
484 "Completion for the `uname' command."
485 (pcomplete-here-using-help "uname --help"))
486
487;;;###autoload
488(defun pcomplete/hostname ()
489 "Completion for the `hostname' command."
490 (pcomplete-here-using-help "hostname --help"))
491
492;;;###autoload
493(defun pcomplete/uptime ()
494 "Completion for the `uptime' command."
495 (pcomplete-here-using-help "uptime --help"))
496
497;;;###autoload
498(defun pcomplete/chcon ()
499 "Completion for the `chcon' command."
500 (pcomplete-here-using-help "chcon --help"))
501
502;;;###autoload
503(defun pcomplete/runcon ()
504 "Completion for the `runcon' command."
505 (while (string-prefix-p "-" (pcomplete-arg 0))
506 (pcomplete-here (pcomplete-from-help "runcon --help"))
507 (when (pcomplete-match "\\`-[turl]\\'" 0) (pcomplete-here)))
508 (funcall pcomplete-command-completion-function)
509 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
510 pcomplete-default-completion-function)))
511
512;;;###autoload
513(defun pcomplete/chroot ()
514 "Completion for the `chroot' command."
515 (while (string-prefix-p "-" (pcomplete-arg 0))
516 (pcomplete-here (pcomplete-from-help "chroot --help")))
517 (pcomplete-here (pcomplete-dirs))
518 (funcall pcomplete-command-completion-function)
519 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
520 pcomplete-default-completion-function)))
521
522;;;###autoload
523(defun pcomplete/env ()
524 "Completion for the `env' command."
525 (while (string-prefix-p "-" (pcomplete-arg 0))
526 (pcomplete-here (pcomplete-from-help "env --help"))
527 (when (pcomplete-match "\\`-[uCS]\\'") (pcomplete-here)))
528 (while (pcomplete-match "=" 0) (pcomplete-here)) ; FIXME: Complete env vars
529 (funcall pcomplete-command-completion-function)
530 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
531 pcomplete-default-completion-function)))
532
533;;;###autoload
534(defun pcomplete/nice ()
535 "Completion for the `nice' command."
536 (while (string-prefix-p "-" (pcomplete-arg 0))
537 (pcomplete-here (pcomplete-from-help "nice --help"))
538 (pcomplete-here))
539 (funcall pcomplete-command-completion-function)
540 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
541 pcomplete-default-completion-function)))
542
543;;;###autoload
544(defun pcomplete/nohup ()
545 "Completion for the `nohup' command."
546 (while (string-prefix-p "-" (pcomplete-arg 0))
547 (pcomplete-here (pcomplete-from-help "nohup --help")))
548 (funcall pcomplete-command-completion-function)
549 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
550 pcomplete-default-completion-function)))
551
552;;;###autoload
553(defun pcomplete/stdbuf ()
554 "Completion for the `stdbuf' command."
555 (while (string-prefix-p "-" (pcomplete-arg 0))
556 (pcomplete-here (pcomplete-from-help "stdbuf --help"))
557 (when (pcomplete-match "\\`-[ioe]\\'") (pcomplete-here)))
558 (funcall pcomplete-command-completion-function)
559 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
560 pcomplete-default-completion-function)))
561
562;;;###autoload
563(defun pcomplete/timeout ()
564 "Completion for the `timeout' command."
565 (while (string-prefix-p "-" (pcomplete-arg 0))
566 (pcomplete-here (pcomplete-from-help "timeout --help"))
567 (when (pcomplete-match "\\`-[ks]\\'") (pcomplete-here)))
568 (pcomplete-here) ; eat DURATION argument
569 (funcall pcomplete-command-completion-function)
570 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
571 pcomplete-default-completion-function)))
572
573;;;###autoload
574(defun pcomplete/numfmt ()
575 "Completion for the `numfmt' command."
576 (pcomplete-here-using-help "numfmt --help"))
577
578;;;###autoload
579(defun pcomplete/seq ()
580 "Completion for the `seq' command."
581 (pcomplete-here-using-help "seq --help"))
582
583;;; Network commands
150 584
151;; ssh support by Phil Hagelberg. 585;; ssh support by Phil Hagelberg.
152;; https://www.emacswiki.org/cgi-bin/wiki/pcmpl-ssh.el 586;; https://www.emacswiki.org/cgi-bin/wiki/pcmpl-ssh.el
@@ -239,6 +673,18 @@ Includes files as well as host names followed by a colon."
239 (pcomplete-opt "xl(pcmpl-unix-user-names)") 673 (pcomplete-opt "xl(pcmpl-unix-user-names)")
240 (pcmpl-unix-complete-hostname)) 674 (pcmpl-unix-complete-hostname))
241 675
676;;; Miscellaneous
677
678;;;###autoload
679(defun pcomplete/sudo ()
680 "Completion for the `sudo' command."
681 (while (string-prefix-p "-" (pcomplete-arg 0))
682 (pcomplete-here (pcomplete-from-help "sudo --help"))
683 (when (pcomplete-match "\\`-[CDghpRtTUu]\\'") (pcomplete-here)))
684 (funcall pcomplete-command-completion-function)
685 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
686 pcomplete-default-completion-function)))
687
242(provide 'pcmpl-unix) 688(provide 'pcmpl-unix)
243 689
244;;; pcmpl-unix.el ends here 690;;; pcmpl-unix.el ends here
diff --git a/lisp/pcmpl-x.el b/lisp/pcmpl-x.el
index 261a3d4e27b..1ede867c5fb 100644
--- a/lisp/pcmpl-x.el
+++ b/lisp/pcmpl-x.el
@@ -28,6 +28,22 @@
28(eval-when-compile (require 'cl-lib)) 28(eval-when-compile (require 'cl-lib))
29(require 'pcomplete) 29(require 'pcomplete)
30 30
31;;; TeX
32
33;;;###autoload
34(defun pcomplete/tex ()
35 "Completion for the `tex' command."
36 (pcomplete-here-using-help "tex --help"
37 :margin "^\\(?:\\[-no\\]\\)?\\(\\)-"))
38;;;###autoload(defalias 'pcomplete/pdftex 'pcomplete/tex)
39;;;###autoload(defalias 'pcomplete/latex 'pcomplete/tex)
40;;;###autoload(defalias 'pcomplete/pdflatex 'pcomplete/tex)
41
42;;;###autoload
43(defun pcomplete/luatex ()
44 "Completion for the `luatex' command."
45 (pcomplete-here-using-help "luatex --help"))
46;;;###autoload(defalias 'pcomplete/lualatex 'pcomplete/luatex)
31 47
32;;;; tlmgr - https://www.tug.org/texlive/tlmgr.html 48;;;; tlmgr - https://www.tug.org/texlive/tlmgr.html
33 49
@@ -142,6 +158,12 @@
142 (unless (pcomplete-match "^--" 0) 158 (unless (pcomplete-match "^--" 0)
143 (pcomplete-here* (pcomplete-dirs-or-entries))))))) 159 (pcomplete-here* (pcomplete-dirs-or-entries)))))))
144 160
161;;; Grep-like tools
162
163;;;###autoload
164(defun pcomplete/rg ()
165 "Completion for the `rg' command."
166 (pcomplete-here-using-help "rg --help"))
145 167
146;;;; ack - https://betterthangrep.com 168;;;; ack - https://betterthangrep.com
147 169
@@ -288,6 +310,8 @@ long options."
288 (pcmpl-x-ag-options)))) 310 (pcmpl-x-ag-options))))
289 (pcomplete-here* (pcomplete-dirs-or-entries))))) 311 (pcomplete-here* (pcomplete-dirs-or-entries)))))
290 312
313;;; Borland
314
291;;;###autoload 315;;;###autoload
292(defun pcomplete/bcc32 () 316(defun pcomplete/bcc32 ()
293 "Completion function for Borland's C++ compiler." 317 "Completion function for Borland's C++ compiler."
@@ -321,5 +345,24 @@ long options."
321;;;###autoload 345;;;###autoload
322(defalias 'pcomplete/bcc 'pcomplete/bcc32) 346(defalias 'pcomplete/bcc 'pcomplete/bcc32)
323 347
348;;; Network tools
349
350;;;###autoload
351(defun pcomplete/rclone ()
352 "Completion for the `rclone' command."
353 (let ((subcmds (pcomplete-from-help "rclone help"
354 :margin "^ "
355 :argument "[a-z]+"
356 :narrow-start "\n\n")))
357 (while (not (member (pcomplete-arg 1) subcmds))
358 (pcomplete-here (completion-table-merge
359 subcmds
360 (pcomplete-from-help "rclone help flags"))))
361 (let ((subcmd (pcomplete-arg 1)))
362 (while (if (pcomplete-match "\\`-" 0)
363 (pcomplete-here (pcomplete-from-help
364 `("rclone" ,subcmd "--help")))
365 (pcomplete-here (pcomplete-entries)))))))
366
324(provide 'pcmpl-x) 367(provide 'pcmpl-x)
325;;; pcmpl-x.el ends here 368;;; pcmpl-x.el ends here
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 0e3d1df7814..6fe29d9dcfb 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -119,6 +119,9 @@
119;;; Code: 119;;; Code:
120 120
121(require 'comint) 121(require 'comint)
122(eval-when-compile
123 (require 'cl-lib)
124 (require 'rx))
122 125
123(defgroup pcomplete nil 126(defgroup pcomplete nil
124 "Programmable completion." 127 "Programmable completion."
@@ -481,6 +484,14 @@ Same as `pcomplete' but using the standard completion UI."
481 (when completion-ignore-case 484 (when completion-ignore-case
482 (setq table (completion-table-case-fold table))) 485 (setq table (completion-table-case-fold table)))
483 (list beg (point) table 486 (list beg (point) table
487 :annotation-function
488 (lambda (cand)
489 (when (stringp cand)
490 (get-text-property 0 'pcomplete-annotation cand)))
491 :company-docsig
492 (lambda (cand)
493 (when (stringp cand)
494 (get-text-property 0 'pcomplete-help cand)))
484 :predicate pred 495 :predicate pred
485 :exit-function 496 :exit-function
486 ;; If completion is finished, add a terminating space. 497 ;; If completion is finished, add a terminating space.
@@ -1325,6 +1336,133 @@ If specific documentation can't be given, be generic."
1325 (pcomplete-read-hosts pcomplete-hosts-file 'pcomplete--host-name-cache 1336 (pcomplete-read-hosts pcomplete-hosts-file 'pcomplete--host-name-cache
1326 'pcomplete--host-name-cache-timestamp))) 1337 'pcomplete--host-name-cache-timestamp)))
1327 1338
1339;;; Parsing help messages
1340
1341(defvar pcomplete-from-help (make-hash-table :test #'equal)
1342 "Memoization table for function `pcomplete-from-help'.")
1343
1344(cl-defun pcomplete-from-help (command
1345 &rest args
1346 &key
1347 (margin (rx bol (+ " ")))
1348 (argument (rx "-" (+ (any "-" alnum)) (? "=")))
1349 (metavar (rx (? " ")
1350 (or (+ (any alnum "_-"))
1351 (seq "[" (+? nonl) "]")
1352 (seq "<" (+? nonl) ">")
1353 (seq "{" (+? nonl) "}"))))
1354 (separator (rx ", " symbol-start))
1355 (description (rx (* nonl)
1356 (* "\n" (>= 9 " ") (* nonl))))
1357 narrow-start
1358 narrow-end)
1359 "Parse output of COMMAND into a list of completion candidates.
1360
1361COMMAND can be a string to be executed in a shell or a list of
1362strings (program name and arguments). It should print a help
1363message.
1364
1365A list of arguments is collected after each match of MARGIN.
1366Each argument should match ARGUMENT, possibly followed by a match
1367of METAVAR. If a match of SEPARATOR follows, then more
1368argument-metavar pairs are collected. Finally, a match of
1369DESCRIPTION is collected.
1370
1371Keyword ARGS:
1372
1373MARGIN: regular expression after which argument descriptions are
1374 to be found. Parsing continues at the end of the first match
1375 group or, failing that, the entire match.
1376
1377ARGUMENT: regular expression matching an argument name. The
1378 first match group (failing that, the entire match) is collected
1379 as the argument name. Parsing continues at the end of the
1380 second matching group (failing that, the first group or entire
1381 match).
1382
1383METAVAR: regular expression matching an argument parameter name.
1384 The first match group (failing that, the entire match) is
1385 collected as the parameter name and used as completion
1386 annotation. Parsing continues at the end of the second
1387 matching group (failing that, the first group or entire match).
1388
1389SEPARATOR: regular expression matching the separator between
1390 arguments. Parsing continues at the end of the first match
1391 group (failing that, the entire match).
1392
1393DESCRIPTION: regular expression matching the description of an
1394 argument. The first match group (failing that, the entire
1395 match) is collected as the parameter name and used as
1396 completion help. Parsing continues at the end of the first
1397 matching group (failing that, the entire match).
1398
1399NARROW-START, NARROW-END: if non-nil, parsing of the help message
1400 is narrowed to the region between the end of the first match
1401 group (failing that, the entire match) of these regular
1402 expressions."
1403 (with-memoization (gethash (cons command args) pcomplete-from-help)
1404 (with-temp-buffer
1405 (let ((case-fold-search nil)
1406 (default-directory (expand-file-name "~/"))
1407 (command (if (stringp command)
1408 (list shell-file-name
1409 shell-command-switch
1410 command)
1411 command))
1412 i result)
1413 (apply #'call-process (car command) nil t nil (cdr command))
1414 (goto-char (point-min))
1415 (narrow-to-region (or (and narrow-start
1416 (re-search-forward narrow-start nil t)
1417 (or (match-beginning 1) (match-beginning 0)))
1418 (point-min))
1419 (or (and narrow-end
1420 (re-search-forward narrow-end nil t)
1421 (or (match-beginning 1) (match-beginning 0)))
1422 (point-max)))
1423 (goto-char (point-min))
1424 (while (re-search-forward margin nil t)
1425 (goto-char (or (match-end 1) (match-end 0)))
1426 (setq i 0)
1427 (while (and (or (zerop i)
1428 (and (looking-at separator)
1429 (goto-char (or (match-end 1)
1430 (match-end 0)))))
1431 (looking-at argument))
1432 (setq i (1+ i))
1433 (goto-char (seq-some #'match-end '(2 1 0)))
1434 (push (or (match-string 1) (match-string 0)) result)
1435 (when (looking-at metavar)
1436 (goto-char (seq-some #'match-end '(2 1 0)))
1437 (put-text-property 0 1
1438 'pcomplete-annotation
1439 (or (match-string 1) (match-string 0))
1440 (car result))))
1441 (when (looking-at description)
1442 (goto-char (seq-some #'match-end '(2 1 0)))
1443 (let ((help (string-clean-whitespace
1444 (or (match-string 1) (match-string 0))))
1445 (items (take i result)))
1446 (while items
1447 (put-text-property 0 1 'pcomplete-help help
1448 (pop items))))))
1449 (nreverse result)))))
1450
1451(defun pcomplete-here-using-help (command &rest args)
1452 "Perform completion for a simple command.
1453Offer switches and directory entries as completion candidates.
1454The switches are obtained by calling `pcomplete-from-help' with
1455COMMAND and ARGS as arguments."
1456 (while (cond
1457 ((string= "--" (pcomplete-arg 1))
1458 (while (pcomplete-here (pcomplete-entries))))
1459 ((pcomplete-match "\\`--[^=]+=\\(.*\\)" 0)
1460 (pcomplete-here (pcomplete-entries)
1461 (pcomplete-match-string 1 0)))
1462 ((string-prefix-p "-" (pcomplete-arg 0))
1463 (pcomplete-here (apply #'pcomplete-from-help command args)))
1464 (t (pcomplete-here (pcomplete-entries))))))
1465
1328(provide 'pcomplete) 1466(provide 'pcomplete)
1329 1467
1330;;; pcomplete.el ends here 1468;;; pcomplete.el ends here
diff --git a/test/lisp/pcomplete-tests.el b/test/lisp/pcomplete-tests.el
new file mode 100644
index 00000000000..00a82502f30
--- /dev/null
+++ b/test/lisp/pcomplete-tests.el
@@ -0,0 +1,100 @@
1;;; pcomplete-tests.el --- Tests for pcomplete.el -*- lexical-binding: t -*-
2
3;; Copyright (C) 2022 Free Software Foundation, Inc.
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software: you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;;; Code:
23
24(require 'ert)
25(require 'pcomplete)
26
27(ert-deftest pcomplete-test-parse-gpg-help ()
28 (cl-letf ((pcomplete-from-help (make-hash-table :test #'equal))
29 ((symbol-function 'call-process)
30 (lambda (&rest _) (insert "\
31gpg (GnuPG) 2.3.7
32
33Commands:
34
35 -s, --sign make a signature
36 --clear-sign make a clear text signature
37 -b, --detach-sign make a detached signature
38 --tofu-policy VALUE set the TOFU policy for a key
39
40Options to specify keys:
41 -r, --recipient USER-ID encrypt for USER-ID
42 -u, --local-user USER-ID use USER-ID to sign or decrypt
43
44(See the man page for a complete listing of all commands and options)
45
46Examples:
47
48 -se -r Bob [file] sign and encrypt for user Bob
49 --clear-sign [file] make a clear text signature
50"))))
51 (should
52 (equal-including-properties
53 (pcomplete-from-help "gpg --help" :narrow-end "^ -se")
54 '(#("-s" 0 1 (pcomplete-help "make a signature"))
55 #("--sign" 0 1 (pcomplete-help "make a signature"))
56 #("--clear-sign" 0 1 (pcomplete-help "make a clear text signature"))
57 #("-b" 0 1 (pcomplete-help "make a detached signature"))
58 #("--detach-sign" 0 1 (pcomplete-help "make a detached signature"))
59 #("--tofu-policy" 0 1
60 (pcomplete-help "set the TOFU policy for a key" pcomplete-annotation " VALUE"))
61 #("-r" 0 1 (pcomplete-help "encrypt for USER-ID"))
62 #("--recipient" 0 1
63 (pcomplete-help "encrypt for USER-ID" pcomplete-annotation " USER-ID"))
64 #("-u" 0 1
65 (pcomplete-help "use USER-ID to sign or decrypt"))
66 #("--local-user" 0 1
67 (pcomplete-help "use USER-ID to sign or decrypt" pcomplete-annotation " USER-ID")))))))
68
69(ert-deftest pcomplete-test-parse-git-help ()
70 (cl-letf ((pcomplete-from-help (make-hash-table :test #'equal))
71 ((symbol-function 'call-process)
72 (lambda (&rest _) (insert "\
73usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
74 [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
75 [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
76 [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
77 [--super-prefix=<path>] [--config-env=<name>=<envvar>]
78 <command> [<args>]
79"))))
80 (should
81 (equal-including-properties
82 (pcomplete-from-help "git help"
83 :margin "\\(\\[\\)-"
84 :separator " | "
85 :description "\\`")
86 '("-v" "--version" "-h" "--help"
87 #("-C" 0 1 (pcomplete-annotation " <path>"))
88 #("-c" 0 1 (pcomplete-annotation " <name>"))
89 #("--exec-path" 0 1 (pcomplete-annotation "[=<path>]"))
90 "--html-path" "--man-path" "--info-path"
91 "-p" "--paginate" "-P" "--no-pager"
92 "--no-replace-objects" "--bare"
93 #("--git-dir=" 0 1 (pcomplete-annotation "<path>"))
94 #("--work-tree=" 0 1 (pcomplete-annotation "<path>"))
95 #("--namespace=" 0 1 (pcomplete-annotation "<name>"))
96 #("--super-prefix=" 0 1 (pcomplete-annotation "<path>"))
97 #("--config-env=" 0 1 (pcomplete-annotation "<name>")))))))
98
99(provide 'pcomplete-tests)
100;;; pcomplete-tests.el ends here