~ruther/guix-local

23459fa59b3e96baa07da1ae4c78af70c255ac20 — Alex Kost 11 years ago 7c3c037
emacs: Improve interface for working with multiple profiles.

Suggested by David Thompson, Ludovic Courtès and Mathieu Lirzin.

* emacs/guix-base.el (guix-profile-prompt): New procedure.
  (guix-set-current-profile): Use it.
  (guix-buffer-name-simple, guix-buffer-name-default, guix-buffer-name): New
  procedures.
  (guix-buffer-name-function, guix-profile): New variables.
  (guix-set-vars, guix-get-entries, guix-get-show-entries, guix-set-buffer,
  guix-history-call, guix-process-package-actions,
  guix-continue-package-operation-p, guix-delete-generations,
  guix-switch-to-generation): Add 'profile' argument.
* emacs/guix.el (guix-get-show-packages, guix-get-show-generations,
  guix-search-by-name, guix-search-by-regexp, guix-installed-packages,
  guix-obsolete-packages, guix-all-available-packages,
  guix-newest-available-packages, guix-generations, guix-generations-by-time):
  Likewise.
  (guix-last-generations): New command.
* emacs/guix-info.el: Adjust for using 'profile' argument where needed.
* emacs/guix-list.el: Likewise.
* doc/emacs.texi (Emacs Commands): Document 'guix-last-generations' and using
  "C-u" for commands.
  (Emacs Buffer Names): Document 'guix-buffer-name-function'.
5 files changed, 272 insertions(+), 136 deletions(-)

M doc/emacs.texi
M emacs/guix-base.el
M emacs/guix-info.el
M emacs/guix-list.el
M emacs/guix.el
M doc/emacs.texi => doc/emacs.texi +38 -23
@@ 76,19 76,26 @@ can be displayed in a ``list'' or ``info'' buffer.
@node Emacs Commands
@subsubsection Commands

You may use the following commands to display packages and generations:
All commands for displaying packages and generations use the current
profile, which can be changed with
@kbd{M-x@tie{}guix-set-current-profile}.  Alternatively, if you call any
of these commands with prefix argument (@kbd{C-u}), you will be prompted
for a profile just for that command.

Commands for displaying packages:

@table @kbd

@item M-x guix-all-available-packages
@itemx M-x guix-newest-available-packages
Display all/newest available packages.

@item M-x guix-installed-packages
Display all packages installed in the current profile.
Display all installed packages.

@item M-x guix-obsolete-packages
Display obsolete packages (the packages that are installed in the
current profile but cannot be found among available packages).
Display obsolete packages (the packages that are installed in a profile
but cannot be found among available packages).

@item M-x guix-search-by-name
Display package(s) with the specified name.


@@ 98,29 105,33 @@ Search for packages by a specified regexp.  By default ``name'',
``synopsis'' and ``description'' of the packages will be searched.  This
can be changed by modifying @code{guix-search-params} variable.

@item M-x guix-generations
List generations for the current profile.  With numeric prefix, show so
many last generations.

@item M-x guix-generations-by-time
List generations matching time period.  You'll be prompted for the
period using Org mode time prompt based on Emacs calendar (@pxref{The
date/time prompt,,, org, Org Mode Manual}).

@end table

By default commands for displaying packages display each output on a
separate line.  If you prefer to see a list of packages---i.e., a list
with a package per line, use the following setting:
By default, these commands display each output on a separate line.  If
you prefer to see a list of packages---i.e., a list with a package per
line, use the following setting:

