1 ;;; guix-ui.el --- Common code for Guix package management interface -*- lexical-binding: t -*-
3 ;; Copyright © 2014, 2015, 2016 Alex Kost <alezost@gmail.com>
5 ;; This file is part of GNU Guix.
7 ;; GNU Guix is free software; you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, either version 3 of the License, or
10 ;; (at your option) any later version.
12 ;; GNU Guix is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
22 ;; This file provides some general code for 'list'/'info' interfaces for
23 ;; packages and generations.
28 (require 'guix-backend
)
29 (require 'guix-buffer
)
32 (require 'guix-messages
)
33 (require 'guix-profiles
)
35 (guix-define-groups ui
37 Settings for 'ui' (Guix package management) buffers.
38 This group includes settings for displaying packages, outputs and
39 generations in 'list' and 'info' buffers.")
42 (let ((map (make-sparse-keymap)))
43 (define-key map
(kbd "M") 'guix-apply-manifest
)
44 (define-key map
(kbd "C-c C-z") 'guix-switch-to-repl
)
46 "Parent keymap for Guix package/generation buffers.")
48 (guix-buffer-define-current-args-accessors
49 "guix-ui-current" "profile" "search-type" "search-values")
51 (defun guix-ui-read-profile ()
52 "Return `guix-current-profile' or prompt for it.
53 This function is intended for using in `interactive' forms."
54 (if current-prefix-arg
56 guix-current-profile
))
58 (defun guix-ui-get-entries (profile entry-type search-type search-values
60 "Receive ENTRY-TYPE entries for PROFILE.
61 Call an appropriate scheme procedure and return a list of entries.
63 ENTRY-TYPE should be one of the following symbols: `package',
64 `output' or `generation'.
66 SEARCH-TYPE may be one of the following symbols:
68 - If ENTRY-TYPE is `package' or `output': `id', `name', `regexp',
69 `all-available', `newest-available', `installed', `obsolete',
72 - If ENTRY-TYPE is `generation': `id', `last', `all', `time'.
74 PARAMS is a list of parameters for receiving. If nil, get data
75 with all available parameters."
77 (guix-make-guile-expression
79 profile params entry-type search-type search-values
)))
81 (defun guix-ui-list-describe (ids)
82 "Describe 'ui' entries with IDS (list of identifiers)."
83 (guix-buffer-get-display-entries
84 'info
(guix-buffer-current-entry-type)
85 (cl-list* (guix-ui-current-profile) 'id ids
)
89 ;;; Buffers and auto updating
91 (defcustom guix-ui-update-after-operation
'current
92 "Define what kind of data to update after executing an operation.
94 After successful executing an operation in the Guix REPL (for
95 example after installing a package), the data in Guix buffers
96 will or will not be automatically updated depending on a value of
99 If nil, update nothing (do not revert any buffer).
100 If `current', update the buffer from which an operation was performed.
101 If `all', update all Guix buffers (not recommended)."
102 :type
'(choice (const :tag
"Do nothing" nil
)
103 (const :tag
"Update operation buffer" current
)
104 (const :tag
"Update all Guix buffers" all
))
107 (defcustom guix-ui-buffer-name-function
108 #'guix-ui-buffer-name-full
109 "Function used to define a name of a Guix buffer.
110 The function is called with 2 arguments: BASE-NAME and PROFILE."
111 :type
'(choice (function-item guix-ui-buffer-name-full
)
112 (function-item guix-ui-buffer-name-short
)
113 (function-item guix-ui-buffer-name-simple
)
114 (function :tag
"Other function"))
117 (defun guix-ui-buffer-name-simple (base-name &rest _
)
121 (defun guix-ui-buffer-name-short (base-name profile
)
122 "Return buffer name by appending BASE-NAME and PROFILE's base file name."
123 (guix-compose-buffer-name base-name
124 (file-name-base (directory-file-name profile
))))
126 (defun guix-ui-buffer-name-full (base-name profile
)
127 "Return buffer name by appending BASE-NAME and PROFILE's full name."
128 (guix-compose-buffer-name base-name profile
))
130 (defun guix-ui-buffer-name (base-name profile
)
131 "Return Guix buffer name based on BASE-NAME and profile.
132 See `guix-ui-buffer-name-function' for details."
133 (funcall guix-ui-buffer-name-function
136 (defun guix-ui-buffer?
(&optional buffer modes
)
137 "Return non-nil if BUFFER mode is derived from any of the MODES.
138 If BUFFER is nil, check current buffer.
139 If MODES is nil, use `guix-list-mode' and `guix-info-mode'."
140 (with-current-buffer (or buffer
(current-buffer))
141 (apply #'derived-mode-p
142 (or modes
'(guix-list-mode guix-info-mode
)))))
144 (defun guix-ui-buffers (&optional modes
)
145 "Return a list of all buffers with major modes derived from MODES.
146 If MODES is nil, return list of all Guix 'list' and 'info' buffers."
147 (cl-remove-if-not (lambda (buf)
148 (guix-ui-buffer? buf modes
))
151 (defun guix-ui-update-buffer (buffer)
152 "Update data in a 'list' or 'info' BUFFER."
153 (with-current-buffer buffer
154 (guix-buffer-revert nil t
)))
156 (defun guix-ui-update-buffers-after-operation ()
157 "Update buffers after Guix operation if needed.
158 See `guix-ui-update-after-operation' for details."
160 (and guix-operation-buffer
161 (cl-case guix-ui-update-after-operation
162 (current (and (buffer-live-p guix-operation-buffer
)
163 (guix-ui-buffer? guix-operation-buffer
)
164 (list guix-operation-buffer
)))
165 (all (guix-ui-buffers))))))
166 (setq guix-operation-buffer nil
)
167 (mapc #'guix-ui-update-buffer to-update
)))
169 (add-hook 'guix-after-repl-operation-hook
170 'guix-ui-update-buffers-after-operation
)
173 ;;; Interface definers
175 (defmacro guix-ui-define-entry-type
(entry-type &rest args
)
176 "Define general code for ENTRY-TYPE.
177 Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ...
179 The rest keyword arguments are passed to
180 `guix-define-entry-type' macro."
182 `(guix-define-entry-type ,entry-type
183 :parent-group guix-ui
184 :parent-faces-group guix-ui-faces
187 (defmacro guix-ui-define-interface
(buffer-type entry-type
&rest args
)
188 "Define BUFFER-TYPE interface for displaying ENTRY-TYPE entries.
189 Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ...
190 In the following description TYPE means ENTRY-TYPE-BUFFER-TYPE.
194 - `:buffer-name' - base part of a buffer name. It is used in a
195 generated `guix-TYPE-buffer-name' function; see
196 `guix-ui-buffer-name' for details.
200 - `:required' - default value of the generated
201 `guix-TYPE-required-params' variable.
203 The rest keyword arguments are passed to
204 `guix-BUFFER-TYPE-define-interface' macro.
206 Along with the mentioned definitions, this macro also defines:
208 - `guix-TYPE-mode-map' - keymap based on `guix-ui-map' and
209 `guix-BUFFER-TYPE-mode-map'.
211 - `guix-TYPE-get-entries' - a wrapper around `guix-ui-get-entries'.
213 - `guix-TYPE-message' - a wrapper around `guix-result-message'."
215 (let* ((entry-type-str (symbol-name entry-type
))
216 (buffer-type-str (symbol-name buffer-type
))
217 (prefix (concat "guix-" entry-type-str
"-"
219 (mode-str (concat prefix
"-mode"))
220 (mode-map (intern (concat mode-str
"-map")))
221 (parent-map (intern (format "guix-%s-mode-map"
223 (required-var (intern (concat prefix
"-required-params")))
224 (buffer-name-fun (intern (concat prefix
"-buffer-name")))
225 (get-fun (intern (concat prefix
"-get-entries")))
226 (message-fun (intern (concat prefix
"-message")))
227 (displayed-fun (intern (format "guix-%s-displayed-params"
229 (definer (intern (format "guix-%s-define-interface"
231 (guix-keyword-args-let args
232 ((buffer-name-val :buffer-name
)
233 (required-val :required
''(id)))
236 (let ((map (make-sparse-keymap)))
238 map
(make-composed-keymap ,parent-map guix-ui-map
))
240 ,(format "Keymap for `%s' buffers." mode-str
))
242 (defvar ,required-var
,required-val
244 List of the required '%s' parameters.
245 These parameters are received by `%S'
246 along with the displayed parameters.
248 Do not remove `id' from this list as it is required for
249 identifying an entry."
250 entry-type-str get-fun
))
252 (defun ,buffer-name-fun
(profile &rest _
)
254 Return a name of '%s' buffer for displaying '%s' entries.
255 See `guix-ui-buffer-name' for details."
256 buffer-type-str entry-type-str
)
257 (guix-ui-buffer-name ,buffer-name-val profile
))
259 (defun ,get-fun
(profile search-type
&rest search-values
)
261 Receive '%s' entries for displaying them in '%s' buffer.
262 See `guix-ui-get-entries' for details."
263 entry-type-str buffer-type-str
)
265 profile
',entry-type search-type search-values
266 (cl-union ,required-var
267 (,displayed-fun
',entry-type
))))
269 (defun ,message-fun
(entries profile search-type
272 Display a message after showing '%s' entries."
275 profile entries
',entry-type search-type search-values
))
277 (,definer
,entry-type
278 :get-entries-function
',get-fun
279 :message-function
',message-fun
280 :buffer-name
',buffer-name-fun
283 (defmacro guix-ui-info-define-interface
(entry-type &rest args
)
284 "Define 'info' interface for displaying ENTRY-TYPE entries.
285 See `guix-ui-define-interface'."
287 `(guix-ui-define-interface info
,entry-type
290 (defmacro guix-ui-list-define-interface
(entry-type &rest args
)
291 "Define 'list' interface for displaying ENTRY-TYPE entries.
292 Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ...
296 - `:describe-function' - default value of the generated
297 `guix-ENTRY-TYPE-list-describe-function' variable (if not
298 specified, use `guix-ui-list-describe').
300 The rest keyword arguments are passed to
301 `guix-ui-define-interface' macro."
303 (guix-keyword-args-let args
304 ((describe-val :describe-function
))
305 `(guix-ui-define-interface list
,entry-type
306 :describe-function
,(or describe-val
''guix-ui-list-describe
)
310 (defvar guix-ui-font-lock-keywords
312 `((,(rx "(" (group (or "guix-ui-define-entry-type"
313 "guix-ui-define-interface"
314 "guix-ui-info-define-interface"
315 "guix-ui-list-define-interface"))
319 (font-lock-add-keywords 'emacs-lisp-mode guix-ui-font-lock-keywords
)
323 ;;; guix-ui.el ends here