* guix/scripts.scm (%command-categories): Add extension category.
* guix/ui.scm (command-files): Accept an optional directory argument.
(extension-directories): New procedure.
(commands): Use it.
(show-guix-help): Hide empty categories.
(run-guix-command): Try loading an extension if there is no Guix command.
---
 guix/scripts.scm |  4 +++-
 guix/ui.scm      | 60 +++++++++++++++++++++++++++++++++++-------------
 2 files changed, 47 insertions(+), 17 deletions(-)

diff --git a/guix/scripts.scm b/guix/scripts.scm
index 9792aaebe9..34cba35401 100644
--- a/guix/scripts.scm
+++ b/guix/scripts.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2014 Deck Pickard <deck.r.pick...@gmail.com>
 ;;; Copyright © 2015, 2016 Alex Kost <alez...@gmail.com>
 ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <jann...@gnu.org>
+;;; Copyright © 2021 Ricardo Wurmus <rek...@elephly.net>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -86,7 +87,8 @@
   (development (G_ "software development commands"))
   (packaging   (G_ "packaging commands"))
   (plumbing    (G_ "plumbing commands"))
-  (internal    (G_ "internal commands")))
+  (internal    (G_ "internal commands"))
+  (extension   (G_ "extension commands")))
 
 (define-syntax define-command
   (syntax-rules (category synopsis)
diff --git a/guix/ui.scm b/guix/ui.scm
index 0a1c9bd615..2ecfb53c7b 100644
--- a/guix/ui.scm
+++ b/guix/ui.scm
@@ -2046,24 +2046,36 @@ contain a 'define-command' form."
           (_
            (loop)))))))
 
-(define (command-files)
+(define* (command-files #:optional directory)
   "Return the list of source files that define Guix sub-commands."
-  (define directory
-    (and=> (search-path %load-path "guix.scm")
-           (compose (cut string-append <> "/guix/scripts")
-                    dirname)))
+  (define directory*
+    (or directory
+        (and=> (search-path %load-path "guix.scm")
+               (compose (cut string-append <> "/guix/scripts")
+                        dirname))))
 
   (define dot-scm?
     (cut string-suffix? ".scm" <>))
 
-  (if directory
-      (map (cut string-append directory "/" <>)
-           (scandir directory dot-scm?))
+  (if directory*
+      (map (cut string-append directory* "/" <>)
+           (scandir directory* dot-scm?))
       '()))
 
+(define (extension-directories)
+  "Return the list of directories containing Guix extensions."
+  (filter-map (lambda (directory)
+                (let ((scripts (string-append directory "/guix/scripts")))
+                  (and (file-exists? scripts) scripts)))
+              (parse-path
+               (getenv "GUIX_EXTENSIONS_PATH"))))
+
 (define (commands)
   "Return the list of commands, alphabetically sorted."
-  (filter-map source-file-command (command-files)))
+  (filter-map source-file-command
+              (append (command-files)
+                      (append-map command-files
+                                  (extension-directories)))))
 
 (define (show-guix-help)
   (define (internal? command)
@@ -2098,9 +2110,14 @@ Run COMMAND with ARGS.\n"))
                 (('internal . _)
                  #t)                              ;hide internal commands
                 ((category . synopsis)
-                 (format #t "~%  ~a~%" (G_ synopsis))
-                 (display-commands (filter (category-predicate category)
-                                           commands))))
+                 (let ((relevant-commands (filter (category-predicate category)
+                                                  commands)))
+                   ;; Only print categories that contain commands.
+                   (match relevant-commands
+                     ((one . more)
+                      (format #t "~%  ~a~%" (G_ synopsis))
+                      (display-commands relevant-commands))
+                     (_ #f)))))
               categories))
   (show-bug-report-information))
 
@@ -2111,10 +2128,21 @@ found."
     (catch 'misc-error
       (lambda ()
         (resolve-interface `(guix scripts ,command)))
-      (lambda -
-        (format (current-error-port)
-                (G_ "guix: ~a: command not found~%") command)
-        (show-guix-usage))))
+      (lambda _
+        ;; Check if there is a matching extension.
+        (catch 'misc-error
+          (lambda ()
+            (match (search-path (extension-directories)
+                                (format #f "~a.scm" command))
+              (file
+               (load file)
+               (resolve-interface `(guix scripts ,command)))
+              (_
+               (throw 'misc-error))))
+          (lambda _
+            (format (current-error-port)
+                    (G_ "guix: ~a: command not found~%") command)
+            (show-guix-usage))))))
 
   (let ((command-main (module-ref module
                                   (symbol-append 'guix- command))))
-- 
2.29.2



Reply via email to