@example
(setq guix-package-list-type 'package)
@end example

It is possible to change the currently used profile with
@kbd{M-x@tie{}guix-set-current-profile}.  This has the same effect as
specifying @code{--profile} option for @command{guix package}
(@pxref{Invoking guix package}).
Commands for displaying generations:

@table @kbd

@item M-x guix-generations
List all the generations.

@item M-x guix-last-generations
List the @var{N} last generations.  You will be prompted for the number
of generations.

@item M-x guix-generations-by-time
List generations matching time period.  You will be prompted for the
period using Org mode time prompt based on Emacs calendar (@pxref{The
date/time prompt,,, org, Org Mode Manual}).

@end table

@node Emacs General info
@subsubsection General information


@@ 304,10 315,13 @@ changed with the following variables:
@item guix-generation-info-buffer-name
@item guix-repl-buffer-name
@item guix-internal-repl-buffer-name
@item guix-temp-buffer-name
@end table

For example if you want to display all types of results in a single
By default, the name of a profile is also displayed in a ``list'' or
``info'' buffer name.  To change this behavior, use
@code{guix-buffer-name-function} variable.

For example, if you want to display all types of results in a single
buffer (in such case you will probably use a history (@kbd{l}/@kbd{r})
extensively), you may do it like this:



@@ 319,7 333,8 @@ extensively), you may do it like this:
   guix-generation-list-buffer-name name
   guix-package-info-buffer-name    name
   guix-output-info-buffer-name     name
   guix-generation-info-buffer-name name))
   guix-generation-info-buffer-name name
   guix-buffer-name-function        #'guix-buffer-name-simple))
@end example

@node Emacs Keymaps

M emacs/guix-base.el => emacs/guix-base.el +125 -54
@@ 48,6 48,18 @@
(defvar guix-current-profile guix-default-profile
  "Current profile.")

(defun guix-profile-prompt (&optional default)
  "Prompt for profile and return it.
Use DEFAULT as a start directory.  If it is nil, use
`guix-current-profile'."
  (let* ((path (read-file-name "Profile: "
                               (file-name-directory
                                (or default guix-current-profile))))
         (path (directory-file-name (expand-file-name path))))
    (if (string= path guix-user-profile)
        guix-default-profile
      path)))

(defun guix-set-current-profile (path)
  "Set `guix-current-profile' to PATH.
Interactively, prompt for PATH.  With prefix, use


@@ 55,15 67,10 @@ Interactively, prompt for PATH.  With prefix, use
  (interactive
   (list (if current-prefix-arg
             guix-default-profile
           (read-file-name "Set profile: "
                           (file-name-directory guix-current-profile)))))
  (let ((path (directory-file-name (expand-file-name path))))
    (setq guix-current-profile
          (if (string= path guix-user-profile)
              guix-default-profile
            path))
    (message "Current profile has been set to '%s'."
             guix-current-profile)))
           (guix-profile-prompt))))
  (setq guix-current-profile path)
  (message "Current profile has been set to '%s'."
           guix-current-profile))


;;; Parameters of the entries


@@ 209,6 216,56 @@ If `all', update all Guix buffers (not recommended)."
                 (const :tag "Update all Guix buffers" all))
  :group 'guix)

(defcustom guix-buffer-name-function #'guix-buffer-name-default
  "Function used to define name of a buffer for displaying information.
The function is called with 4 arguments: PROFILE, BUFFER-TYPE,
ENTRY-TYPE, SEARCH-TYPE.  See `guix-get-entries' for the meaning
of the arguments."
  :type '(choice (function-item guix-buffer-name-default)
                 (function-item guix-buffer-name-simple)
                 (function :tag "Other function"))
  :group 'guix)

(defun guix-buffer-name-simple (_profile buffer-type entry-type
                                &optional _search-type)
  "Return name of a buffer used for displaying information.
The name is defined by `guix-ENTRY-TYPE-BUFFER-TYPE-buffer-name'
variable."
  (symbol-value
   (guix-get-symbol "buffer-name" buffer-type entry-type)))

(defun guix-buffer-name-default (profile buffer-type entry-type
                                 &optional _search-type)
  "Return name of a buffer used for displaying information.
The name is almost the same as the one defined by
`guix-buffer-name-simple' except the PROFILE name is added to it."
  (let ((simple-name (guix-buffer-name-simple
                      profile buffer-type entry-type))
        (profile-name (file-name-base (directory-file-name profile)))
        (re (rx string-start
                (group (? "*"))
                (group (*? any))
                (group (? "*"))
                string-end)))
    (or (string-match re simple-name)
        (error "Unexpected error in defining guix buffer name"))
    (let ((first*    (match-string 1 simple-name))
          (name-body (match-string 2 simple-name))
          (last*     (match-string 3 simple-name)))
      ;; Handle the case when buffer name is wrapped by '*'.
      (if (and (string= "*" first*)
               (string= "*" last*))
          (concat "*" name-body ": " profile-name "*")
        (concat simple-name ": " profile-name)))))

