Subject: bug#78661: 30.1; pcomplete/dnf does not work with dnf5

From: Rahguzar <[email-masked]>
Date: Sun, 22 Jun 2025 13:25:28 +0500

Hi Augusto and Eli,

Augusto Stoffel <[email-masked]> writes:

> On Sat, 14 Jun 2025 at 16:28, Eli Zaretskii wrote:
>
>> Augusto, any comments or suggestions?
>>
>>> Cc: [email-masked]
>>> Date: Sun, 01 Jun 2025 12:49:02 +0500
>>> From:  Rahguzar via "Bug reports for GNU Emacs,
>>>  the Swiss army knife of text editors" <[email-masked]>
>>> 
>>> 
>>> Dear Emacs maintainers,
>>> 
>>> Recently released Fedora 42 defaults to dnf 5. This had the effect of
>>> breaking pcomplete completions provided by pcomplete/dnf. I have worked
>>> around the problem in my init file by means of advice.
>>> 
>>> I can send a patch with the changes needed to make completion work with
>>> dnf 5. However, I want to make some additional changes too so I would
>>> like to ask some questions first (I have cced Augusto Stoffel since he
>>> added the code in question):
>>> 
>>> 1) I think "upgrade" should offer completions from installed packages
>>> but currently it offers completions from all packages. Is there a reason?
>
> No, this change makes sense.
>
>>> 2) Currently completions of package names come with a very long version
>>> string which I have never needed and have to delete by hand. Is it ok to
>>> remove that?
>
> I think I used to get that with shell completions so there might be a
> reason it was done that way, but seems reasonable to me to remove the
> versions.
>
>>> 3) The most reliable way of getting package names without version string
>>> is (in my opinion) to use the dnf repoquery command. It also has the
>>> (slight) advantage of not requiring sqlite3. However, although I can get
>>> the list of installed and available packages from it, I can't get the
>>> list of 'not installed' packages. On the other hand the current version
>>> seems to return the same list for 'not installed' and available packages.
>>> Is it ok to just remove the 'not installed' case and use the available
>>> packages instead?
>
> I think this is fine too.

I have attached a patch implementing these changes. It also provides
better support for dnf repoquery --what* commands and adds some aliases
to the list of subcommands for which completion of package names is
provided.

Rahguzar

>From def6d745db5f60a01bc8042d004d65bb6786a78c Mon Sep 17 00:00:00 2001
From: Rahguzar <[email-masked]>
Date: Sun, 22 Jun 2025 13:13:06 +0500
Subject: [PATCH] * lisp/pcmpl-rpm.el: generate completions for dnf5

(pcmpl-rpm-dnf-cache-file): remove now unused variable
(pcmpl-rpm--dnf-packages): use dnf repoquery
(pcomplete/dnf): adjust regexps to allow for help format of
dnf5. New regexps works for both dnf4 and dnf5. Add aliases
for better subcommand completion. Better support for
dnf repoquery command.
---
 lisp/pcmpl-rpm.el | 50 +++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 26 deletions(-)

diff --git a/lisp/pcmpl-rpm.el b/lisp/pcmpl-rpm.el
index d92e94a050e..239500c37dd 100644
--- a/lisp/pcmpl-rpm.el
+++ b/lisp/pcmpl-rpm.el
@@ -381,43 +381,41 @@ pcomplete/rpm
 
 ;;; DNF
 
-(defvar pcmpl-rpm-dnf-cache-file "/var/cache/dnf/packages.db"
-  "Location of the DNF cache.")
-
 (defun pcmpl-rpm--dnf-packages (status)
-  (when (and (file-exists-p pcmpl-rpm-dnf-cache-file)
-             (executable-find "sqlite3"))
-    (with-temp-message
-        "Getting list of packages..."
-      (process-lines "sqlite3" "-batch" "-init" "/dev/null"
-                     pcmpl-rpm-dnf-cache-file
-                     (pcase-exhaustive status
-                       ('available "select pkg from available")
-                       ('installed "select pkg from installed")
-                       ('not-installed "\
-select pkg from available where pkg not in (select pkg from installed)"))))))
+  "Return packages matching STATUS.
+STATUS should be one of --available or --installed."
+  (with-temp-message
+      "Getting list of packages..."
+    (process-lines "dnf" "--cacheonly" "repoquery" "--queryformat=%{name}\\n"
+                   status)))
 
 ;;;###autoload
 (defun pcomplete/dnf ()
   "Completion for the `dnf' command."
-  (let ((subcmds (pcomplete-from-help "dnf help"
-                                      :margin "^\\(\\)[a-z-]+  "
-                                      :argument "[a-z-]+")))
+  (let ((subcmds (pcomplete-from-help "dnf --help"
+                                      :margin (rx bol (group (* " "))
+                                                  (one-or-more (any "a-z" "-")) "  ")
+                                      :argument (rx (not "-") (1+ (any "a-z" "-"))))))
     (while (not (member (pcomplete-arg 1) subcmds))
       (pcomplete-here (completion-table-merge
                        subcmds
-                       (pcomplete-from-help "dnf help"))))
+                       (pcomplete-from-help "dnf --help"))))
     (let ((subcmd (pcomplete-arg 1)))
       (while (pcase subcmd
                ((guard (pcomplete-match "\\`-" 0))
-                (pcomplete-here
-                 (pcomplete-from-help `("dnf" "help" ,subcmd))))
-               ((or "downgrade" "reinstall" "remove")
-                (pcomplete-here (pcmpl-rpm--dnf-packages 'installed)))
-               ((or "install" "mark" "reinstall" "upgrade")
-                (pcomplete-here (pcmpl-rpm--dnf-packages 'not-installed)))
-               ((or "builddep" "changelog" "info" "list" "repoquery" "updateinfo")
-                (pcomplete-here (pcmpl-rpm--dnf-packages 'available))))))))
+                (if-let* (((pcomplete-match (rx bos "--what" (* (not "=")) "="
+                                                (group (* any)) eos)
+                                            0))
+                          (stub (pcomplete-match-string 1 0)))
+                    (pcomplete-here (pcmpl-rpm--dnf-packages "--available") stub)
+                  (pcomplete-here
+                   (pcomplete-from-help `("dnf" ,subcmd "--help")))))
+               ((or "downgrade" "dg" "upgrade" "up" "update" "reinstall" "rei"
+                    "remove" "rm")
+                (pcomplete-here (pcmpl-rpm--dnf-packages "--installed")))
+               ((or "builddep" "changelog" "info" "if" "install" "in" "list" "ls"
+                    "mark" "repoquery" "rq" "advisory" "updateinfo")
+                (pcomplete-here (pcmpl-rpm--dnf-packages "--available"))))))))
 
 (provide 'pcmpl-rpm)
 
-- 
2.49.0