aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Kost <alezost@gmail.com>2015-12-02 15:24:07 +0300
committerAlex Kost <alezost@gmail.com>2016-01-02 17:25:35 +0300
commit6c40b7b703424f757ff2e1fbb7503a525f9acfd8 (patch)
treef2eaad8eedcc9f2e2a413ec9eb899cb3832d640a
parent8103c22fea9eef5e6ed1b97fedb565e0296bb6e3 (diff)
downloadgnu-guix-6c40b7b703424f757ff2e1fbb7503a525f9acfd8.tar
gnu-guix-6c40b7b703424f757ff2e1fbb7503a525f9acfd8.tar.gz
emacs: Generalize buffer interface.
Extract the code for defining buffer interface from "guix-base.el", generalize it and move to "guix-buffer.el". * emacs.am (ELFILES): Add "emacs/guix-buffer.el". * emacs/guix-base.el (guix-profile, guix-entries, guix-buffer-type) (guix-entry-type, guix-search-type, guix-search-vals, guix-set-vars) (guix-get-symbol, guix-show-entries, guix-get-show-entries) (guix-set-buffer, guix-history-call, guix-make-history-item) (guix-get-params-for-receiving): Remove. (guix-switch-to-buffer): Rename to 'guix-buffer-display' and move to "guix-buffer.el". (guix-get-entries): Rename to 'guix-ui-get-entries' and move to "guix-ui.el". (guix-buffer-data, guix-buffer-value, guix-buffer-param-title) (guix-buffer-name, guix-buffer-history-size) (guix-buffer-revert-confirm?, guix-buffer-map, guix-buffer-revert) (guix-buffer-after-redisplay-hook, guix-buffer-redisplay) (guix-buffer-redisplay-goto-button): Move to... * emacs/guix-buffer.el: ... here. New file. (guix-buffer-item): New variable. (guix-buffer-with-item, guix-buffer-with-current-item) (guix-buffer-define-current-item-accessor) (guix-buffer-define-current-item-accessors) (guix-buffer-define-current-args-accessor) (guix-buffer-define-current-args-accessors): New macros. (guix-buffer-get-entries, guix-buffer-mode-enable) (guix-buffer-mode-initialize, guix-buffer-insert-entries) (guix-buffer-show-entries-default, guix-buffer-show-entries) (guix-buffer-message, guix-buffer-history-item, guix-buffer-set) (guix-buffer-display-entries-current) (guix-buffer-get-display-entries-current) (guix-buffer-display-entries, guix-buffer-get-display-entries): New procedures. * emacs/guix-info.el: Adjust for the procedures renaming. (guix-info-define-interface): Add ':show-entries-function' keyword. * emacs/guix-list.el: Likewise. * emacs/guix-ui.el (guix-ui-define-interface): Generate 'guix-ENTRY-TYPE-BUFFER-TYPE-get-entries' procedure based on 'guix-ui-get-entries'. * emacs/guix.el (guix-get-show-packages, guix-get-show-generations): Adjust for the procedures renaming.
-rw-r--r--emacs.am1
-rw-r--r--emacs/guix-base.el396
-rw-r--r--emacs/guix-buffer.el566
-rw-r--r--emacs/guix-info.el58
-rw-r--r--emacs/guix-list.el103
-rw-r--r--emacs/guix-ui.el80
-rw-r--r--emacs/guix.el45
7 files changed, 780 insertions, 469 deletions
diff --git a/emacs.am b/emacs.am
index ad7de78657..7848b1c415 100644
--- a/emacs.am
+++ b/emacs.am
@@ -22,6 +22,7 @@ ELFILES = \
emacs/guix-backend.el \
emacs/guix-base.el \
emacs/guix-build-log.el \
+ emacs/guix-buffer.el \
emacs/guix-command.el \
emacs/guix-devel.el \
emacs/guix-emacs.el \
diff --git a/emacs/guix-base.el b/emacs/guix-base.el
index 21be02d26d..4bd88992c4 100644
--- a/emacs/guix-base.el
+++ b/emacs/guix-base.el
@@ -22,9 +22,6 @@
;; This file provides some base and common definitions for guix.el
;; package.
-;; List and info buffers have many common patterns that are defined
-;; using `guix-buffer-define-interface' macro from this file.
-
;;; Code:
(require 'cl-lib)
@@ -34,8 +31,6 @@
(require 'guix-guile)
(require 'guix-utils)
(require 'guix-ui)
-(require 'guix-history)
-(require 'guix-messages)
;;; Parameters of the entries
@@ -143,227 +138,6 @@ For the meaning of location, see `guix-find-location'."
#'string<))
-;;; Buffers
-
-(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))))
-
-
-;;; Common definitions for buffer types
-
-(defvar guix-buffer-data nil
- "Alist with 'buffer' data.
-This alist is filled by `guix-buffer-define-interface' macro.")
-
-(defun guix-buffer-value (buffer-type entry-type symbol)
- "Return SYMBOL's value for BUFFER-TYPE/ENTRY-TYPE from `guix-buffer-data'."
- (symbol-value
- (guix-assq-value guix-buffer-data buffer-type entry-type symbol)))
-
-(defun guix-buffer-param-title (buffer-type entry-type param)
- "Return PARAM title for BUFFER-TYPE/ENTRY-TYPE."
- (or (guix-assq-value (guix-buffer-value buffer-type entry-type 'titles)
- param)
- ;; Fallback to a title defined in 'info' interface.
- (unless (eq buffer-type 'info)
- (guix-assq-value (guix-buffer-value 'info entry-type 'titles)
- param))
- (guix-symbol-title param)))
-
-(defun guix-buffer-name (buffer-type entry-type profile)
- "Return name of BUFFER-TYPE buffer for displaying ENTRY-TYPE entries."
- (let ((str-or-fun (guix-buffer-value buffer-type entry-type
- 'buffer-name)))
- (if (stringp str-or-fun)
- str-or-fun
- (funcall str-or-fun profile))))
-
-(defun guix-buffer-history-size (buffer-type entry-type)
- "Return history size for BUFFER-TYPE/ENTRY-TYPE."
- (guix-buffer-value buffer-type entry-type 'history-size))
-
-(defun guix-buffer-revert-confirm? (buffer-type entry-type)
- "Return 'revert-confirm' value for BUFFER-TYPE/ENTRY-TYPE."
- (guix-buffer-value buffer-type entry-type 'revert-confirm))
-
-(defvar guix-buffer-map
- (let ((map (make-sparse-keymap)))
- (define-key map (kbd "l") 'guix-history-back)
- (define-key map (kbd "r") 'guix-history-forward)
- (define-key map (kbd "g") 'revert-buffer)
- (define-key map (kbd "R") 'guix-buffer-redisplay)
- map)
- "Parent keymap for Guix buffer modes.")
-
-(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
-following form:
-
- ((PARAM . VAL) ...)
-
-PARAM is a name of the entry parameter.
-VAL is a value of this parameter.")
-(put 'guix-entries 'permanent-local t)
-
-(defvar-local guix-buffer-type nil
- "Type of the current buffer.")
-(put 'guix-buffer-type 'permanent-local t)
-
-(defvar-local guix-entry-type nil
- "Type of the current entry.")
-(put 'guix-entry-type 'permanent-local t)
-
-(defvar-local guix-search-type nil
- "Type of the current search.")
-(put 'guix-search-type 'permanent-local t)
-
-(defvar-local guix-search-vals nil
- "Values of the current search.")
-(put 'guix-search-vals 'permanent-local t)
-
-(defsubst guix-set-vars (profile entries buffer-type entry-type
- search-type 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-"
- (when entry-type
- (concat (symbol-name entry-type) "-"))
- (symbol-name buffer-type) "-" postfix)))
-
-(defmacro guix-buffer-define-interface (buffer-type entry-type &rest args)
- "Define BUFFER-TYPE interface for displaying ENTRY-TYPE entries.
-Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ...
-In the following description TYPE means ENTRY-TYPE-BUFFER-TYPE.
-
-The following stuff should be defined outside this macro:
-
- - `guix-BUFFER-TYPE-mode' - parent mode of the generated mode.
-
- - `guix-TYPE-mode-initialize' (optional) - function for
- additional mode settings; it is called without arguments.
-
-Required keywords:
-
- - `:buffer-name' - default value of the generated
- `guix-TYPE-buffer-name' variable.
-
-Optional keywords:
-
- - `:titles' - default value of the generated
- `guix-TYPE-titles' variable.
-
- - `:history-size' - default value of the generated
- `guix-TYPE-history-size' variable.
-
- - `:revert-confirm?' - default value of the generated
- `guix-TYPE-revert-confirm' variable.
-
- - `:reduced?' - if non-nil, generate only group, faces group
- and titles variable."
- (declare (indent 2))
- (let* ((entry-type-str (symbol-name entry-type))
- (buffer-type-str (symbol-name buffer-type))
- (Entry-type-str (capitalize entry-type-str))
- (Buffer-type-str (capitalize buffer-type-str))
- (entry-str (concat entry-type-str " entries"))
- (prefix (concat "guix-" entry-type-str "-"
- buffer-type-str))
- (group (intern prefix))
- (faces-group (intern (concat prefix "-faces")))
- (mode-map-str (concat prefix "-mode-map"))
- (parent-mode (intern (concat "guix-" buffer-type-str "-mode")))
- (mode (intern (concat prefix "-mode")))
- (mode-init-fun (intern (concat prefix "-mode-initialize")))
- (buffer-name-var (intern (concat prefix "-buffer-name")))
- (titles-var (intern (concat prefix "-titles")))
- (history-size-var (intern (concat prefix "-history-size")))
- (revert-confirm-var (intern (concat prefix "-revert-confirm"))))
- (guix-keyword-args-let args
- ((buffer-name-val :buffer-name)
- (titles-val :titles)
- (history-size-val :history-size 20)
- (revert-confirm-val :revert-confirm? t)
- (reduced? :reduced?))
- `(progn
- (defgroup ,group nil
- ,(format "Display '%s' entries in '%s' buffer."
- entry-type-str buffer-type-str)
- :prefix ,(concat prefix "-")
- :group ',(intern (concat "guix-" buffer-type-str)))
-
- (defgroup ,faces-group nil
- ,(format "Faces for displaying '%s' entries in '%s' buffer."
- entry-type-str buffer-type-str)
- :group ',(intern (concat "guix-" buffer-type-str "-faces")))
-
- (defcustom ,titles-var ,titles-val
- ,(format "Alist of titles of '%s' parameters."
- entry-type-str)
- :type '(alist :key-type symbol :value-type string)
- :group ',group)
-
- ,(unless reduced?
- `(progn
- (defcustom ,buffer-name-var ,buffer-name-val
- ,(format "\
-Default name of '%s' buffer for displaying '%s' entries."
- buffer-type-str entry-type-str)
- :type 'string
- :group ',group)
-
- (defcustom ,history-size-var ,history-size-val
- ,(format "\
-Maximum number of items saved in history of `%S' buffer.
-If 0, the history is disabled."
- buffer-name-var)
- :type 'integer
- :group ',group)
-
- (defcustom ,revert-confirm-var ,revert-confirm-val
- ,(format "\
-If non-nil, ask to confirm for reverting `%S' buffer."
- buffer-name-var)
- :type 'boolean
- :group ',group)
-
- (guix-alist-put!
- '((buffer-name . ,buffer-name-var)
- (history-size . ,history-size-var)
- (revert-confirm . ,revert-confirm-var))
- 'guix-buffer-data ',buffer-type ',entry-type)
-
- (define-derived-mode ,mode ,parent-mode
- ,(concat "Guix-" Buffer-type-str)
- ,(concat "Major mode for displaying information about "
- entry-str ".\n\n"
- "\\{" mode-map-str "}")
- (setq-local revert-buffer-function 'guix-buffer-revert)
- (setq-local guix-history-size
- (guix-buffer-history-size
- ',buffer-type ',entry-type))
- (and (fboundp ',mode-init-fun) (,mode-init-fun)))))
-
- (guix-alist-put!
- ',titles-var 'guix-buffer-data
- ',buffer-type ',entry-type 'titles)))))
-
-
;;; Getting and displaying info about packages and generations
(defcustom guix-package-list-type 'output
@@ -384,159 +158,6 @@ information)."
(const :tag "Display outputs" output))
:group 'guix)
-(defun guix-get-entries (profile entry-type search-type search-vals
- &optional params)
- "Search for entries of ENTRY-TYPE.
-
-Call an appropriate scheme function and return a list of the
-form of `guix-entries'.
-
-ENTRY-TYPE should be one of the following symbols: `package',
-`output' or `generation'.
-
-SEARCH-TYPE may be one of the following symbols:
-
-- If ENTRY-TYPE is `package' or `output': `id', `name', `regexp',
- `all-available', `newest-available', `installed', `obsolete',
- `generation'.
-
-- 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
- profile params entry-type search-type 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 profile entry-type search-type search-vals
- (guix-get-params-for-receiving
- buffer-type entry-type))))
- (guix-set-buffer profile entries buffer-type entry-type
- search-type search-vals)))
-
-(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.
-
-Insert ENTRIES in buffer, set variables and make history item.
-ENTRIES should have a form of `guix-entries'.
-
-See `guix-get-entries' for the meaning of SEARCH-TYPE and SEARCH-VALS.
-
-If HISTORY-REPLACE is non-nil, replace current history item,
-otherwise add the new one.
-
-If NO-DISPLAY is non-nil, do not switch to the buffer."
- (when entries
- (let ((buf (if (and (eq major-mode
- (guix-get-symbol "mode" buffer-type entry-type))
- (equal guix-profile profile))
- (current-buffer)
- (get-buffer-create
- (guix-buffer-name buffer-type entry-type profile)))))
- (with-current-buffer buf
- (guix-show-entries 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
- #'guix-history-add)
- (guix-make-history-item)))
- (or no-display
- (guix-switch-to-buffer buf))))
- (guix-result-message profile entries entry-type
- search-type search-vals))
-
-(defun guix-show-entries (entries buffer-type entry-type)
- "Display ENTRY-TYPE ENTRIES in the current BUFFER-TYPE buffer."
- (let ((inhibit-read-only t))
- (erase-buffer)
- (funcall (symbol-function (guix-get-symbol
- "mode" buffer-type entry-type)))
- (funcall (guix-get-symbol "insert-entries" buffer-type)
- entries entry-type)
- (goto-char (point-min))))
-
-(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 profile entries buffer-type entry-type
- search-type search-vals)
- (guix-result-message profile 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-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)
- "Return parameters that should be received for BUFFER-TYPE, ENTRY-TYPE."
- (let* ((required-var (guix-get-symbol "required-params"
- buffer-type entry-type))
- (required (symbol-value required-var)))
- (unless (equal required 'all)
- (cl-union required
- (funcall (guix-get-symbol "displayed-params"
- buffer-type)
- entry-type)))))
-
-(defun guix-buffer-revert (_ignore-auto noconfirm)
- "Update information in the current buffer.
-The function is suitable for `revert-buffer-function'.
-See `revert-buffer' for the meaning of NOCONFIRM."
- (when (or noconfirm
- (guix-buffer-revert-confirm? guix-buffer-type
- guix-entry-type)
- (y-or-n-p "Update current information? "))
- (let* ((params (guix-get-params-for-receiving guix-buffer-type
- guix-entry-type))
- (entries (guix-get-entries
- guix-profile guix-entry-type
- guix-search-type guix-search-vals params)))
- (guix-set-buffer guix-profile entries guix-buffer-type guix-entry-type
- guix-search-type guix-search-vals t t))))
-
-(defvar guix-buffer-after-redisplay-hook nil
- "Hook run by `guix-buffer-redisplay'.
-This hook is called before seting up a window position.")
-
-(defun guix-buffer-redisplay ()
- "Redisplay the current Guix buffer.
-Restore the point and window positions after redisplaying.
-
-This function does not update the buffer data, use
-'\\[revert-buffer]' if you want the full update."
- (interactive)
- (let* ((old-point (point))
- ;; For simplicity, ignore an unlikely case when multiple
- ;; windows display the same buffer.
- (window (car (get-buffer-window-list (current-buffer) nil t)))
- (window-start (and window (window-start window))))
- (guix-set-buffer guix-profile guix-entries guix-buffer-type
- guix-entry-type guix-search-type guix-search-vals
- t t)
- (goto-char old-point)
- (run-hooks 'guix-buffer-after-redisplay-hook)
- (when window
- (set-window-point window (point))
- (set-window-start window window-start))))
-
-(defun guix-buffer-redisplay-goto-button ()
- "Redisplay the current buffer and go to the next button, if needed."
- (let ((guix-buffer-after-redisplay-hook
- (cons (lambda ()
- (unless (button-at (point))
- (forward-button 1)))
- guix-buffer-after-redisplay-hook)))
- (guix-buffer-redisplay)))
-
;;; Generations
@@ -640,13 +261,14 @@ Create the buffer if needed."
(defun guix-profile-generation-manifest-file (generation)
"Return the file name of a GENERATION's manifest.
-GENERATION is a generation number of `guix-profile' profile."
- (guix-manifest-file guix-profile generation))
+GENERATION is a generation number of the current profile."
+ (guix-manifest-file (guix-ui-current-profile) generation))
(defun guix-profile-generation-packages-buffer (generation)
"Insert GENERATION's package outputs in a buffer and return it.
-GENERATION is a generation number of `guix-profile' profile."
- (guix-generation-packages-buffer guix-profile generation))
+GENERATION is a generation number of the current profile."
+ (guix-generation-packages-buffer (guix-ui-current-profile)
+ generation))
;;; Actions on packages and generations
@@ -757,7 +379,7 @@ 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
+ (let* ((entries (guix-ui-get-entries
profile 'package 'id
(append (mapcar #'car install)
(mapcar #'car upgrade)
@@ -930,12 +552,12 @@ See Info node `(guix) Invoking guix package' for details.
Interactively, use the current profile and prompt for manifest
FILE. With a prefix argument, also prompt for PROFILE."
(interactive
- (let* ((default-profile (or guix-profile guix-current-profile))
+ (let* ((current-profile (guix-ui-current-profile))
(profile (if current-prefix-arg
(guix-profile-prompt)
- default-profile))
+ (or current-profile guix-current-profile)))
(file (read-file-name "File with manifest: "))
- (buffer (and guix-profile (current-buffer))))
+ (buffer (and current-profile (current-buffer))))
(list profile file buffer)))
(when (or (not guix-operation-confirm)
(y-or-n-p (format "Apply manifest from '%s' to profile '%s'? "
diff --git a/emacs/guix-buffer.el b/emacs/guix-buffer.el
new file mode 100644
index 0000000000..5687a250aa
--- /dev/null
+++ b/emacs/guix-buffer.el
@@ -0,0 +1,566 @@
+;;; guix-buffer.el --- Buffer interface for displaying data -*- lexical-binding: t -*-
+
+;; Copyright © 2014, 2015 Alex Kost <alezost@gmail.com>
+
+;; This file is part of GNU Guix.
+
+;; GNU Guix is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Guix is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file provides a general 'buffer' interface for displaying an
+;; arbitrary data.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'guix-history)
+(require 'guix-utils)
+
+(defvar guix-buffer-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "l") 'guix-history-back)
+ (define-key map (kbd "r") 'guix-history-forward)
+ (define-key map (kbd "g") 'revert-buffer)
+ (define-key map (kbd "R") 'guix-buffer-redisplay)
+ map)
+ "Parent keymap for Guix buffer modes.")
+
+
+;;; Buffer item
+
+(cl-defstruct (guix-buffer-item
+ (:constructor nil)
+ (:constructor guix-buffer-make-item
+ (entries buffer-type entry-type args))
+ (:copier nil))
+ entries buffer-type entry-type args)
+
+(defvar-local guix-buffer-item nil
+ "Data (structure) for the current Guix buffer.
+The structure consists of the following elements:
+
+- `entries': list of the currently displayed entries.
+
+ Each element of the list is an alist with an entry data of the
+ following form:
+
+ ((PARAM . VAL) ...)
+
+ PARAM is a name of the entry parameter.
+ VAL is a value of this parameter.
+
+- `entry-type': type of the currently displayed entries.
+
+- `buffer-type': type of the current buffer.
+
+- `args': search arguments used to get the current entries.")
+(put 'guix-buffer-item 'permanent-local t)
+
+(defmacro guix-buffer-with-item (item &rest body)
+ "Evaluate BODY using buffer ITEM.
+The following local variables are available inside BODY:
+`%entries', `%buffer-type', `%entry-type', `%args'.
+See `guix-buffer-item' for details."
+ (declare (indent 1) (debug t))
+ (let ((item-var (make-symbol "item")))
+ `(let ((,item-var ,item))
+ (let ((%entries (guix-buffer-item-entries ,item-var))
+ (%buffer-type (guix-buffer-item-buffer-type ,item-var))
+ (%entry-type (guix-buffer-item-entry-type ,item-var))
+ (%args (guix-buffer-item-args ,item-var)))
+ ,@body))))
+
+(defmacro guix-buffer-with-current-item (&rest body)
+ "Evaluate BODY using `guix-buffer-item'.
+See `guix-buffer-with-item' for details."
+ (declare (indent 0) (debug t))
+ `(guix-buffer-with-item guix-buffer-item
+ ,@body))
+
+(defmacro guix-buffer-define-current-item-accessor (name)
+ "Define `guix-buffer-current-NAME' function to access NAME
+element of `guix-buffer-item' structure.
+NAME should be a symbol."
+ (let* ((name-str (symbol-name name))
+ (accessor (intern (concat "guix-buffer-item-" name-str)))
+ (fun-name (intern (concat "guix-buffer-current-" name-str)))
+ (doc (format "\
+Return '%s' of the current Guix buffer.
+See `guix-buffer-item' for details."
+ name-str)))
+ `(defun ,fun-name ()
+ ,doc
+ (and guix-buffer-item
+ (,accessor guix-buffer-item)))))
+
+(defmacro guix-buffer-define-current-item-accessors (&rest names)
+ "Define `guix-buffer-current-NAME' functions for NAMES.
+See `guix-buffer-define-current-item-accessor' for details."
+ `(progn
+ ,@(mapcar (lambda (name)
+ `(guix-buffer-define-current-item-accessor ,name))
+ names)))
+
+(guix-buffer-define-current-item-accessors
+ entries entry-type buffer-type args)
+
+(defmacro guix-buffer-define-current-args-accessor (n prefix name)
+ "Define `PREFIX-NAME' function to access Nth element of 'args'
+field of `guix-buffer-item' structure.
+PREFIX and NAME should be strings."
+ (let ((fun-name (intern (concat prefix "-" name)))
+ (doc (format "\
+Return '%s' of the current Guix buffer.
+'%s' is the element number %d in 'args' of `guix-buffer-item'."
+ name name n)))
+ `(defun ,fun-name ()
+ ,doc
+ (nth ,n (guix-buffer-current-args)))))
+
+(defmacro guix-buffer-define-current-args-accessors (prefix &rest names)
+ "Define `PREFIX-NAME' functions for NAMES.
+See `guix-buffer-define-current-args-accessor' for details."
+ `(progn
+ ,@(cl-loop for name in names
+ for i from 0
+ collect `(guix-buffer-define-current-args-accessor
+ ,i ,prefix ,name))))
+
+
+;;; Wrappers for defined variables
+
+(defvar guix-buffer-data nil
+ "Alist with 'buffer' data.
+This alist is filled by `guix-buffer-define-interface' macro.")
+
+(defun guix-buffer-value (buffer-type entry-type symbol)
+ "Return SYMBOL's value for BUFFER-TYPE/ENTRY-TYPE from `guix-buffer-data'."
+ (symbol-value
+ (guix-assq-value guix-buffer-data buffer-type entry-type symbol)))
+
+(defun guix-buffer-get-entries (buffer-type entry-type args)
+ "Return ENTRY-TYPE entries.
+Call an appropriate 'get-entries' function from `guix-buffer'
+using ARGS as its arguments."
+ (apply (guix-buffer-value buffer-type entry-type 'get-entries)
+ args))
+
+(defun guix-buffer-mode-enable (buffer-type entry-type)
+ "Turn on major mode to display ENTRY-TYPE ENTRIES in BUFFER-TYPE buffer."
+ (funcall (guix-buffer-value buffer-type entry-type 'mode)))
+
+(defun guix-buffer-mode-initialize (buffer-type entry-type)
+ "Set up the current BUFFER-TYPE buffer to display ENTRY-TYPE entries."
+ (let ((fun (guix-buffer-value buffer-type entry-type 'mode-init)))
+ (when fun
+ (funcall fun))))
+
+(defun guix-buffer-insert-entries (entries buffer-type entry-type)
+ "Show ENTRY-TYPE ENTRIES in the current BUFFER-TYPE buffer."
+ (funcall (guix-buffer-value buffer-type entry-type 'insert-entries)
+ entries))
+
+(defun guix-buffer-show-entries-default (entries buffer-type entry-type)
+ "Show ENTRY-TYPE ENTRIES in the current BUFFER-TYPE buffer."
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (guix-buffer-mode-enable buffer-type entry-type)
+ (guix-buffer-insert-entries entries buffer-type entry-type)
+ (goto-char (point-min))))
+
+(defun guix-buffer-show-entries (entries buffer-type entry-type)
+ "Show ENTRY-TYPE ENTRIES in the current BUFFER-TYPE buffer."
+ (funcall (guix-buffer-value buffer-type entry-type 'show-entries)
+ entries))
+
+(defun guix-buffer-message (entries buffer-type entry-type args)
+ "Display a message for BUFFER-ITEM after showing entries."
+ (let ((fun (guix-buffer-value buffer-type entry-type 'message)))
+ (when fun
+ (apply fun entries args))))
+
+(defun guix-buffer-name (buffer-type entry-type args)
+ "Return name of BUFFER-TYPE buffer for displaying ENTRY-TYPE entries."
+ (let ((str-or-fun (guix-buffer-value buffer-type entry-type
+ 'buffer-name)))
+ (if (stringp str-or-fun)
+ str-or-fun
+ (apply str-or-fun args))))
+
+(defun guix-buffer-param-title (buffer-type entry-type param)
+ "Return PARAM title for BUFFER-TYPE/ENTRY-TYPE."
+ (or (guix-assq-value (guix-buffer-value buffer-type entry-type 'titles)
+ param)
+ ;; Fallback to a title defined in 'info' interface.
+ (unless (eq buffer-type 'info)
+ (guix-assq-value (guix-buffer-value 'info entry-type 'titles)
+ param))
+ (guix-symbol-title param)))
+
+(defun guix-buffer-history-size (buffer-type entry-type)
+ "Return history size for BUFFER-TYPE/ENTRY-TYPE."
+ (guix-buffer-value buffer-type entry-type 'history-size))
+
+(defun guix-buffer-revert-confirm? (buffer-type entry-type)
+ "Return 'revert-confirm' value for BUFFER-TYPE/ENTRY-TYPE."
+ (guix-buffer-value buffer-type entry-type 'revert-confirm))
+
+
+;;; Displaying entries
+
+(defun guix-buffer-display (buffer)
+ "Switch to a Guix BUFFER."
+ (pop-to-buffer buffer
+ '((display-buffer-reuse-window
+ display-buffer-same-window))))
+
+(defun guix-buffer-history-item (buffer-item)
+ "Make and return a history item for displaying BUFFER-ITEM."
+ (list #'guix-buffer-set buffer-item))
+
+(defun guix-buffer-set (buffer-item &optional history)
+ "Set up the current buffer for displaying BUFFER-ITEM.
+HISTORY should be one of the following:
+
+ `nil' - do not save BUFFER-ITEM in history,
+
+ `add' - add it to history,
+
+ `replace' - replace the current history item."
+ (guix-buffer-with-item buffer-item
+ (when %entries
+ (guix-buffer-show-entries %entries %buffer-type %entry-type)
+ (setq guix-buffer-item buffer-item)
+ (when history
+ (funcall (cl-ecase history
+ (add #'guix-history-add)
+ (replace #'guix-history-replace))
+ (guix-buffer-history-item buffer-item))))
+ (guix-buffer-message %entries %buffer-type %entry-type %args)))
+
+(defun guix-buffer-display-entries-current
+ (entries buffer-type entry-type args &optional history)
+ "Show ENTRIES in the current Guix buffer.
+See `guix-buffer-item' for the meaning of BUFFER-TYPE, ENTRY-TYPE
+and ARGS, and `guix-buffer-set' for the meaning of HISTORY."
+ (let ((item (guix-buffer-make-item entries buffer-type
+ entry-type args)))
+ (guix-buffer-set item history)))
+
+(defun guix-buffer-get-display-entries-current
+ (buffer-type entry-type args &optional history)
+ "Search for entries and show them in the current Guix buffer.
+See `guix-buffer-display-entries-current' for details."
+ (guix-buffer-display-entries-current
+ (guix-buffer-get-entries buffer-type entry-type args)
+ buffer-type entry-type args history))
+
+(defun guix-buffer-display-entries
+ (entries buffer-type entry-type args &optional history)
+ "Show ENTRIES in a BUFFER-TYPE buffer.
+See `guix-buffer-display-entries-current' for details."
+ (let ((buffer (get-buffer-create
+ (guix-buffer-name buffer-type entry-type args))))
+ (with-current-buffer buffer
+ (guix-buffer-display-entries-current
+ entries buffer-type entry-type args history))
+ (when entries
+ (guix-buffer-display buffer))))
+
+(defun guix-buffer-get-display-entries
+ (buffer-type entry-type args &optional history)
+ "Search for entries and show them in a BUFFER-TYPE buffer.
+See `guix-buffer-display-entries-current' for details."
+ (guix-buffer-display-entries
+ (guix-buffer-get-entries buffer-type entry-type args)
+ buffer-type entry-type args history))
+
+(defun guix-buffer-revert (_ignore-auto noconfirm)
+ "Update the data in the current Guix buffer.
+This function is suitable for `revert-buffer-function'.
+See `revert-buffer' for the meaning of NOCONFIRM."
+ (guix-buffer-with-current-item
+ (when (or noconfirm
+ (not (guix-buffer-revert-confirm? %buffer-type %entry-type))
+ (y-or-n-p "Update the current buffer? "))
+ (guix-buffer-get-display-entries-current
+ %buffer-type %entry-type %args 'replace))))
+
+(defvar guix-buffer-after-redisplay-hook nil
+ "Hook run by `guix-buffer-redisplay'.
+This hook is called before seting up a window position.")
+
+(defun guix-buffer-redisplay ()
+ "Redisplay the current Guix buffer.
+Restore the point and window positions after redisplaying.
+
+This function does not update the buffer data, use
+'\\[revert-buffer]' if you want the full update."
+ (interactive)
+ (let* ((old-point (point))
+ ;; For simplicity, ignore an unlikely case when multiple
+ ;; windows display the same buffer.
+ (window (car (get-buffer-window-list (current-buffer) nil t)))
+ (window-start (and window (window-start window))))
+ (guix-buffer-set guix-buffer-item)
+ (goto-char old-point)
+ (run-hooks 'guix-buffer-after-redisplay-hook)
+ (when window
+ (set-window-point window (point))
+ (set-window-start window window-start))))
+
+(defun guix-buffer-redisplay-goto-button ()
+ "Redisplay the current buffer and go to the next button, if needed."
+ (let ((guix-buffer-after-redisplay-hook
+ (cons (lambda ()
+ (unless (button-at (point))
+ (forward-button 1)))
+ guix-buffer-after-redisplay-hook)))
+ (guix-buffer-redisplay)))
+
+
+;;; Interface definer
+
+(defmacro guix-buffer-define-interface (buffer-type entry-type &rest args)
+ "Define BUFFER-TYPE interface for displaying ENTRY-TYPE entries.
+Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ...
+In the following description TYPE means ENTRY-TYPE-BUFFER-TYPE.
+
+Required keywords:
+
+ - `:buffer-name' - default value of the generated
+ `guix-TYPE-buffer-name' variable.
+
+ - `:get-entries-function' - default value of the generated
+ `guix-TYPE-get-function' variable.
+
+ - `:show-entries-function' - default value of the generated
+ `guix-TYPE-show-function' variable.
+
+ Alternatively, if `:show-entries-function' is not specified, a
+ default `guix-TYPE-show-entries' will be generated, and the
+ following keyword should be specified instead:
+
+ - `:insert-entries-function' - default value of the generated
+ `guix-TYPE-insert-function' variable.
+
+Optional keywords:
+
+ - `:message-function' - default value of the generated
+ `guix-TYPE-message-function' variable.
+
+ - `:titles' - default value of the generated
+ `guix-TYPE-titles' variable.
+
+ - `:history-size' - default value of the generated
+ `guix-TYPE-history-size' variable.
+
+ - `:revert-confirm?' - default value of the generated
+ `guix-TYPE-revert-confirm' variable.
+
+ - `:mode-name' - name (a string appeared in the mode-line) of
+ the generated `guix-TYPE-mode'.
+
+ - `:mode-init-function' - default value of the generated
+ `guix-TYPE-mode-initialize-function' variable.
+
+ - `:reduced?' - if non-nil, generate only group, faces group
+ and titles variable (if specified); all keywords become
+ optional."
+ (declare (indent 2))
+ (let* ((entry-type-str (symbol-name entry-type))
+ (buffer-type-str (symbol-name buffer-type))
+ (prefix (concat "guix-" entry-type-str "-"
+ buffer-type-str))
+ (group (intern prefix))
+ (faces-group (intern (concat prefix "-faces")))
+ (get-entries-var (intern (concat prefix "-get-function")))
+ (show-entries-var (intern (concat prefix "-show-function")))
+ (show-entries-fun (intern (concat prefix "-show-entries")))
+ (message-var (intern (concat prefix "-message-function")))
+ (buffer-name-var (intern (concat prefix "-buffer-name")))
+ (titles-var (intern (concat prefix "-titles")))
+ (history-size-var (intern (concat prefix "-history-size")))
+ (revert-confirm-var (intern (concat prefix "-revert-confirm"))))
+ (guix-keyword-args-let args
+ ((get-entries-val :get-entries-function)
+ (show-entries-val :show-entries-function)
+ (insert-entries-val :insert-entries-function)
+ (mode-name :mode-name (capitalize prefix))
+ (mode-init-val :mode-init-function)
+ (message-val :message-function)
+ (buffer-name-val :buffer-name)
+ (titles-val :titles)
+ (history-size-val :history-size 20)
+ (revert-confirm-val :revert-confirm? t)
+ (reduced? :reduced?))
+ `(progn
+ (defgroup ,group nil
+ ,(format "Display '%s' entries in '%s' buffer."
+ entry-type-str buffer-type-str)
+ :prefix ,(concat prefix "-")
+ :group ',(intern (concat "guix-" buffer-type-str)))
+
+ (defgroup ,faces-group nil
+ ,(format "Faces for displaying '%s' entries in '%s' buffer."
+ entry-type-str buffer-type-str)
+ :group ',(intern (concat "guix-" buffer-type-str "-faces")))
+
+ (defcustom ,titles-var ,titles-val
+ ,(format "Alist of titles of '%s' parameters."
+ entry-type-str)
+ :type '(alist :key-type symbol :value-type string)
+ :group ',group)
+
+ ,(unless reduced?
+ `(progn
+ (defvar ,get-entries-var ,get-entries-val
+ ,(format "\
+Function used to receive '%s' entries for '%s' buffer."
+ entry-type-str buffer-type-str))
+
+ (defvar ,show-entries-var
+ ,(or show-entries-val `',show-entries-fun)
+ ,(format "\
+Function used to show '%s' entries in '%s' buffer."
+ entry-type-str buffer-type-str))
+
+ (defvar ,message-var ,message-val
+ ,(format "\
+Function used to display a message after showing '%s' entries.
+If nil, do not display messages."
+ entry-type-str))
+
+ (defcustom ,buffer-name-var ,buffer-name-val
+ ,(format "\
+Default name of '%s' buffer for displaying '%s' entries.
+May be a string or a function returning a string. The function
+is called with the same arguments as `%S'."
+ buffer-type-str entry-type-str get-entries-var)
+ :type '(choice string function)
+ :group ',group)
+
+ (defcustom ,history-size-var ,history-size-val
+ ,(format "\
+Maximum number of items saved in history of `%S' buffer.
+If 0, the history is disabled."
+ buffer-name-var)
+ :type 'integer
+ :group ',group)
+
+ (defcustom ,revert-confirm-var ,revert-confirm-val
+ ,(format "\
+If non-nil, ask to confirm for reverting `%S' buffer."
+ buffer-name-var)
+ :type 'boolean
+ :group ',group)
+
+ (guix-alist-put!
+ '((get-entries . ,get-entries-var)
+ (show-entries . ,show-entries-var)
+ (message . ,message-var)
+ (buffer-name . ,buffer-name-var)
+ (history-size . ,history-size-var)
+ (revert-confirm . ,revert-confirm-var))
+ 'guix-buffer-data ',buffer-type ',entry-type)
+
+ ,(unless show-entries-val
+ `(defun ,show-entries-fun (entries)
+ ,(format "\
+Show '%s' ENTRIES in the current '%s' buffer."
+ entry-type-str buffer-type-str)
+ (guix-buffer-show-entries-default
+ entries ',buffer-type ',entry-type)))
+
+ ,(when (or insert-entries-val
+ (null show-entries-val))
+ (let ((insert-entries-var
+ (intern (concat prefix "-insert-function"))))
+ `(progn
+ (defvar ,insert-entries-var ,insert-entries-val
+ ,(format "\
+Function used to print '%s' entries in '%s' buffer."
+ entry-type-str buffer-type-str))
+
+ (guix-alist-put!
+ ',insert-entries-var 'guix-buffer-data
+ ',buffer-type ',entry-type
+ 'insert-entries))))
+
+ ,(when (or mode-name
+ mode-init-val
+ (null show-entries-val))
+ (let* ((mode-str (concat prefix "-mode"))
+ (mode-map-str (concat mode-str "-map"))
+ (mode (intern mode-str))
+ (parent-mode (intern
+ (concat "guix-" buffer-type-str
+ "-mode")))
+ (mode-var (intern
+ (concat mode-str "-function")))
+ (mode-init-var (intern
+ (concat mode-str
+ "-initialize-function"))))
+ `(progn
+ (defvar ,mode-var ',mode
+ ,(format "\
+Major mode for displaying '%s' entries in '%s' buffer."
+ entry-type-str buffer-type-str))
+
+ (defvar ,mode-init-var ,mode-init-val
+ ,(format "\
+Function used to set up '%s' buffer for displaying '%s' entries."
+ buffer-type-str entry-type-str))
+
+ (define-derived-mode ,mode ,parent-mode ,mode-name
+ ,(format "\
+Major mode for displaying '%s' entries in '%s' buffer.
+
+\\{%s}"
+ entry-type-str buffer-type-str mode-map-str)
+ (setq-local revert-buffer-function
+ 'guix-buffer-revert)
+ (setq-local guix-history-size
+ (guix-buffer-history-size
+ ',buffer-type ',entry-type))
+ (guix-buffer-mode-initialize
+ ',buffer-type ',entry-type))
+
+ (guix-alist-put!
+ ',mode-var 'guix-buffer-data
+ ',buffer-type ',entry-type 'mode)
+ (guix-alist-put!
+ ',mode-init-var 'guix-buffer-data
+ ',buffer-type ',entry-type
+ 'mode-init))))))
+
+ (guix-alist-put!
+ ',titles-var 'guix-buffer-data
+ ',buffer-type ',entry-type 'titles)))))
+
+
+(defvar guix-buffer-font-lock-keywords
+ (eval-when-compile
+ `((,(rx "(" (group (or "guix-buffer-with-item"
+ "guix-buffer-with-current-item"
+ "guix-buffer-define-interface"))
+ symbol-end)
+ . 1))))
+
+(font-lock-add-keywords 'emacs-lisp-mode guix-buffer-font-lock-keywords)
+
+(provide 'guix-buffer)
+
+;;; guix-buffer.el ends here
diff --git a/emacs/guix-info.el b/emacs/guix-info.el
index 871c4b085f..9c63892d06 100644
--- a/emacs/guix-info.el
+++ b/emacs/guix-info.el
@@ -202,8 +202,7 @@ LEVEL is 1 by default."
(insert (guix-info-get-indent level)))
(defun guix-info-insert-entries (entries entry-type)
- "Display ENTRIES of ENTRY-TYPE in the current info buffer.
-ENTRIES should have a form of `guix-entries'."
+ "Display ENTRY-TYPE ENTRIES in the current info buffer."
(guix-mapinsert (lambda (entry)
(guix-info-insert-entry entry entry-type))
entries
@@ -371,8 +370,11 @@ BUTTON-OR-FACE is a button type)."
'face 'guix-package-info-name-button
'help-echo "Describe this package"
'action (lambda (btn)
- (guix-get-show-entries guix-profile 'info guix-package-info-type
- 'name (button-label btn))))
+ (guix-buffer-get-display-entries-current
+ 'info guix-package-info-type
+ (list (guix-ui-current-profile)
+ 'name (button-label btn))
+ 'add)))
(defun guix-info-button-copy-label (&optional pos)
"Copy a label of the button at POS into kill ring.
@@ -407,7 +409,8 @@ See `insert-text-button' for the meaning of PROPERTIES."
"Keymap for `guix-info-mode' buffers.")
(define-derived-mode guix-info-mode special-mode "Guix-Info"
- "Parent mode for displaying information in info buffers.")
+ "Parent mode for displaying data in 'info' form."
+ (setq-local revert-buffer-function 'guix-buffer-revert))
(defun guix-info-mode-initialize ()
"Set up the current 'info' buffer."
@@ -435,7 +438,8 @@ The rest keyword arguments are passed to
(group (intern prefix))
(format-var (intern (concat prefix "-format"))))
(guix-keyword-args-let args
- ((format-val :format))
+ ((show-entries-val :show-entries-function)
+ (format-val :format))
`(progn
(defcustom ,format-var ,format-val
,(format "\
@@ -473,9 +477,23 @@ After calling each METHOD, a new line is inserted."
'((format . ,format-var))
'guix-info-data ',entry-type)
- (guix-buffer-define-interface info ,entry-type
- :mode-init-function 'guix-info-mode-initialize
- ,@%foreign-args)))))
+ ,(if show-entries-val
+ `(guix-buffer-define-interface info ,entry-type
+ :show-entries-function ,show-entries-val
+ ,@%foreign-args)
+
+ (let ((insert-fun (intern (concat prefix "-insert-entries"))))
+ `(progn
+ (defun ,insert-fun (entries)
+ ,(format "\
+Print '%s' ENTRIES in the current 'info' buffer."
+ entry-type-str)
+ (guix-info-insert-entries entries ',entry-type))
+
+ (guix-buffer-define-interface info ,entry-type
+ :insert-entries-function ',insert-fun
+ :mode-init-function 'guix-info-mode-initialize
+ ,@%foreign-args))))))))
;;; Displaying packages
@@ -675,7 +693,7 @@ ENTRY is an alist with package info."
type-str
(lambda (btn)
(guix-process-package-actions
- guix-profile
+ (guix-ui-current-profile)
`((,(button-get btn 'action-type) (,(button-get btn 'id)
,(button-get btn 'output))))
(current-buffer)))
@@ -726,15 +744,16 @@ prompt depending on `guix-operation-confirm' variable)."
Find the file if needed (see `guix-package-info-auto-find-source').
ENTRY-ID is an ID of the current entry (package or output).
PACKAGE-ID is an ID of the package which source to show."
- (let* ((entries guix-entries)
- (entry (guix-entry-by-id entry-id guix-entries))
+ (let* ((entries (guix-buffer-current-entries))
+ (entry (guix-entry-by-id entry-id entries))
(file (guix-package-source-path package-id)))
(or file
(error "Couldn't define file name of the package source"))
(let* ((new-entry (cons (cons 'source-file file)
entry))
(new-entries (guix-replace-entry entry-id new-entry entries)))
- (setq guix-entries new-entries)
+ (setf (guix-buffer-item-entries guix-buffer-item)
+ new-entries)
(guix-buffer-redisplay-goto-button)
(if (file-exists-p file)
(if guix-package-info-auto-find-source
@@ -872,15 +891,19 @@ This function is used to hide a \"Download\" button if needed."
(guix-info-insert-action-button
"Packages"
(lambda (btn)
- (guix-get-show-entries guix-profile 'list guix-package-list-type
- 'generation (button-get btn 'number)))
+ (guix-buffer-get-display-entries
+ 'list guix-package-list-type
+ (list (guix-ui-current-profile)
+ 'generation (button-get btn 'number))
+ 'add))
"Show installed packages for this generation"
'number number)
(guix-info-insert-indent)
(guix-info-insert-action-button
"Delete"
(lambda (btn)
- (guix-delete-generations guix-profile (list (button-get btn 'number))
+ (guix-delete-generations (guix-ui-current-profile)
+ (list (button-get btn 'number))
(current-buffer)))
"Delete this generation"
'number number))
@@ -894,7 +917,8 @@ This function is used to hide a \"Download\" button if needed."
(guix-info-insert-action-button
"Switch"
(lambda (btn)
- (guix-switch-to-generation guix-profile (button-get btn 'number)
+ (guix-switch-to-generation (guix-ui-current-profile)
+ (button-get btn 'number)
(current-buffer)))
"Switch to this generation (make it the current one)"
'number (guix-entry-value entry 'number))))
diff --git a/emacs/guix-list.el b/emacs/guix-list.el
index 42bc0c87f5..f5c50389ed 100644
--- a/emacs/guix-list.el
+++ b/emacs/guix-list.el
@@ -61,7 +61,7 @@ With prefix argument, describe entries marked with any mark."
(let* ((ids (or (apply #'guix-list-get-marked-id-list mark-names)
(list (guix-list-current-id))))
(count (length ids))
- (entry-type guix-entry-type))
+ (entry-type (guix-buffer-current-entry-type)))
(when (or (<= count (guix-list-describe-warning-count entry-type))
(y-or-n-p (format "Do you really want to describe %d entries? "
count)))
@@ -168,8 +168,7 @@ Return a vector made of values of FUN calls."
rest-spec))))
(defun guix-list-insert-entries (entries entry-type)
- "Display ENTRIES of ENTRY-TYPE in the current list buffer.
-ENTRIES should have a form of `guix-entries'."
+ "Print ENTRY-TYPE ENTRIES in the current buffer."
(setq tabulated-list-entries
(guix-list-tabulated-entries entries entry-type))
(tabulated-list-print))
@@ -212,14 +211,18 @@ VAL may be nil."
'follow-link t
'help-echo "Find file"))
+
+;;; 'List' lines
+
(defun guix-list-current-id ()
- "Return ID of the current entry."
+ "Return ID of the entry at point."
(or (tabulated-list-get-id)
(user-error "No entry here")))
(defun guix-list-current-entry ()
- "Return alist of the current entry info."
- (guix-entry-by-id (guix-list-current-id) guix-entries))
+ "Return entry at point."
+ (guix-entry-by-id (guix-list-current-id)
+ (guix-buffer-current-entries)))
(defun guix-list-for-each-line (fun &rest args)
"Call FUN with ARGS for each entry line."
@@ -429,8 +432,6 @@ The rest keyword arguments are passed to
(let* ((entry-type-str (symbol-name entry-type))
(prefix (concat "guix-" entry-type-str "-list"))
(group (intern prefix))
- (mode-str (concat prefix "-mode"))
- (init-fun (intern (concat prefix "-mode-initialize")))
(describe-var (intern (concat prefix "-describe-function")))
(describe-count-var (intern (concat prefix
"-describe-warning-count")))
@@ -438,7 +439,8 @@ The rest keyword arguments are passed to
(sort-key-var (intern (concat prefix "-sort-key")))
(marks-var (intern (concat prefix "-marks"))))
(guix-keyword-args-let args
- ((describe-val :describe-function)
+ ((show-entries-val :show-entries-function)
+ (describe-val :describe-function)
(describe-count-val :describe-count 10)
(format-val :format)
(sort-key-val :sort-key)
@@ -498,10 +500,6 @@ See also `guix-list-describe'."
,(format "Function used to describe '%s' entries."
entry-type-str))
- (defun ,init-fun ()
- ,(concat "Initial settings for `" mode-str "'.")
- (guix-list-mode-initialize ',entry-type))
-
(guix-alist-put!
'((describe . ,describe-var)
(describe-count . ,describe-count-var)
@@ -510,8 +508,30 @@ See also `guix-list-describe'."
(marks . ,marks-var))
'guix-list-data ',entry-type)
- (guix-buffer-define-interface list ,entry-type
- ,@%foreign-args)))))
+ ,(if show-entries-val
+ `(guix-buffer-define-interface list ,entry-type
+ :show-entries-function ,show-entries-val
+ ,@%foreign-args)
+
+ (let ((insert-fun (intern (concat prefix "-insert-entries")))
+ (mode-init-fun (intern (concat prefix "-mode-initialize"))))
+ `(progn
+ (defun ,insert-fun (entries)
+ ,(format "\
+Print '%s' ENTRIES in the current 'list' buffer."
+ entry-type-str)
+ (guix-list-insert-entries entries ',entry-type))
+
+ (defun ,mode-init-fun ()
+ ,(format "\
+Set up the current 'list' buffer for displaying '%s' entries."
+ entry-type-str)
+ (guix-list-mode-initialize ',entry-type))
+
+ (guix-buffer-define-interface list ,entry-type
+ :insert-entries-function ',insert-fun
+ :mode-init-function ',mode-init-fun
+ ,@%foreign-args))))))))
;;; Displaying packages
@@ -584,7 +604,7 @@ Colorize it with `guix-package-list-installed' or
(when (and (not guix-package-list-generation-marking-enabled)
(or (derived-mode-p 'guix-package-list-mode)
(derived-mode-p 'guix-output-list-mode))
- (eq guix-search-type 'generation))
+ (eq (guix-ui-current-search-type) 'generation))
(error "Action marks are disabled for lists of 'generation packages'")))
(defun guix-package-list-mark-outputs (mark default
@@ -655,7 +675,7 @@ accept an entry as argument."
(let ((obsolete (cl-remove-if-not
(lambda (entry)
(guix-entry-value entry 'obsolete))
- guix-entries)))
+ (guix-buffer-current-entries))))
(guix-list-for-each-line
(lambda ()
(let* ((id (guix-list-current-id))
@@ -682,8 +702,8 @@ FUN should accept action-type as argument."
(let ((actions (delq nil
(mapcar fun '(install delete upgrade)))))
(if actions
- (guix-process-package-actions
- guix-profile actions (current-buffer))
+ (guix-process-package-actions (guix-ui-current-profile)
+ actions (current-buffer))
(user-error "No operations specified"))))
(defun guix-package-list-execute ()
@@ -714,7 +734,7 @@ The specification is suitable for `guix-process-package-actions'."
(output nil 9 t)
(installed nil 12 t)
(synopsis guix-list-get-one-line 30 nil))
- :required '(package-id)
+ :required '(id package-id)
:sort-key '(name)
:marks '((install . ?I)
(upgrade . ?U)
@@ -784,15 +804,19 @@ The specification is suitable for `guix-process-output-actions'."
"Describe outputs with IDS (list of output identifiers).
See `guix-package-info-type'."
(if (eq guix-package-info-type 'output)
- (apply #'guix-get-show-entries
- guix-profile 'info 'output 'id ids)
+ (guix-buffer-get-display-entries
+ 'info 'output
+ (cl-list* (guix-ui-current-profile) 'id ids)
+ 'add)
(let ((pids (mapcar (lambda (oid)
(car (guix-package-id-and-output-by-output-id
oid)))
ids)))
- (apply #'guix-get-show-entries
- guix-profile 'info 'package 'id
- (cl-remove-duplicates pids)))))
+ (guix-buffer-get-display-entries
+ 'info 'package
+ (cl-list* (guix-ui-current-profile)
+ 'id (cl-remove-duplicates pids))
+ 'add))))
(defun guix-output-list-edit ()
"Go to the location of the current package."
@@ -837,13 +861,15 @@ VAL is a boolean value."
(number (guix-entry-value entry 'number)))
(if current
(user-error "This generation is already the current one")
- (guix-switch-to-generation guix-profile number (current-buffer)))))
+ (guix-switch-to-generation (guix-ui-current-profile)
+ number (current-buffer)))))
(defun guix-generation-list-show-packages ()
"List installed packages for the generation at point."
(interactive)
- (guix-get-show-entries guix-profile 'list guix-package-list-type
- 'generation (guix-list-current-id)))
+ (guix-get-show-packages
+ (guix-ui-current-profile)
+ 'generation (guix-list-current-id)))
(defun guix-generation-list-generations-to-compare ()
"Return a sorted list of 2 marked generations for comparing."
@@ -858,9 +884,12 @@ If 2 generations are marked with \\[guix-list-mark], display
outputs installed in the latest marked generation that were not
installed in the other one."
(interactive)
- (apply #'guix-get-show-entries
- guix-profile 'list 'output 'generation-diff
- (reverse (guix-generation-list-generations-to-compare))))
+ (guix-buffer-get-display-entries
+ 'list 'output
+ (cl-list* (guix-ui-current-profile)
+ 'generation-diff
+ (reverse (guix-generation-list-generations-to-compare)))
+ 'add))
(defun guix-generation-list-show-removed-packages ()
"List package outputs removed from the latest marked generation.
@@ -868,9 +897,12 @@ If 2 generations are marked with \\[guix-list-mark], display
outputs not installed in the latest marked generation that were
installed in the other one."
(interactive)
- (apply #'guix-get-show-entries
- guix-profile 'list 'output 'generation-diff
- (guix-generation-list-generations-to-compare)))
+ (guix-buffer-get-display-entries
+ 'list 'output
+ (cl-list* (guix-ui-current-profile)
+ 'generation-diff
+ (guix-generation-list-generations-to-compare))
+ 'add))
(defun guix-generation-list-compare (diff-fun gen-fun)
"Run GEN-FUN on the 2 marked generations and run DIFF-FUN on the results."
@@ -938,7 +970,8 @@ 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 guix-profile marked (current-buffer))))
+ (guix-delete-generations (guix-ui-current-profile)
+ marked (current-buffer))))
(defvar guix-list-font-lock-keywords
diff --git a/emacs/guix-ui.el b/emacs/guix-ui.el
index a92439baf1..55c3f1b55c 100644
--- a/emacs/guix-ui.el
+++ b/emacs/guix-ui.el
@@ -26,7 +26,10 @@
(require 'cl-lib)
(require 'guix-backend)
+(require 'guix-buffer)
+(require 'guix-guile)
(require 'guix-utils)
+(require 'guix-messages)
(defgroup guix-ui nil
"Settings for Guix package management.
@@ -41,10 +44,38 @@ generations in 'list' and 'info' buffers."
map)
"Parent keymap for Guix package/generation buffers.")
+(guix-buffer-define-current-args-accessors
+ "guix-ui-current" "profile" "search-type" "search-values")
+
+(defun guix-ui-get-entries (profile entry-type search-type search-values
+ &optional params)
+ "Receive ENTRY-TYPE entries for PROFILE.
+Call an appropriate scheme procedure and return a list of entries.
+
+ENTRY-TYPE should be one of the following symbols: `package',
+`output' or `generation'.
+
+SEARCH-TYPE may be one of the following symbols:
+
+- If ENTRY-TYPE is `package' or `output': `id', `name', `regexp',
+ `all-available', `newest-available', `installed', `obsolete',
+ `generation'.
+
+- If ENTRY-TYPE is `generation': `id', `last', `all', `time'.
+
+PARAMS is a list of parameters for receiving. If nil, get data
+with all available parameters."
+ (guix-eval-read
+ (guix-make-guile-expression
+ 'entries
+ profile params entry-type search-type search-values)))
+
(defun guix-ui-list-describe (ids)
"Describe 'ui' entries with IDS (list of identifiers)."
- (apply #'guix-get-show-entries
- guix-profile 'info guix-entry-type 'id ids))
+ (guix-buffer-get-display-entries
+ 'info (guix-buffer-current-entry-type)
+ (cl-list* (guix-ui-current-profile) 'id ids)
+ 'add))
;;; Buffers and auto updating
@@ -161,7 +192,16 @@ Optional keywords:
`guix-TYPE-required-params' variable.
The rest keyword arguments are passed to
-`guix-BUFFER-TYPE-define-interface' macro."
+`guix-BUFFER-TYPE-define-interface' macro.
+
+Along with the mentioned definitions, this macro also defines:
+
+ - `guix-TYPE-mode-map' - keymap based on `guix-ui-map' and
+ `guix-BUFFER-TYPE-mode-map'.
+
+ - `guix-TYPE-get-entries' - a wrapper around `guix-ui-get-entries'.
+
+ - `guix-TYPE-message' - a wrapper around `guix-result-message'."
(declare (indent 2))
(let* ((entry-type-str (symbol-name entry-type))
(buffer-type-str (symbol-name buffer-type))
@@ -173,6 +213,10 @@ The rest keyword arguments are passed to
buffer-type-str)))
(required-var (intern (concat prefix "-required-params")))
(buffer-name-fun (intern (concat prefix "-buffer-name")))
+ (get-fun (intern (concat prefix "-get-entries")))
+ (message-fun (intern (concat prefix "-message")))
+ (displayed-fun (intern (format "guix-%s-displayed-params"
+ buffer-type-str)))
(definer (intern (format "guix-%s-define-interface"
buffer-type-str))))
(guix-keyword-args-let args
@@ -188,9 +232,13 @@ The rest keyword arguments are passed to
(defvar ,required-var ,required-val
,(format "\
-List of the required '%s' parameters for '%s' buffer.
-These parameters are received along with the displayed parameters."
- entry-type-str buffer-type-str))
+List of the required '%s' parameters.
+These parameters are received by `%S'
+along with the displayed parameters.
+
+Do not remove `id' from this list as it is required for
+identifying an entry."
+ entry-type-str get-fun))
(defun ,buffer-name-fun (profile &rest _)
,(format "\
@@ -199,7 +247,27 @@ See `guix-ui-buffer-name' for details."
buffer-type-str entry-type-str)
(guix-ui-buffer-name ,buffer-name-val profile))
+ (defun ,get-fun (profile search-type &rest search-values)
+ ,(format "\
+Receive '%s' entries for displaying them in '%s' buffer.
+See `guix-ui-get-entries' for details."
+ entry-type-str buffer-type-str)
+ (guix-ui-get-entries
+ profile ',entry-type search-type search-values
+ (cl-union ,required-var
+ (,displayed-fun ',entry-type))))
+
+ (defun ,message-fun (entries profile search-type
+ &rest search-values)
+ ,(format "\
+Display a message after showing '%s' entries."
+ entry-type-str)
+ (guix-result-message
+ profile entries ',entry-type search-type search-values))
+
(,definer ,entry-type
+ :get-entries-function ',get-fun
+ :message-function ',message-fun
:buffer-name ',buffer-name-fun
,@%foreign-args)))))
diff --git a/emacs/guix.el b/emacs/guix.el
index ac6efbb475..12dd4a2553 100644
--- a/emacs/guix.el
+++ b/emacs/guix.el
@@ -56,42 +56,39 @@ 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 (profile search-type &rest search-vals)
+(defun guix-get-show-packages (profile search-type &rest search-values)
"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.
+See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and
+SEARCH-VALUES.
Results are displayed in the list buffer, unless a single package
is found and `guix-list-single-package' is nil."
- (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))))
+ (let* ((args (cl-list* (or profile guix-current-profile)
+ search-type search-values))
+ (entries (guix-buffer-get-entries
+ 'list guix-package-list-type args)))
(if (or guix-list-single-package
- (cdr packages))
- (guix-set-buffer profile packages 'list guix-package-list-type
- search-type search-vals)
- (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 profile packages 'info guix-package-info-type
- search-type search-vals)))))
-
-(defun guix-get-show-generations (profile search-type &rest search-vals)
+ (null entries)
+ (cdr entries))
+ (guix-buffer-display-entries
+ entries 'list guix-package-list-type args 'add)
+ (guix-buffer-get-display-entries
+ 'info guix-package-info-type args 'add))))
+
+(defun guix-get-show-generations (profile search-type &rest search-values)
"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))
+See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and
+SEARCH-VALUES."
+ (let ((args (cl-list* (or profile guix-current-profile)
+ search-type search-values)))
+ (guix-buffer-get-display-entries
+ 'list 'generation args 'add)))
;;;###autoload
(defun guix-search-by-name (name &optional profile)