(defun guix-buffer-name (profile buffer-type entry-type search-type)
  "Return name of a buffer used for displaying information.
See `guix-buffer-name-function' for details."
  (let ((fun (if (functionp guix-buffer-name-function)
                 guix-buffer-name-function
               #'guix-buffer-name-default)))
    (funcall fun profile buffer-type entry-type search-type)))

(defun guix-switch-to-buffer (buffer)
  "Switch to a 'list' or 'info' BUFFER."
  (pop-to-buffer buffer


@@ 246,6 303,10 @@ See `guix-update-after-operation' for details."

;;; Common definitions for buffer types

(defvar-local guix-profile nil
  "Profile used for the current buffer.")
(put 'guix-profile 'permanent-local t)

(defvar-local guix-entries nil
  "List of the currently displayed entries.
Each element of the list is alist with entry info of the


@@ 273,13 334,16 @@ VAL is a value of this parameter.")
  "Values of the current search.")
(put 'guix-search-vals 'permanent-local t)

(defsubst guix-set-vars (entries buffer-type entry-type
(defsubst guix-set-vars (profile entries buffer-type entry-type
                         search-type search-vals)
  (setq guix-entries     entries
        guix-buffer-type buffer-type
        guix-entry-type  entry-type
        guix-search-type search-type
        guix-search-vals search-vals))
  "Set local variables for the current Guix buffer."
  (setq default-directory profile
        guix-profile      profile
        guix-entries      entries
        guix-buffer-type  buffer-type
        guix-entry-type   entry-type
        guix-search-type  search-type
        guix-search-vals  search-vals))

(defun guix-get-symbol (postfix buffer-type &optional entry-type)
  (intern (concat "guix-"


@@ 416,7 480,7 @@ information)."
                 (const :tag "Display outputs" output))
  :group 'guix)

(defun guix-get-entries (entry-type search-type search-vals
(defun guix-get-entries (profile entry-type search-type search-vals
                         &optional params)
  "Search for entries of ENTRY-TYPE.



@@ 432,26 496,25 @@ SEARCH-TYPE may be one of the following symbols:
  `all-available', `newest-available', `installed', `obsolete',
  `generation'.

- If ENTRY-TYPE is `generation': `id', `last', `all'.
- If ENTRY-TYPE is `generation': `id', `last', `all', `time'.

PARAMS is a list of parameters for receiving.  If nil, get
information with all available parameters."
  (guix-eval-read (guix-make-guile-expression
                   'entries
                   guix-current-profile params
                   entry-type search-type search-vals)))
                   profile params entry-type search-type search-vals)))

(defun guix-get-show-entries (buffer-type entry-type search-type
                                          &rest search-vals)
(defun guix-get-show-entries (profile buffer-type entry-type search-type
                              &rest search-vals)
  "Search for ENTRY-TYPE entries and show results in BUFFER-TYPE buffer.
See `guix-get-entries' for the meaning of SEARCH-TYPE and SEARCH-VALS."
  (let ((entries (guix-get-entries entry-type search-type search-vals
  (let ((entries (guix-get-entries profile entry-type search-type search-vals
                                   (guix-get-params-for-receiving
                                    buffer-type entry-type))))
    (guix-set-buffer entries buffer-type entry-type
    (guix-set-buffer profile entries buffer-type entry-type
                     search-type search-vals)))

(defun guix-set-buffer (entries buffer-type entry-type search-type
(defun guix-set-buffer (profile entries buffer-type entry-type search-type
                        search-vals &optional history-replace no-display)
  "Set up BUFFER-TYPE buffer for displaying ENTRY-TYPE ENTRIES.



@@ 465,16 528,16 @@ otherwise add the new one.

If NO-DISPLAY is non-nil, do not switch to the buffer."
  (when entries
    (let ((buf (if (eq major-mode (guix-get-symbol
                                   "mode" buffer-type entry-type))
    (let ((buf (if (and (eq major-mode
                            (guix-get-symbol "mode" buffer-type entry-type))
                        (equal guix-profile profile))
                   (current-buffer)
                 (get-buffer-create
                  (symbol-value
                   (guix-get-symbol "buffer-name"
                                    buffer-type entry-type))))))
                  (guix-buffer-name profile buffer-type
                                    entry-type search-type)))))
      (with-current-buffer buf
        (guix-show-entries entries buffer-type entry-type)
        (guix-set-vars entries buffer-type entry-type
        (guix-set-vars profile entries buffer-type entry-type
                       search-type search-vals)
        (funcall (if history-replace
                     #'guix-history-replace


@@ 494,18 557,18 @@ If NO-DISPLAY is non-nil, do not switch to the buffer."
             entries entry-type)
    (goto-char (point-min))))

(defun guix-history-call (entries buffer-type entry-type
(defun guix-history-call (profile entries buffer-type entry-type
                          search-type search-vals)
  "Function called for moving by history."
  (guix-show-entries entries buffer-type entry-type)
  (guix-set-vars entries buffer-type entry-type
  (guix-set-vars profile entries buffer-type entry-type
                 search-type search-vals)
  (guix-result-message entries entry-type search-type search-vals))

(defun guix-make-history-item ()
  "Make and return a history item for the current buffer."
  (list #'guix-history-call
        guix-entries guix-buffer-type guix-entry-type
        guix-profile guix-entries guix-buffer-type guix-entry-type
        guix-search-type guix-search-vals))

(defun guix-get-params-for-receiving (buffer-type entry-type)


@@ 529,10 592,11 @@ See `revert-buffer' for the meaning of NOCONFIRM."
                              guix-buffer-type guix-entry-type))
            (y-or-n-p "Update current information? "))
    (let ((entries (guix-get-entries
                    guix-entry-type guix-search-type guix-search-vals
                    guix-profile guix-entry-type
                    guix-search-type guix-search-vals
                    (guix-get-params-for-receiving guix-buffer-type
                                                   guix-entry-type))))
      (guix-set-buffer entries guix-buffer-type guix-entry-type
      (guix-set-buffer guix-profile entries guix-buffer-type guix-entry-type
                       guix-search-type guix-search-vals t t))))

