aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/pcomplete.el
diff options
context:
space:
mode:
authorAugusto Stoffel2022-09-08 11:09:42 +0200
committerLars Ingebrigtsen2022-09-14 21:58:04 +0200
commita9941269683fe50673d0aa81feefb7a9d3d8a6b9 (patch)
tree566c9ecd3afb90b58607c71ad794cee14ba7b823 /lisp/pcomplete.el
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.
Diffstat (limited to 'lisp/pcomplete.el')
-rw-r--r--lisp/pcomplete.el138
1 files changed, 138 insertions, 0 deletions
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