+;;; Buffers and auto updating.
+
+(defcustom guix-update-after-operation 'current
+ "Define what information to update after executing an operation.
+
+After successful executing an operation in the Guix REPL (for
+example after installing a package), information in Guix buffers
+will or will not be automatically updated depending on a value of
+this variable.
+
+If nil, update nothing (do not revert any buffer).
+If `current', update the buffer from which an operation was performed.
+If `all', update all Guix buffers (not recommended)."
+ :type '(choice (const :tag "Do nothing" nil)
+ (const :tag "Update operation buffer" current)
+ (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
+ '((display-buffer-reuse-window
+ display-buffer-same-window))))
+
+(defun guix-buffer-p (&optional buffer modes)
+ "Return non-nil if BUFFER mode is derived from any of the MODES.
+If BUFFER is nil, check current buffer.
+If MODES is nil, use `guix-list-mode' and `guix-info-mode'."
+ (with-current-buffer (or buffer (current-buffer))
+ (apply #'derived-mode-p
+ (or modes
+ '(guix-list-mode guix-info-mode)))))
+
+(defun guix-buffers (&optional modes)
+ "Return list of all buffers with major modes derived from MODES.
+If MODES is nil, return list of all Guix 'list' and 'info' buffers."
+ (cl-remove-if-not (lambda (buf)
+ (guix-buffer-p buf modes))
+ (buffer-list)))
+
+(defun guix-update-buffer (buffer)
+ "Update information in a 'list' or 'info' BUFFER."
+ (with-current-buffer buffer
+ (guix-revert-buffer nil t)))
+
+(defun guix-update-buffers-maybe-after-operation ()
+ "Update buffers after Guix operation if needed.
+See `guix-update-after-operation' for details."
+ (let ((to-update
+ (and guix-operation-buffer
+ (cl-case guix-update-after-operation
+ (current (and (buffer-live-p guix-operation-buffer)
+ (guix-buffer-p guix-operation-buffer)
+ (list guix-operation-buffer)))
+ (all (guix-buffers))))))
+ (setq guix-operation-buffer nil)
+ (mapc #'guix-update-buffer to-update)))
+
+(add-hook 'guix-after-repl-operation-hook
+ 'guix-update-buffers-maybe-after-operation)
+
+\f