Archived
1
0
Fork 0

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.
This commit is contained in:
Alex Kost 2015-12-02 15:24:07 +03:00
parent 8103c22fea
commit 6c40b7b703
7 changed files with 779 additions and 468 deletions

View file

@ -22,6 +22,7 @@ ELFILES = \
emacs/guix-backend.el \ emacs/guix-backend.el \
emacs/guix-base.el \ emacs/guix-base.el \
emacs/guix-build-log.el \ emacs/guix-build-log.el \
emacs/guix-buffer.el \
emacs/guix-command.el \ emacs/guix-command.el \
emacs/guix-devel.el \ emacs/guix-devel.el \
emacs/guix-emacs.el \ emacs/guix-emacs.el \

View file

@ -22,9 +22,6 @@
;; This file provides some base and common definitions for guix.el ;; This file provides some base and common definitions for guix.el
;; package. ;; package.
;; List and info buffers have many common patterns that are defined
;; using `guix-buffer-define-interface' macro from this file.
;;; Code: ;;; Code:
(require 'cl-lib) (require 'cl-lib)
@ -34,8 +31,6 @@
(require 'guix-guile) (require 'guix-guile)
(require 'guix-utils) (require 'guix-utils)
(require 'guix-ui) (require 'guix-ui)
(require 'guix-history)
(require 'guix-messages)
;;; Parameters of the entries ;;; Parameters of the entries
@ -142,227 +137,6 @@ For the meaning of location, see `guix-find-location'."
'package-names-lists))) 'package-names-lists)))
#'string<)) #'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 ;;; Getting and displaying info about packages and generations
@ -384,159 +158,6 @@ information)."
(const :tag "Display outputs" output)) (const :tag "Display outputs" output))
:group 'guix) :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 ;;; Generations
@ -640,13 +261,14 @@ Create the buffer if needed."
(defun guix-profile-generation-manifest-file (generation) (defun guix-profile-generation-manifest-file (generation)
"Return the file name of a GENERATION's manifest. "Return the file name of a GENERATION's manifest.
GENERATION is a generation number of `guix-profile' profile." GENERATION is a generation number of the current profile."
(guix-manifest-file guix-profile generation)) (guix-manifest-file (guix-ui-current-profile) generation))
(defun guix-profile-generation-packages-buffer (generation) (defun guix-profile-generation-packages-buffer (generation)
"Insert GENERATION's package outputs in a buffer and return it. "Insert GENERATION's package outputs in a buffer and return it.
GENERATION is a generation number of `guix-profile' profile." GENERATION is a generation number of the current profile."
(guix-generation-packages-buffer guix-profile generation)) (guix-generation-packages-buffer (guix-ui-current-profile)
generation))
;;; Actions on packages and generations ;;; 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'. INSTALL, UPGRADE, REMOVE are 'package action specifications'.
See `guix-process-package-actions' for details." See `guix-process-package-actions' for details."
(or (null guix-operation-confirm) (or (null guix-operation-confirm)
(let* ((entries (guix-get-entries (let* ((entries (guix-ui-get-entries
profile 'package 'id profile 'package 'id
(append (mapcar #'car install) (append (mapcar #'car install)
(mapcar #'car upgrade) (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 Interactively, use the current profile and prompt for manifest
FILE. With a prefix argument, also prompt for PROFILE." FILE. With a prefix argument, also prompt for PROFILE."
(interactive (interactive
(let* ((default-profile (or guix-profile guix-current-profile)) (let* ((current-profile (guix-ui-current-profile))
(profile (if current-prefix-arg (profile (if current-prefix-arg
(guix-profile-prompt) (guix-profile-prompt)
default-profile)) (or current-profile guix-current-profile)))
(file (read-file-name "File with manifest: ")) (file (read-file-name "File with manifest: "))
(buffer (and guix-profile (current-buffer)))) (buffer (and current-profile (current-buffer))))
(list profile file buffer))) (list profile file buffer)))
(when (or (not guix-operation-confirm) (when (or (not guix-operation-confirm)
(y-or-n-p (format "Apply manifest from '%s' to profile '%s'? " (y-or-n-p (format "Apply manifest from '%s' to profile '%s'? "

566
emacs/guix-buffer.el Normal file
View file

@ -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

View file

@ -202,8 +202,7 @@ LEVEL is 1 by default."
(insert (guix-info-get-indent level))) (insert (guix-info-get-indent level)))
(defun guix-info-insert-entries (entries entry-type) (defun guix-info-insert-entries (entries entry-type)
"Display ENTRIES of ENTRY-TYPE in the current info buffer. "Display ENTRY-TYPE ENTRIES in the current info buffer."
ENTRIES should have a form of `guix-entries'."
(guix-mapinsert (lambda (entry) (guix-mapinsert (lambda (entry)
(guix-info-insert-entry entry entry-type)) (guix-info-insert-entry entry entry-type))
entries entries
@ -371,8 +370,11 @@ BUTTON-OR-FACE is a button type)."
'face 'guix-package-info-name-button 'face 'guix-package-info-name-button
'help-echo "Describe this package" 'help-echo "Describe this package"
'action (lambda (btn) 'action (lambda (btn)
(guix-get-show-entries guix-profile 'info guix-package-info-type (guix-buffer-get-display-entries-current
'name (button-label btn)))) 'info guix-package-info-type
(list (guix-ui-current-profile)
'name (button-label btn))
'add)))
(defun guix-info-button-copy-label (&optional pos) (defun guix-info-button-copy-label (&optional pos)
"Copy a label of the button at POS into kill ring. "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.") "Keymap for `guix-info-mode' buffers.")
(define-derived-mode guix-info-mode special-mode "Guix-Info" (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 () (defun guix-info-mode-initialize ()
"Set up the current 'info' buffer." "Set up the current 'info' buffer."
@ -435,7 +438,8 @@ The rest keyword arguments are passed to
(group (intern prefix)) (group (intern prefix))
(format-var (intern (concat prefix "-format")))) (format-var (intern (concat prefix "-format"))))
(guix-keyword-args-let args (guix-keyword-args-let args
((format-val :format)) ((show-entries-val :show-entries-function)
(format-val :format))
`(progn `(progn
(defcustom ,format-var ,format-val (defcustom ,format-var ,format-val
,(format "\ ,(format "\
@ -473,9 +477,23 @@ After calling each METHOD, a new line is inserted."
'((format . ,format-var)) '((format . ,format-var))
'guix-info-data ',entry-type) 'guix-info-data ',entry-type)
(guix-buffer-define-interface info ,entry-type ,(if show-entries-val
:mode-init-function 'guix-info-mode-initialize `(guix-buffer-define-interface info ,entry-type
,@%foreign-args))))) :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 ;;; Displaying packages
@ -675,7 +693,7 @@ ENTRY is an alist with package info."
type-str type-str
(lambda (btn) (lambda (btn)
(guix-process-package-actions (guix-process-package-actions
guix-profile (guix-ui-current-profile)
`((,(button-get btn 'action-type) (,(button-get btn 'id) `((,(button-get btn 'action-type) (,(button-get btn 'id)
,(button-get btn 'output)))) ,(button-get btn 'output))))
(current-buffer))) (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'). Find the file if needed (see `guix-package-info-auto-find-source').
ENTRY-ID is an ID of the current entry (package or output). ENTRY-ID is an ID of the current entry (package or output).
PACKAGE-ID is an ID of the package which source to show." PACKAGE-ID is an ID of the package which source to show."
(let* ((entries guix-entries) (let* ((entries (guix-buffer-current-entries))
(entry (guix-entry-by-id entry-id guix-entries)) (entry (guix-entry-by-id entry-id entries))
(file (guix-package-source-path package-id))) (file (guix-package-source-path package-id)))
(or file (or file
(error "Couldn't define file name of the package source")) (error "Couldn't define file name of the package source"))
(let* ((new-entry (cons (cons 'source-file file) (let* ((new-entry (cons (cons 'source-file file)
entry)) entry))
(new-entries (guix-replace-entry entry-id new-entry entries))) (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) (guix-buffer-redisplay-goto-button)
(if (file-exists-p file) (if (file-exists-p file)
(if guix-package-info-auto-find-source (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 (guix-info-insert-action-button
"Packages" "Packages"
(lambda (btn) (lambda (btn)
(guix-get-show-entries guix-profile 'list guix-package-list-type (guix-buffer-get-display-entries
'generation (button-get btn 'number))) 'list guix-package-list-type
(list (guix-ui-current-profile)
'generation (button-get btn 'number))
'add))
"Show installed packages for this generation" "Show installed packages for this generation"
'number number) 'number number)
(guix-info-insert-indent) (guix-info-insert-indent)
(guix-info-insert-action-button (guix-info-insert-action-button
"Delete" "Delete"
(lambda (btn) (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))) (current-buffer)))
"Delete this generation" "Delete this generation"
'number number)) 'number number))
@ -894,7 +917,8 @@ This function is used to hide a \"Download\" button if needed."
(guix-info-insert-action-button (guix-info-insert-action-button
"Switch" "Switch"
(lambda (btn) (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))) (current-buffer)))
"Switch to this generation (make it the current one)" "Switch to this generation (make it the current one)"
'number (guix-entry-value entry 'number)))) 'number (guix-entry-value entry 'number))))

View file

@ -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) (let* ((ids (or (apply #'guix-list-get-marked-id-list mark-names)
(list (guix-list-current-id)))) (list (guix-list-current-id))))
(count (length ids)) (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)) (when (or (<= count (guix-list-describe-warning-count entry-type))
(y-or-n-p (format "Do you really want to describe %d entries? " (y-or-n-p (format "Do you really want to describe %d entries? "
count))) count)))
@ -168,8 +168,7 @@ Return a vector made of values of FUN calls."
rest-spec)))) rest-spec))))
(defun guix-list-insert-entries (entries entry-type) (defun guix-list-insert-entries (entries entry-type)
"Display ENTRIES of ENTRY-TYPE in the current list buffer. "Print ENTRY-TYPE ENTRIES in the current buffer."
ENTRIES should have a form of `guix-entries'."
(setq tabulated-list-entries (setq tabulated-list-entries
(guix-list-tabulated-entries entries entry-type)) (guix-list-tabulated-entries entries entry-type))
(tabulated-list-print)) (tabulated-list-print))
@ -212,14 +211,18 @@ VAL may be nil."
'follow-link t 'follow-link t
'help-echo "Find file")) 'help-echo "Find file"))
;;; 'List' lines
(defun guix-list-current-id () (defun guix-list-current-id ()
"Return ID of the current entry." "Return ID of the entry at point."
(or (tabulated-list-get-id) (or (tabulated-list-get-id)
(user-error "No entry here"))) (user-error "No entry here")))
(defun guix-list-current-entry () (defun guix-list-current-entry ()
"Return alist of the current entry info." "Return entry at point."
(guix-entry-by-id (guix-list-current-id) guix-entries)) (guix-entry-by-id (guix-list-current-id)
(guix-buffer-current-entries)))
(defun guix-list-for-each-line (fun &rest args) (defun guix-list-for-each-line (fun &rest args)
"Call FUN with ARGS for each entry line." "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)) (let* ((entry-type-str (symbol-name entry-type))
(prefix (concat "guix-" entry-type-str "-list")) (prefix (concat "guix-" entry-type-str "-list"))
(group (intern prefix)) (group (intern prefix))
(mode-str (concat prefix "-mode"))
(init-fun (intern (concat prefix "-mode-initialize")))
(describe-var (intern (concat prefix "-describe-function"))) (describe-var (intern (concat prefix "-describe-function")))
(describe-count-var (intern (concat prefix (describe-count-var (intern (concat prefix
"-describe-warning-count"))) "-describe-warning-count")))
@ -438,7 +439,8 @@ The rest keyword arguments are passed to
(sort-key-var (intern (concat prefix "-sort-key"))) (sort-key-var (intern (concat prefix "-sort-key")))
(marks-var (intern (concat prefix "-marks")))) (marks-var (intern (concat prefix "-marks"))))
(guix-keyword-args-let args (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) (describe-count-val :describe-count 10)
(format-val :format) (format-val :format)
(sort-key-val :sort-key) (sort-key-val :sort-key)
@ -498,10 +500,6 @@ See also `guix-list-describe'."
,(format "Function used to describe '%s' entries." ,(format "Function used to describe '%s' entries."
entry-type-str)) entry-type-str))
(defun ,init-fun ()
,(concat "Initial settings for `" mode-str "'.")
(guix-list-mode-initialize ',entry-type))
(guix-alist-put! (guix-alist-put!
'((describe . ,describe-var) '((describe . ,describe-var)
(describe-count . ,describe-count-var) (describe-count . ,describe-count-var)
@ -510,8 +508,30 @@ See also `guix-list-describe'."
(marks . ,marks-var)) (marks . ,marks-var))
'guix-list-data ',entry-type) 'guix-list-data ',entry-type)
(guix-buffer-define-interface list ,entry-type ,(if show-entries-val
,@%foreign-args))))) `(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 ;;; Displaying packages
@ -584,7 +604,7 @@ Colorize it with `guix-package-list-installed' or
(when (and (not guix-package-list-generation-marking-enabled) (when (and (not guix-package-list-generation-marking-enabled)
(or (derived-mode-p 'guix-package-list-mode) (or (derived-mode-p 'guix-package-list-mode)
(derived-mode-p 'guix-output-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'"))) (error "Action marks are disabled for lists of 'generation packages'")))
(defun guix-package-list-mark-outputs (mark default (defun guix-package-list-mark-outputs (mark default
@ -655,7 +675,7 @@ accept an entry as argument."
(let ((obsolete (cl-remove-if-not (let ((obsolete (cl-remove-if-not
(lambda (entry) (lambda (entry)
(guix-entry-value entry 'obsolete)) (guix-entry-value entry 'obsolete))
guix-entries))) (guix-buffer-current-entries))))
(guix-list-for-each-line (guix-list-for-each-line
(lambda () (lambda ()
(let* ((id (guix-list-current-id)) (let* ((id (guix-list-current-id))
@ -682,8 +702,8 @@ FUN should accept action-type as argument."
(let ((actions (delq nil (let ((actions (delq nil
(mapcar fun '(install delete upgrade))))) (mapcar fun '(install delete upgrade)))))
(if actions (if actions
(guix-process-package-actions (guix-process-package-actions (guix-ui-current-profile)
guix-profile actions (current-buffer)) actions (current-buffer))
(user-error "No operations specified")))) (user-error "No operations specified"))))
(defun guix-package-list-execute () (defun guix-package-list-execute ()
@ -714,7 +734,7 @@ The specification is suitable for `guix-process-package-actions'."
(output nil 9 t) (output nil 9 t)
(installed nil 12 t) (installed nil 12 t)
(synopsis guix-list-get-one-line 30 nil)) (synopsis guix-list-get-one-line 30 nil))
:required '(package-id) :required '(id package-id)
:sort-key '(name) :sort-key '(name)
:marks '((install . ?I) :marks '((install . ?I)
(upgrade . ?U) (upgrade . ?U)
@ -784,15 +804,19 @@ The specification is suitable for `guix-process-output-actions'."
"Describe outputs with IDS (list of output identifiers). "Describe outputs with IDS (list of output identifiers).
See `guix-package-info-type'." See `guix-package-info-type'."
(if (eq guix-package-info-type 'output) (if (eq guix-package-info-type 'output)
(apply #'guix-get-show-entries (guix-buffer-get-display-entries
guix-profile 'info 'output 'id ids) 'info 'output
(cl-list* (guix-ui-current-profile) 'id ids)
'add)
(let ((pids (mapcar (lambda (oid) (let ((pids (mapcar (lambda (oid)
(car (guix-package-id-and-output-by-output-id (car (guix-package-id-and-output-by-output-id
oid))) oid)))
ids))) ids)))
(apply #'guix-get-show-entries (guix-buffer-get-display-entries
guix-profile 'info 'package 'id 'info 'package
(cl-remove-duplicates pids))))) (cl-list* (guix-ui-current-profile)
'id (cl-remove-duplicates pids))
'add))))
(defun guix-output-list-edit () (defun guix-output-list-edit ()
"Go to the location of the current package." "Go to the location of the current package."
@ -837,13 +861,15 @@ VAL is a boolean value."
(number (guix-entry-value entry 'number))) (number (guix-entry-value entry 'number)))
(if current (if current
(user-error "This generation is already the current one") (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 () (defun guix-generation-list-show-packages ()
"List installed packages for the generation at point." "List installed packages for the generation at point."
(interactive) (interactive)
(guix-get-show-entries guix-profile 'list guix-package-list-type (guix-get-show-packages
'generation (guix-list-current-id))) (guix-ui-current-profile)
'generation (guix-list-current-id)))
(defun guix-generation-list-generations-to-compare () (defun guix-generation-list-generations-to-compare ()
"Return a sorted list of 2 marked generations for comparing." "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 outputs installed in the latest marked generation that were not
installed in the other one." installed in the other one."
(interactive) (interactive)
(apply #'guix-get-show-entries (guix-buffer-get-display-entries
guix-profile 'list 'output 'generation-diff 'list 'output
(reverse (guix-generation-list-generations-to-compare)))) (cl-list* (guix-ui-current-profile)
'generation-diff
(reverse (guix-generation-list-generations-to-compare)))
'add))
(defun guix-generation-list-show-removed-packages () (defun guix-generation-list-show-removed-packages ()
"List package outputs removed from the latest marked generation. "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 outputs not installed in the latest marked generation that were
installed in the other one." installed in the other one."
(interactive) (interactive)
(apply #'guix-get-show-entries (guix-buffer-get-display-entries
guix-profile 'list 'output 'generation-diff 'list 'output
(guix-generation-list-generations-to-compare))) (cl-list* (guix-ui-current-profile)
'generation-diff
(guix-generation-list-generations-to-compare))
'add))
(defun guix-generation-list-compare (diff-fun gen-fun) (defun guix-generation-list-compare (diff-fun gen-fun)
"Run GEN-FUN on the 2 marked generations and run DIFF-FUN on the results." "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))) (let ((marked (guix-list-get-marked-id-list 'delete)))
(or marked (or marked
(user-error "No generations marked for deletion")) (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 (defvar guix-list-font-lock-keywords

View file

@ -26,7 +26,10 @@
(require 'cl-lib) (require 'cl-lib)
(require 'guix-backend) (require 'guix-backend)
(require 'guix-buffer)
(require 'guix-guile)
(require 'guix-utils) (require 'guix-utils)
(require 'guix-messages)
(defgroup guix-ui nil (defgroup guix-ui nil
"Settings for Guix package management. "Settings for Guix package management.
@ -41,10 +44,38 @@ generations in 'list' and 'info' buffers."
map) map)
"Parent keymap for Guix package/generation buffers.") "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) (defun guix-ui-list-describe (ids)
"Describe 'ui' entries with IDS (list of identifiers)." "Describe 'ui' entries with IDS (list of identifiers)."
(apply #'guix-get-show-entries (guix-buffer-get-display-entries
guix-profile 'info guix-entry-type 'id ids)) 'info (guix-buffer-current-entry-type)
(cl-list* (guix-ui-current-profile) 'id ids)
'add))
;;; Buffers and auto updating ;;; Buffers and auto updating
@ -161,7 +192,16 @@ Optional keywords:
`guix-TYPE-required-params' variable. `guix-TYPE-required-params' variable.
The rest keyword arguments are passed to 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)) (declare (indent 2))
(let* ((entry-type-str (symbol-name entry-type)) (let* ((entry-type-str (symbol-name entry-type))
(buffer-type-str (symbol-name buffer-type)) (buffer-type-str (symbol-name buffer-type))
@ -173,6 +213,10 @@ The rest keyword arguments are passed to
buffer-type-str))) buffer-type-str)))
(required-var (intern (concat prefix "-required-params"))) (required-var (intern (concat prefix "-required-params")))
(buffer-name-fun (intern (concat prefix "-buffer-name"))) (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" (definer (intern (format "guix-%s-define-interface"
buffer-type-str)))) buffer-type-str))))
(guix-keyword-args-let args (guix-keyword-args-let args
@ -188,9 +232,13 @@ The rest keyword arguments are passed to
(defvar ,required-var ,required-val (defvar ,required-var ,required-val
,(format "\ ,(format "\
List of the required '%s' parameters for '%s' buffer. List of the required '%s' parameters.
These parameters are received along with the displayed parameters." These parameters are received by `%S'
entry-type-str buffer-type-str)) 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 _) (defun ,buffer-name-fun (profile &rest _)
,(format "\ ,(format "\
@ -199,7 +247,27 @@ See `guix-ui-buffer-name' for details."
buffer-type-str entry-type-str) buffer-type-str entry-type-str)
(guix-ui-buffer-name ,buffer-name-val profile)) (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 (,definer ,entry-type
:get-entries-function ',get-fun
:message-function ',message-fun
:buffer-name ',buffer-name-fun :buffer-name ',buffer-name-fun
,@%foreign-args))))) ,@%foreign-args)))))

View file

@ -56,42 +56,39 @@ If nil, show a single package in the info buffer."
(defvar guix-search-history nil (defvar guix-search-history nil
"A history of minibuffer prompts.") "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. "Search for packages and show results.
If PROFILE is nil, use `guix-current-profile'. If PROFILE is nil, use `guix-current-profile'.
See `guix-get-entries' for the meaning of SEARCH-TYPE and See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and
SEARCH-VALS. SEARCH-VALUES.
Results are displayed in the list buffer, unless a single package Results are displayed in the list buffer, unless a single package
is found and `guix-list-single-package' is nil." is found and `guix-list-single-package' is nil."
(or profile (setq profile guix-current-profile)) (let* ((args (cl-list* (or profile guix-current-profile)
(let ((packages (guix-get-entries profile guix-package-list-type search-type search-values))
search-type search-vals (entries (guix-buffer-get-entries
(guix-get-params-for-receiving 'list guix-package-list-type args)))
'list guix-package-list-type))))
(if (or guix-list-single-package (if (or guix-list-single-package
(cdr packages)) (null entries)
(guix-set-buffer profile packages 'list guix-package-list-type (cdr entries))
search-type search-vals) (guix-buffer-display-entries
(let ((packages (guix-get-entries profile guix-package-info-type entries 'list guix-package-list-type args 'add)
search-type search-vals (guix-buffer-get-display-entries
(guix-get-params-for-receiving 'info guix-package-info-type args 'add))))
'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) (defun guix-get-show-generations (profile search-type &rest search-values)
"Search for generations and show results. "Search for generations and show results.
If PROFILE is nil, use `guix-current-profile'. If PROFILE is nil, use `guix-current-profile'.
See `guix-get-entries' for the meaning of SEARCH-TYPE and See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and
SEARCH-VALS." SEARCH-VALUES."
(apply #'guix-get-show-entries (let ((args (cl-list* (or profile guix-current-profile)
(or profile guix-current-profile) search-type search-values)))
'list 'generation search-type search-vals)) (guix-buffer-get-display-entries
'list 'generation args 'add)))
;;;###autoload ;;;###autoload
(defun guix-search-by-name (name &optional profile) (defun guix-search-by-name (name &optional profile)