(defun guix-redisplay-buffer ()


@@ 719,8 783,9 @@ VARIABLE is a name of an option variable.")
      guix-operation-option-true-string
    guix-operation-option-false-string))

(defun guix-process-package-actions (actions &optional operation-buffer)
  "Process package ACTIONS.
(defun guix-process-package-actions (profile actions
                                     &optional operation-buffer)
  "Process package ACTIONS on PROFILE.
Each action is a list of the form:

  (ACTION-TYPE PACKAGE-SPEC ...)


@@ 738,23 803,25 @@ PACKAGE-SPEC should have the following form: (ID [OUTPUT] ...)."
                ((remove delete) (setq remove (append remove specs))))))
          actions)
    (when (guix-continue-package-operation-p
           profile
           :install install :upgrade upgrade :remove remove)
      (guix-eval-in-repl
       (guix-make-guile-expression
        'process-package-actions guix-current-profile
        'process-package-actions profile
        :install install :upgrade upgrade :remove remove
        :use-substitutes? (or guix-use-substitutes 'f)
        :dry-run? (or guix-dry-run 'f))
       (and (not guix-dry-run) operation-buffer)))))

(cl-defun guix-continue-package-operation-p (&key install upgrade remove)
(cl-defun guix-continue-package-operation-p (profile
                                             &key install upgrade remove)
  "Return non-nil if a package operation should be continued.
Ask a user if needed (see `guix-operation-confirm').
INSTALL, UPGRADE, REMOVE are 'package action specifications'.
See `guix-process-package-actions' for details."
  (or (null guix-operation-confirm)
      (let* ((entries (guix-get-entries
                       'package 'id
                       profile 'package 'id
                       (append (mapcar #'car install)
                               (mapcar #'car upgrade)
                               (mapcar #'car remove))


@@ 768,6 835,7 @@ See `guix-process-package-actions' for details."
                (setq-local cursor-type nil)
                (setq buffer-read-only nil)
                (erase-buffer)
                (insert "Profile: " profile "\n\n")
                (guix-insert-package-strings install-strings "install")
                (guix-insert-package-strings upgrade-strings "upgrade")
                (guix-insert-package-strings remove-strings "remove")


@@ 861,29 929,32 @@ Return non-nil, if the operation should be continued; nil otherwise."
                 guix-operation-option-separator)))
  (force-mode-line-update))

(defun guix-delete-generations (generations &optional operation-buffer)
  "Delete GENERATIONS.
(defun guix-delete-generations (profile generations
                                &optional operation-buffer)
  "Delete GENERATIONS from PROFILE.
Each element from GENERATIONS is a generation number."
  (when (or (not guix-operation-confirm)
              (y-or-n-p
               (let ((count (length generations)))
                 (if (> count 1)
                     (format "Delete %d generations? " count)
                   (format "Delete generation number %d? "
                           (car generations))))))
            (y-or-n-p
             (let ((count (length generations)))
               (if (> count 1)
                   (format "Delete %d generations from profile '%s'? "
                           count profile)
                 (format "Delete generation %d from profile '%s'? "
                         (car generations) profile)))))
    (guix-eval-in-repl
     (guix-make-guile-expression
      'delete-generations* guix-current-profile generations)
      'delete-generations* profile generations)
     operation-buffer)))

(defun guix-switch-to-generation (generation &optional operation-buffer)
  "Switch `guix-current-profile' to GENERATION number."
(defun guix-switch-to-generation (profile generation
                                  &optional operation-buffer)
  "Switch PROFILE to GENERATION."
  (when (or (not guix-operation-confirm)
            (y-or-n-p (format "Switch current profile to generation %d? "
                              generation)))
            (y-or-n-p (format "Switch profile '%s' to generation %d? "
                              profile generation)))
    (guix-eval-in-repl
     (guix-make-guile-expression
      'switch-to-generation guix-current-profile generation)
      'switch-to-generation profile generation)
     operation-buffer)))

(provide 'guix-base)

M emacs/guix-info.el => emacs/guix-info.el +7 -6
@@ 334,8 334,8 @@ VAL is a list, call the function on each element of this list."
  'face 'guix-package-info-name-button
  'help-echo "Describe this package"
  'action (lambda (btn)
            (guix-get-show-entries 'info guix-package-info-type 'name
                                   (button-label btn))))
            (guix-get-show-entries guix-profile 'info guix-package-info-type
                                   'name (button-label btn))))

(defun guix-info-insert-action-button (label action &optional message
                                             &rest properties)


@@ 558,6 558,7 @@ ENTRY is an alist with package info."
     type-str
     (lambda (btn)
       (guix-process-package-actions
        guix-profile
        `((,(button-get btn 'action-type) (,(button-get btn 'id)
                                           ,(button-get btn 'output))))
        (current-buffer)))


@@ 631,15 632,15 @@ ENTRY is an alist with package info."
  (guix-info-insert-action-button
   "Packages"
   (lambda (btn)
     (guix-get-show-entries 'list guix-package-list-type 'generation
                            (button-get btn 'number)))
     (guix-get-show-entries guix-profile 'list guix-package-list-type
                            'generation (button-get btn 'number)))
   "Show installed packages for this generation"
   'number number)
  (guix-info-insert-indent)
  (guix-info-insert-action-button
   "Delete"
   (lambda (btn)
     (guix-delete-generations (list (button-get btn 'number))
     (guix-delete-generations guix-profile (list (button-get btn 'number))
                              (current-buffer)))
   "Delete this generation"
   'number number))


@@ 653,7 654,7 @@ ENTRY is an alist with package info."
    (guix-info-insert-action-button
     "Switch"
     (lambda (btn)
       (guix-switch-to-generation (button-get btn 'number)
       (guix-switch-to-generation guix-profile (button-get btn 'number)
                                  (current-buffer)))
     "Switch to this generation (make it the current one)"
     'number (guix-get-key-val entry 'number))))

M emacs/guix-list.el => emacs/guix-list.el +8 -6
@@ 441,7 441,8 @@ This macro defines the following functions:
    (when (or (<= count guix-list-describe-warning-count)
              (y-or-n-p (format "Do you really want to describe %d entries? "
                                count)))
      (apply #'guix-get-show-entries 'info entry-type 'id ids))))
      (apply #'guix-get-show-entries
             guix-profile 'info entry-type 'id ids))))

(defun guix-list-describe (&optional arg)
  "Describe entries marked with a general mark.


@@ 617,7 618,8 @@ FUN should accept action-type as argument."
  (let ((actions (delq nil
                       (mapcar fun '(install delete upgrade)))))
    (if actions
        (guix-process-package-actions actions (current-buffer))
        (guix-process-package-actions
         guix-profile actions (current-buffer))
      (user-error "No operations specified"))))

(defun guix-package-list-execute ()


@@ 751,13 753,13 @@ VAL is a boolean value."
         (number  (guix-get-key-val entry 'number)))
    (if current
        (user-error "This generation is already the current one")
      (guix-switch-to-generation number (current-buffer)))))
      (guix-switch-to-generation guix-profile number (current-buffer)))))

(defun guix-generation-list-show-packages ()
  "List installed packages for the generation at point."
  (interactive)
  (guix-get-show-entries 'list guix-package-list-type 'generation
                         (guix-list-current-id)))
  (guix-get-show-entries guix-profile 'list guix-package-list-type
                         'generation (guix-list-current-id)))

(defun guix-generation-list-mark-delete (&optional arg)
  "Mark the current generation for deletion and move to the next line.


@@ 773,7 775,7 @@ With ARG, mark all generations for deletion."
  (let ((marked (guix-list-get-marked-id-list 'delete)))
    (or marked
        (user-error "No generations marked for deletion"))
    (guix-delete-generations marked (current-buffer))))
    (guix-delete-generations guix-profile marked (current-buffer))))

(provide 'guix-list)


M emacs/guix.el => emacs/guix.el +94 -47
@@ 50,99 50,146 @@ If nil, show a single package in the info buffer."
(defvar guix-search-history nil
  "A history of minibuffer prompts.")

(defun guix-get-show-packages (search-type &rest search-vals)
(defun guix-get-show-packages (profile search-type &rest search-vals)
  "Search for packages and show results.

If PROFILE is nil, use `guix-current-profile'.

See `guix-get-entries' for the meaning of SEARCH-TYPE and
SEARCH-VALS.

Results are displayed in the list buffer, unless a single package
is found and `guix-list-single-package' is nil."
  (let ((packages (guix-get-entries guix-package-list-type
  (or profile (setq profile guix-current-profile))
  (let ((packages (guix-get-entries profile guix-package-list-type
                                    search-type search-vals
                                    (guix-get-params-for-receiving
                                     'list guix-package-list-type))))
    (if (or guix-list-single-package
            (cdr packages))
        (guix-set-buffer packages 'list guix-package-list-type
        (guix-set-buffer profile packages 'list guix-package-list-type
                         search-type search-vals)
      (let ((packages (guix-get-entries guix-package-info-type
      (let ((packages (guix-get-entries profile guix-package-info-type
                                        search-type search-vals
                                        (guix-get-params-for-receiving
                                         'info guix-package-info-type))))
        (guix-set-buffer packages 'info guix-package-info-type
        (guix-set-buffer profile packages 'info guix-package-info-type
                         search-type search-vals)))))

(defun guix-get-show-generations (search-type &rest search-vals)
  "Search for generations and show results."
(defun guix-get-show-generations (profile search-type &rest search-vals)
  "Search for generations and show results.

If PROFILE is nil, use `guix-current-profile'.

See `guix-get-entries' for the meaning of SEARCH-TYPE and
SEARCH-VALS."
  (apply #'guix-get-show-entries
         (or profile guix-current-profile)
         'list 'generation search-type search-vals))

;;;###autoload
(defun guix-search-by-name (name)
(defun guix-search-by-name (name &optional profile)
  "Search for Guix packages by NAME.
NAME is a string with name specification.  It may optionally contain
a version number.  Examples: \"guile\", \"guile-2.0.11\"."
a version number.  Examples: \"guile\", \"guile-2.0.11\".

If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (read-string "Package name: " nil 'guix-search-history)))
  (guix-get-show-packages 'name name))
   (list (read-string "Package name: " nil 'guix-search-history)
         (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-packages profile 'name name))

;;;###autoload
(defun guix-search-by-regexp (regexp &rest params)
(defun guix-search-by-regexp (regexp &optional params profile)
  "Search for Guix packages by REGEXP.
PARAMS are package parameters that should be searched.
If PARAMS are not specified, use `guix-search-params'."
If PARAMS are not specified, use `guix-search-params'.

If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (read-regexp "Regexp: " nil 'guix-search-history)
         nil
         (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-packages profile 'regexp regexp
                          (or params guix-search-params)))

;;;###autoload
(defun guix-installed-packages (&optional profile)
  "Display information about installed Guix packages.
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (read-string "Regexp: " nil 'guix-search-history)))
  (or params (setq params guix-search-params))
  (guix-get-show-packages 'regexp regexp params))
   (list (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-packages profile 'installed))

;;;###autoload
(defun guix-installed-packages ()
  "Display information about installed Guix packages."
  (interactive)
  (guix-get-show-packages 'installed))
(defun guix-obsolete-packages (&optional profile)
  "Display information about obsolete Guix packages.
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-packages profile 'obsolete))

;;;###autoload
(defun guix-obsolete-packages ()
  "Display information about obsolete Guix packages."
  (interactive)
  (guix-get-show-packages 'obsolete))
(defun guix-all-available-packages (&optional profile)
  "Display information about all available Guix packages.
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-packages profile 'all-available))

;;;###autoload
(defun guix-all-available-packages ()
  "Display information about all available Guix packages."
  (interactive)
  (guix-get-show-packages 'all-available))
(defun guix-newest-available-packages (&optional profile)
  "Display information about the newest available Guix packages.
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-packages profile 'newest-available))

;;;###autoload
(defun guix-newest-available-packages ()
  "Display information about the newest available Guix packages."
  (interactive)
  (guix-get-show-packages 'newest-available))
(defun guix-generations (&optional profile)
  "Display information about all generations.
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-generations profile 'all))

;;;###autoload
(defun guix-generations (&optional number)
(defun guix-last-generations (number &optional profile)
  "Display information about last NUMBER generations.
If NUMBER is nil, display all generations.

Generations can be displayed in a list or info buffers depending
on `guix-show-generations-function'.

Interactively, NUMBER is defined by a numeric prefix."
  (interactive "P")
  (if (numberp number)
      (guix-get-show-generations 'last number)
    (guix-get-show-generations 'all)))
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (read-number "The number of last generations: ")
         (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-generations profile 'last number))

;;;###autoload
(defun guix-generations-by-time (from to)
(defun guix-generations-by-time (from to &optional profile)
  "Display information about generations created between FROM and TO.
FROM and TO should be time values."
FROM and TO should be time values.
If PROFILE is nil, use `guix-current-profile'.
Interactively with prefix, prompt for PROFILE."
  (interactive
   (list (guix-read-date "Find generations (from): ")
         (guix-read-date "Find generations (to): ")))
  (guix-get-show-generations 'time
         (guix-read-date "Find generations (to): ")
         (and current-prefix-arg
              (guix-profile-prompt))))
  (guix-get-show-generations profile 'time
                             (float-time from)
                             (float-time to)))