summary refs log tree commit diff
path: root/emacs
diff options
context:
space:
mode:
Diffstat (limited to 'emacs')
-rw-r--r--emacs/guix-backend.el2
-rw-r--r--emacs/guix-base.el18
-rw-r--r--emacs/guix-build-log.el58
-rw-r--r--emacs/guix-command.el159
-rw-r--r--emacs/guix-devel.el2
-rw-r--r--emacs/guix-emacs.el52
-rw-r--r--emacs/guix-info.el11
-rw-r--r--emacs/guix-init.el3
-rw-r--r--emacs/guix-list.el8
-rw-r--r--emacs/guix-main.scm7
-rw-r--r--emacs/guix-pcomplete.el21
-rw-r--r--emacs/guix-read.el6
-rw-r--r--emacs/guix-utils.el11
13 files changed, 291 insertions, 67 deletions
diff --git a/emacs/guix-backend.el b/emacs/guix-backend.el
index e7c158bef4..82383e48ff 100644
--- a/emacs/guix-backend.el
+++ b/emacs/guix-backend.el
@@ -202,7 +202,7 @@ this address (it should be defined by
   ;; A mix of the code from `geiser-repl--start-repl' and
   ;; `geiser-repl--to-repl-buffer'.
   (let ((impl 'guile)
-        (geiser-guile-load-path (cons guix-load-path
+        (geiser-guile-load-path (cons (expand-file-name guix-load-path)
                                       geiser-guile-load-path))
         (geiser-repl-startup-time guix-repl-startup-time))
     (with-current-buffer buffer
diff --git a/emacs/guix-base.el b/emacs/guix-base.el
index e64e375e33..d9c70aae9e 100644
--- a/emacs/guix-base.el
+++ b/emacs/guix-base.el
@@ -186,6 +186,10 @@ For the meaning of location, see `guix-find-location'."
   "Return a list of names of available graph node types."
   (guix-eval-read (guix-make-guile-expression 'graph-type-names)))
 
+(guix-memoized-defun guix-refresh-updater-names ()
+  "Return a list of names of available refresh updater types."
+  (guix-eval-read (guix-make-guile-expression 'refresh-updater-names)))
+
 (guix-memoized-defun guix-lint-checker-names ()
   "Return a list of names of available lint checkers."
   (guix-eval-read (guix-make-guile-expression 'lint-checker-names)))
@@ -1035,7 +1039,7 @@ Each element from GENERATIONS is a generation number."
                               profile generation)))
     (guix-eval-in-repl
      (guix-make-guile-expression
-      'switch-to-generation profile generation)
+      'switch-to-generation* profile generation)
      operation-buffer)))
 
 (defun guix-package-source-path (package-id)
@@ -1083,9 +1087,10 @@ FILE.  With a prefix argument, also prompt for PROFILE."
                               file profile)))
     (guix-eval-in-repl
      (guix-make-guile-expression
-      'guix-package
-      (concat "--profile=" profile)
-      (concat "--manifest=" file))
+      'guix-command
+      "package"
+      (concat "--profile="  (expand-file-name profile))
+      (concat "--manifest=" (expand-file-name file)))
      operation-buffer)))
 
 
@@ -1181,10 +1186,11 @@ The function is called with a single argument - a command line string."
 (defun guix-pull (&optional verbose)
   "Run Guix pull operation.
 If VERBOSE is non-nil (with prefix argument), produce verbose output."
-  (interactive)
+  (interactive "P")
   (let ((args (and verbose '("--verbose"))))
     (guix-eval-in-repl
-     (apply #'guix-make-guile-expression 'guix-pull args)
+     (apply #'guix-make-guile-expression
+            'guix-command "pull" args)
      nil 'pull)))
 
 (provide 'guix-base)
diff --git a/emacs/guix-build-log.el b/emacs/guix-build-log.el
index c0855b284c..e08a88f6cc 100644
--- a/emacs/guix-build-log.el
+++ b/emacs/guix-build-log.el
@@ -24,6 +24,8 @@
 
 ;;; Code:
 
+(require 'guix-utils)
+
 (defgroup guix-build-log nil
   "Settings for `guix-build-log-mode'."
   :group 'guix)
@@ -102,10 +104,13 @@
   "Face for the number of seconds for a phase."
   :group 'guix-build-log-faces)
 
-(defcustom guix-build-log-mode-hook
-  ;; Not using `compilation-minor-mode' because it rebinds some standard
-  ;; keys, including M-n/M-p.
-  '(compilation-shell-minor-mode view-mode)
+(defcustom guix-build-log-minor-mode-activate t
+  "If non-nil, then `guix-build-log-minor-mode' is automatically
+activated in `shell-mode' buffers."
+  :type 'boolean
+  :group 'guix-build-log)
+
+(defcustom guix-build-log-mode-hook '()
   "Hook run after `guix-build-log-mode' is entered."
   :type 'hook
   :group 'guix-build-log)
@@ -178,9 +183,8 @@ STATE is a symbol denoting how a build phase was ended.  It should be
      (3 'guix-build-log-phase-seconds prepend)))
   "A list of `font-lock-keywords' for `guix-build-log-mode'.")
 
-(defvar guix-build-log-mode-map
+(defvar guix-build-log-common-map
   (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map special-mode-map)
     (define-key map (kbd "M-n") 'guix-build-log-next-phase)
     (define-key map (kbd "M-p") 'guix-build-log-previous-phase)
     (define-key map (kbd "TAB") 'guix-build-log-phase-toggle)
@@ -188,8 +192,26 @@ STATE is a symbol denoting how a build phase was ended.  It should be
     (define-key map (kbd "<backtab>") 'guix-build-log-phase-toggle-all)
     (define-key map [(shift tab)] 'guix-build-log-phase-toggle-all)
     map)
+  "Parent keymap for 'build-log' buffers.
+For `guix-build-log-mode' this map is used as is.
+For `guix-build-log-minor-mode' this map is prefixed with 'C-c'.")
+
+(defvar guix-build-log-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent
+     map (make-composed-keymap (list guix-build-log-common-map)
+                               special-mode-map))
+    (define-key map (kbd "c") 'compilation-shell-minor-mode)
+    (define-key map (kbd "v") 'view-mode)
+    map)
   "Keymap for `guix-build-log-mode' buffers.")
 
+(defvar guix-build-log-minor-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "C-c") guix-build-log-common-map)
+    map)
+  "Keymap for `guix-build-log-minor-mode' buffers.")
+
 (defun guix-build-log-phase-start (&optional with-header?)
   "Return the start point of the current build phase.
 If WITH-HEADER? is non-nil, do not skip 'starting phase ...' header.
@@ -319,9 +341,12 @@ When Guix Build Log minor mode is enabled, it highlights build
 log in the current buffer.  This mode can be enabled
 programmatically using hooks:
 
-  (add-hook 'shell-mode-hook 'guix-build-log-minor-mode)"
+  (add-hook 'shell-mode-hook 'guix-build-log-minor-mode)
+
+\\{guix-build-log-minor-mode-map}"
   :init-value nil
   :lighter " Guix-Build-Log"
+  :keymap guix-build-log-minor-mode-map
   :group 'guix-build-log
   (if guix-build-log-minor-mode
       (font-lock-add-keywords nil guix-build-log-font-lock-keywords)
@@ -329,6 +354,25 @@ programmatically using hooks:
   (when font-lock-mode
     (font-lock-fontify-buffer)))
 
+;;;###autoload
+(defun guix-build-log-minor-mode-activate-maybe ()
+  "Activate `guix-build-log-minor-mode' depending on
+`guix-build-log-minor-mode-activate' variable."
+  (when guix-build-log-minor-mode-activate
+    (guix-build-log-minor-mode)))
+
+(defun guix-build-log-find-file (file-or-url)
+  "Open FILE-OR-URL in `guix-build-log-mode'."
+  (guix-find-file-or-url file-or-url)
+  (guix-build-log-mode))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist
+             ;; Regexp for log files (usually placed in /var/log/guix/...)
+             (cons (rx "/guix/drvs/" (= 2 alnum) "/" (= 30 alnum)
+                       "-" (+ (any alnum "-+.")) ".drv" string-end)
+                   'guix-build-log-mode))
+
 (provide 'guix-build-log)
 
 ;;; guix-build-log.el ends here
diff --git a/emacs/guix-command.el b/emacs/guix-command.el
index 1a42594b68..ccd85d25b9 100644
--- a/emacs/guix-command.el
+++ b/emacs/guix-command.el
@@ -65,6 +65,7 @@
 (require 'guix-help-vars)
 (require 'guix-read)
 (require 'guix-base)
+(require 'guix-build-log)
 (require 'guix-guile)
 (require 'guix-external)
 
@@ -131,7 +132,8 @@ to be modified."
 
 (guix-command-define-argument-improver
     guix-command-improve-action-argument
-  '(("graph"       :char ?G)
+  '(("container"   :char ?C)
+    ("graph"       :char ?G)
     ("environment" :char ?E)
     ("publish"     :char ?u)
     ("pull"        :char ?P)
@@ -173,7 +175,8 @@ to be modified."
 (defvar guix-command-improve-common-build-argument
   '(("--no-substitutes"  :char ?s)
     ("--no-build-hook"   :char ?h)
-    ("--max-silent-time" :char ?x)))
+    ("--max-silent-time" :char ?x)
+    ("--rounds"          :char ?R :fun read-number)))
 
 (defun guix-command-improve-common-build-argument (argument)
   (guix-command-modify-argument-from-alist
@@ -195,7 +198,11 @@ to be modified."
 
 (guix-command-define-argument-improver
     guix-command-improve-environment-argument
-  '(("--exec" :fun read-shell-command)
+  '(("--ad-hoc"
+     :name "--ad-hoc " :fun guix-read-package-names-string
+     :switch? nil :option? t)
+    ("--expose" :char ?E)
+    ("--share" :char ?S)
     ("--load" :fun guix-read-file-name)))
 
 (guix-command-define-argument-improver
@@ -234,6 +241,7 @@ to be modified."
      :switch? nil :option? t)
     ("--install-from-file" :fun guix-read-file-name)
     ("--manifest"       :fun guix-read-file-name)
+    ("--profile"        :fun guix-read-file-name)
     ("--do-not-upgrade" :char ?U)
     ("--roll-back"      :char ?R)
     ("--show"           :char ?w :fun guix-read-package-name)))
@@ -241,6 +249,7 @@ to be modified."
 (guix-command-define-argument-improver
     guix-command-improve-refresh-argument
   '(("--select"     :fun guix-read-refresh-subset)
+    ("--type"       :fun guix-read-refresh-updater-names-string)
     ("--key-server" :char ?S)))
 
 (guix-command-define-argument-improver
@@ -364,11 +373,16 @@ to be modified."
                      :name "-- " :char ?= :option? t args)))
     (let ((command (car commands)))
       (cond
-       ((member command '("archive" "build" "graph" "edit"
-                          "environment" "lint" "refresh"))
+       ((member command
+                '("archive" "build" "challenge" "edit"
+                  "graph" "lint" "refresh"))
         (argument :doc "Packages" :fun 'guix-read-package-names-string))
+       ((equal commands '("container" "exec"))
+        (argument :doc "PID Command [Args...]"))
        ((string= command "download")
         (argument :doc "URL"))
+       ((string= command "environment")
+        (argument :doc "Command [Args...]" :fun 'read-shell-command))
        ((string= command "gc")
         (argument :doc "Paths" :fun 'guix-read-file-name))
        ((member command '("hash" "system"))
@@ -382,10 +396,22 @@ to be modified."
              (string= command "import"))
         (argument :doc "Package name"))))))
 
+(defvar guix-command-additional-arguments
+  `((("environment")
+     ,(guix-command-make-argument
+       :name "++packages " :char ?p :option? t
+       :doc "build inputs of the specified packages"
+       :fun 'guix-read-package-names-string)))
+  "Alist of guix commands and additional arguments for them.
+These are 'fake' arguments that are not presented in 'guix' shell
+commands.")
+
 (defun guix-command-additional-arguments (&optional commands)
   "Return additional arguments for COMMANDS."
   (let ((rest-arg (guix-command-rest-argument commands)))
-    (and rest-arg (list rest-arg))))
+    (append (guix-assoc-value guix-command-additional-arguments
+                              commands)
+            (and rest-arg (list rest-arg)))))
 
 ;; Ideally only `guix-command-arguments' function should exist with the
 ;; contents of `guix-command-all-arguments', but we need to make a
@@ -463,28 +489,113 @@ to be modified."
   "Return actions from ARGUMENTS."
   (cl-remove-if-not #'guix-command-argument-action? arguments))
 
-(defun guix-command-post-process-args (args)
-  "Adjust appropriately command line ARGS returned from popup command."
-  ;; XXX We need to split "--install foo bar" and similar strings into
-  ;; lists of strings.  But some commands (e.g., 'guix hash') accept a
-  ;; file name as the 'rest' argument, and as file names may contain
-  ;; spaces, splitting by spaces will break such names.  For example, the
-  ;; following argument: "-- /tmp/file with spaces" will be transformed
-  ;; into the following list: ("--" "/tmp/file" "with" "spaces") instead
-  ;; of the wished ("--" "/tmp/file with spaces").
-  (let* (rest
-         (rx (rx string-start
-                 (or "-- " "--install " "--remove ")))
+
+;;; Post processing popup arguments
+
+(defvar guix-command-post-processors
+  '(("environment"
+     guix-command-post-process-environment-packages
+     guix-command-post-process-environment-ad-hoc
+     guix-command-post-process-rest-multiple-leave)
+    ("hash"
+     guix-command-post-process-rest-single)
+    ("package"
+     guix-command-post-process-package-args)
+    ("system"
+     guix-command-post-process-rest-single))
+  "Alist of guix commands and functions for post-processing
+a list of arguments returned from popup interface.
+Each function is called on the returned arguments in turn.")
+
+(defvar guix-command-rest-arg-regexp
+  (rx string-start "-- " (group (+ any)))
+  "Regexp to match a string with the 'rest' arguments.")
+
+(defun guix-command-replace-args (args predicate modifier)
+  "Replace arguments matching PREDICATE from ARGS.
+Call MODIFIER on each argument matching PREDICATE and append the
+returned list of strings to the end of ARGS.  Remove the original
+arguments."
+  (let* ((rest nil)
          (args (mapcar (lambda (arg)
-                         (if (string-match-p rx arg)
-                             (progn (push (split-string arg) rest)
-                                    nil)
+                         (if (funcall predicate arg)
+                             (progn
+                               (push (funcall modifier arg) rest)
+                               nil)
                            arg))
                        args)))
     (if rest
         (apply #'append (delq nil args) rest)
       args)))
 
+(cl-defun guix-command-post-process-matching-args (args regexp
+                                                   &key group split?)
+  "Modify arguments from ARGS matching REGEXP by moving them to
+the end of ARGS list.  If SPLIT? is non-nil, split matching
+arguments into multiple subarguments."
+  (guix-command-replace-args
+   args
+   (lambda (arg)
+     (string-match regexp arg))
+   (lambda (arg)
+     (let ((val (match-string (or group 0) arg))
+           (fun (if split? #'split-string #'list)))
+       (funcall fun val)))))
+
+(defun guix-command-post-process-rest-single (args)
+  "Modify ARGS by moving '-- ARG' argument to the end of ARGS list."
+  (guix-command-post-process-matching-args
+   args guix-command-rest-arg-regexp
+   :group 1))
+
+(defun guix-command-post-process-rest-multiple (args)
+  "Modify ARGS by splitting '-- ARG ...' into multiple subarguments
+and moving them to the end of ARGS list.
+Remove '-- ' string."
+  (guix-command-post-process-matching-args
+   args guix-command-rest-arg-regexp
+   :group 1
+   :split? t))
+
+(defun guix-command-post-process-rest-multiple-leave (args)
+  "Modify ARGS by splitting '-- ARG ...' into multiple subarguments
+and moving them to the end of ARGS list.
+Leave '--' string as a separate argument."
+  (guix-command-post-process-matching-args
+   args guix-command-rest-arg-regexp
+   :split? t))
+
+(defun guix-command-post-process-package-args (args)
+  "Adjust popup ARGS for 'guix package' command."
+  (guix-command-post-process-matching-args
+   args (rx string-start (or "--install " "--remove ") (+ any))
+   :split? t))
+
+(defun guix-command-post-process-environment-packages (args)
+  "Adjust popup ARGS for specified packages of 'guix environment'
+command."
+  (guix-command-post-process-matching-args
+   args (rx string-start "++packages " (group (+ any)))
+   :group 1
+   :split? t))
+
+(defun guix-command-post-process-environment-ad-hoc (args)
+  "Adjust popup ARGS for '--ad-hoc' argument of 'guix environment'
+command."
+  (guix-command-post-process-matching-args
+   args (rx string-start "--ad-hoc " (+ any))
+   :split? t))
+
+(defun guix-command-post-process-args (commands args)
+  "Adjust popup ARGS for guix COMMANDS."
+  (let* ((command (car commands))
+         (processors
+          (append (guix-assoc-value guix-command-post-processors commands)
+                  (guix-assoc-value guix-command-post-processors command))))
+    (guix-modify args
+                 (or processors
+                     (list #'guix-command-post-process-rest-multiple)))))
+
 
 ;;; 'Execute' actions
 
@@ -583,8 +694,7 @@ open the log file(s)."
          (output (guix-command-output args))
          (files  (split-string output "\n" t)))
     (dolist (file files)
-      (guix-find-file-or-url file)
-      (guix-build-log-mode))))
+      (guix-build-log-find-file file))))
 
 (defun guix-run-view-graph (args)
   "Run 'guix ARGS ...' graph command, make the image and open it."
@@ -640,7 +750,8 @@ EXECUTOR function is called with the current command line arguments."
        ,doc
        (interactive (,arguments-fun))
        (,executor (append ',commands
-                          (guix-command-post-process-args args))))))
+                          (guix-command-post-process-args
+                           ',commands args))))))
 
 (defun guix-command-generate-popup-actions (actions &optional commands)
   "Generate 'popup' commands from ACTIONS arguments for guix COMMANDS."
diff --git a/emacs/guix-devel.el b/emacs/guix-devel.el
index 170ce1ad54..8eb030942c 100644
--- a/emacs/guix-devel.el
+++ b/emacs/guix-devel.el
@@ -198,6 +198,7 @@ to find 'modify-phases' keywords."
     "mbegin"
     "mlet"
     "mlet*"
+    "modify-services"
     "munless"
     "mwhen"
     "run-with-state"
@@ -288,6 +289,7 @@ Each rule should have a form (SYMBOL VALUE).  See `put' for details."
   (mlet 2)
   (mlet* 2)
   (modify-phases 1)
+  (modify-services 1)
   (munless 1)
   (mwhen 1)
   (operating-system 0)
diff --git a/emacs/guix-emacs.el b/emacs/guix-emacs.el
index 0e3e8c211c..2f809ed16e 100644
--- a/emacs/guix-emacs.el
+++ b/emacs/guix-emacs.el
@@ -37,6 +37,11 @@ they are successfully installed."
 (defvar guix-emacs-autoloads nil
   "List of the last loaded Emacs autoloads.")
 
+(defvar guix-emacs-autoloads-regexp
+  (rx (group (* any) "-autoloads")
+      ".el" (zero-or-one "c") string-end)
+  "Regexp to match Emacs 'autoloads' file.")
+
 (defun guix-emacs-directory (&optional profile)
   "Return directory with Emacs packages installed in PROFILE.
 If PROFILE is nil, use `guix-user-profile'."
@@ -44,8 +49,15 @@ If PROFILE is nil, use `guix-user-profile'."
                     (or profile guix-user-profile)))
 
 (defun guix-emacs-find-autoloads-in-directory (directory)
-  "Return list of Emacs 'autoloads' files in DIRECTORY."
-  (directory-files directory 'full-name "-autoloads\\.el\\'" 'no-sort))
+  "Return a list of Emacs 'autoloads' files in DIRECTORY.
+The files in the list do not have extensions (.el, .elc)."
+  (cl-remove-duplicates
+   (delq nil
+        (mapcar (lambda (file)
+                  (when (string-match guix-emacs-autoloads-regexp file)
+                    (match-string 1 file)))
+                (directory-files directory 'full-name nil 'no-sort)))
+   :test #'string=))
 
 (defun guix-emacs-subdirs (directory)
   "Return list of DIRECTORY subdirectories."
@@ -74,29 +86,33 @@ Return nil if there are no emacs packages installed in PROFILE."
       nil)))
 
 ;;;###autoload
-(defun guix-emacs-load-autoloads (&optional all)
-  "Load autoloads for Emacs packages installed in a user profile.
-Add autoloads directories to `load-path'.
-If ALL is nil, activate only those packages that were installed
-after the last activation, otherwise activate all Emacs packages
-installed in `guix-user-profile'."
-  (interactive "P")
-  (let* ((autoloads (guix-emacs-find-autoloads))
-         (files (if all
-                    autoloads
-                  (cl-nset-difference autoloads guix-emacs-autoloads
-                                      :test #'string=))))
-    (dolist (file files)
-      (cl-pushnew (file-name-directory file) load-path
+(defun guix-emacs-load-autoloads (&optional profile)
+  "Load autoloads for Emacs packages installed in PROFILE.
+If PROFILE is nil, use `guix-user-profile'.
+Add autoloads directories to `load-path'."
+  (interactive (list (guix-profile-prompt)))
+  (let* ((autoloads     (guix-emacs-find-autoloads profile))
+         (new-autoloads (cl-nset-difference autoloads
+                                            guix-emacs-autoloads
+                                            :test #'string=)))
+    (dolist (file new-autoloads)
+      (cl-pushnew (directory-file-name (file-name-directory file))
+                  load-path
                   :test #'string=)
       (load file 'noerror))
-    (setq guix-emacs-autoloads autoloads)))
+    (setq guix-emacs-autoloads
+          (append new-autoloads guix-emacs-autoloads))))
 
 (defun guix-emacs-load-autoloads-maybe ()
   "Load autoloads for Emacs packages if needed.
 See `guix-emacs-activate-after-operation' for details."
   (and guix-emacs-activate-after-operation
-       (guix-emacs-load-autoloads)))
+       ;; FIXME Since a user can work with a non-current profile (using
+       ;; C-u before `guix-search-by-name' and other commands), emacs
+       ;; packages can be installed to another profile, and the
+       ;; following code will not work (i.e., the autoloads for this
+       ;; profile will not be loaded).
+       (guix-emacs-load-autoloads guix-current-profile)))
 
 (provide 'guix-emacs)
 
diff --git a/emacs/guix-info.el b/emacs/guix-info.el
index 260c7680f5..1c7e79b954 100644
--- a/emacs/guix-info.el
+++ b/emacs/guix-info.el
@@ -38,6 +38,12 @@
   :group 'guix-info
   :group 'guix-faces)
 
+(defface guix-info-heading
+  '((((type tty pc) (class color)) :weight bold)
+    (t :height 1.6 :weight bold :inherit variable-pitch))
+  "Face for headings."
+  :group 'guix-info-faces)
+
 (defface guix-info-param-title
   '((t :inherit font-lock-type-face))
   "Face used for titles of parameters."
@@ -374,7 +380,7 @@ If POS is nil, use the current point position."
   (interactive)
   (let ((button (button-at (or pos (point)))))
     (when button
-      (kill-new (button-label button)))))
+      (guix-copy-as-kill (button-label button)))))
 
 (defun guix-info-insert-action-button (label action &optional message
                                              &rest properties)
@@ -416,8 +422,7 @@ See `insert-text-button' for the meaning of PROPERTIES."
   :required (id installed non-unique))
 
 (defface guix-package-info-heading
-  '((((type tty pc) (class color)) :weight bold)
-    (t :height 1.6 :weight bold :inherit variable-pitch))
+  '((t :inherit guix-info-heading))
   "Face for package name and version headings."
   :group 'guix-package-info-faces)
 
diff --git a/emacs/guix-init.el b/emacs/guix-init.el
index 4b3d9c281c..1da607034f 100644
--- a/emacs/guix-init.el
+++ b/emacs/guix-init.el
@@ -12,8 +12,9 @@ avoid loading autoloads of Emacs packages installed in
 (add-to-list 'load-path (guix-emacs-directory))
 
 (when guix-package-enable-at-startup
-  (guix-emacs-load-autoloads 'all))
+  (guix-emacs-load-autoloads))
 
 (add-hook 'scheme-mode-hook 'guix-devel-activate-mode-maybe)
+(add-hook 'shell-mode-hook 'guix-build-log-minor-mode-activate-maybe)
 
 (provide 'guix-init)
diff --git a/emacs/guix-list.el b/emacs/guix-list.el
index 87d214bb4d..560ae6a86f 100644
--- a/emacs/guix-list.el
+++ b/emacs/guix-list.el
@@ -45,6 +45,11 @@
   "Face used for file paths."
   :group 'guix-list-faces)
 
+(defface guix-list-time
+  '((t :inherit guix-info-time))
+  "Face used for time stamps."
+  :group 'guix-list-faces)
+
 (defcustom guix-list-describe-warning-count 10
   "The maximum number of entries for describing without a warning.
 If a user wants to describe more than this number of marked
@@ -201,7 +206,8 @@ VAL may be nil."
 
 (defun guix-list-get-time (seconds &optional _)
   "Return formatted time string from SECONDS."
-  (guix-get-time-string seconds))
+  (guix-get-string (guix-get-time-string seconds)
+                   'guix-list-time))
 
 (defun guix-list-get-file-path (path &optional _)
   "Return PATH button specification for `tabulated-list-entries'."
diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
index e29a0a0acc..7175b103da 100644
--- a/emacs/guix-main.scm
+++ b/emacs/guix-main.scm
@@ -905,7 +905,7 @@ OUTPUTS is a list of package outputs (may be an empty list)."
                                     "~a packages in profile~%"
                                     count)
                              count)
-                     (display-search-paths entries profile))))))))))
+                     (display-search-paths entries (list profile)))))))))))
 
 (define (delete-generations* profile generations)
   "Delete GENERATIONS from PROFILE.
@@ -991,6 +991,11 @@ Return #t if the shell command was executed successfully."
   "Return a list of names of available graph node types."
   (map node-type-name %node-types))
 
+(define (refresh-updater-names)
+  "Return a list of names of available refresh updater types."
+  (map (@ (guix upstream) upstream-updater-name)
+       (@ (guix scripts refresh) %updaters)))
+
 (define (lint-checker-names)
   "Return a list of names of available lint checkers."
   (map (lambda (checker)
diff --git a/emacs/guix-pcomplete.el b/emacs/guix-pcomplete.el
index 4743be59bd..85b267a78d 100644
--- a/emacs/guix-pcomplete.el
+++ b/emacs/guix-pcomplete.el
@@ -128,6 +128,13 @@ subcommands, actions, etc. for this guix COMMAND."
    guix-help-parse-regexp-group
    "graph" "--list-types"))
 
+(guix-memoized-defun guix-pcomplete-refresh-updaters ()
+  "Return a list of all available refresh updater types."
+  (guix-pcomplete-run-guix-and-search
+   guix-help-parse-list-regexp
+   guix-help-parse-regexp-group
+   "refresh" "--list-updaters"))
+
 
 ;;; Completing
 
@@ -209,8 +216,8 @@ group - the argument.")
   "Complete argument for guix COMMAND."
   (cond
    ((member command
-            '("archive" "build" "graph" "edit" "environment"
-              "lint" "refresh" "size"))
+            '("archive" "build" "challenge" "edit" "environment"
+              "graph" "lint" "refresh" "size"))
     (while t
       (pcomplete-here (guix-pcomplete-all-packages))))
    (t (pcomplete-here* (pcomplete-entries)))))
@@ -287,9 +294,13 @@ INPUT is the current partially completed string."
            (option? "-u" "--user"))
       (complete* (pcmpl-unix-user-names)))
 
-     ((and (command? "refresh")
-           (option? "-s" "--select"))
-      (complete* guix-help-refresh-subsets))
+     ((command? "refresh")
+      (cond
+       ((option? "-s" "--select")
+        (complete* guix-help-refresh-subsets))
+       ((option? "-t" "--type")
+        (guix-pcomplete-complete-comma-args
+         (guix-pcomplete-refresh-updaters)))))
 
      ((and (command? "size")
            (option? "-m" "--map-file"))
diff --git a/emacs/guix-read.el b/emacs/guix-read.el
index 5a7201c3aa..e60af9c2f7 100644
--- a/emacs/guix-read.el
+++ b/emacs/guix-read.el
@@ -137,6 +137,12 @@ keywords are available:
  :single-prompt "Refresh subset: ")
 
 (guix-define-readers
+ :completions-getter guix-refresh-updater-names
+ :multiple-reader guix-read-refresh-updater-names
+ :multiple-prompt "Refresh updater,s: "
+ :multiple-separator ",")
+
+(guix-define-readers
  :completions-var guix-help-key-policies
  :single-reader guix-read-key-policy
  :single-prompt "Key policy: ")
diff --git a/emacs/guix-utils.el b/emacs/guix-utils.el
index d1f088b6a8..5f3f3ecc10 100644
--- a/emacs/guix-utils.el
+++ b/emacs/guix-utils.el
@@ -226,6 +226,17 @@ single argument."
      (while (re-search-forward ,regexp nil t)
        ,@body)))
 
+(defun guix-modify (object modifiers)
+  "Apply MODIFIERS to OBJECT.
+OBJECT is passed as an argument to the first function from
+MODIFIERS list, the returned result is passed to the second
+function from the list and so on.  Return result of the last
+modifier call."
+  (if (null modifiers)
+      object
+    (guix-modify (funcall (car modifiers) object)
+                 (cdr modifiers))))
+
 
 ;;; Alist accessors