Commit | Line | Data |
---|---|---|
c80ce104 AK |
1 | ;;; guix-ui-package.el --- Interface for displaying packages -*- lexical-binding: t -*- |
2 | ||
3 | ;; Copyright © 2014, 2015 Alex Kost <alezost@gmail.com> | |
4 | ||
5 | ;; This file is part of GNU Guix. | |
6 | ||
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. | |
11 | ||
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. | |
16 | ||
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/>. | |
19 | ||
20 | ;;; Commentary: | |
21 | ||
22 | ;; This file provides an interface for displaying packages and outputs | |
23 | ;; in 'list' and 'info' buffers, and commands for working with them. | |
24 | ||
25 | ;;; Code: | |
26 | ||
27 | (require 'cl-lib) | |
28 | (require 'guix-buffer) | |
29 | (require 'guix-list) | |
30 | (require 'guix-info) | |
31 | (require 'guix-ui) | |
32 | (require 'guix-base) | |
33 | (require 'guix-backend) | |
34 | (require 'guix-guile) | |
35 | (require 'guix-entry) | |
36 | (require 'guix-utils) | |
b8fa5a2a | 37 | (require 'guix-hydra) |
5c8994d9 | 38 | (require 'guix-hydra-build) |
83aab70b | 39 | (require 'guix-read) |
cefb7aea | 40 | (require 'guix-license) |
e20f051e | 41 | (require 'guix-profiles) |
c80ce104 | 42 | |
8ed2c92e AK |
43 | (guix-ui-define-entry-type package) |
44 | (guix-ui-define-entry-type output) | |
c80ce104 AK |
45 | |
46 | (defcustom guix-package-list-type 'output | |
47 | "Define how to display packages in 'list' buffer. | |
48 | Should be a symbol `package' or `output' (if `output', display each | |
49 | output on a separate line; if `package', display each package on | |
50 | a separate line)." | |
51 | :type '(choice (const :tag "List of packages" package) | |
52 | (const :tag "List of outputs" output)) | |
53 | :group 'guix-package) | |
54 | ||
55 | (defcustom guix-package-info-type 'package | |
56 | "Define how to display packages in 'info' buffer. | |
57 | Should be a symbol `package' or `output' (if `output', display | |
58 | each output separately; if `package', display outputs inside | |
59 | package data)." | |
60 | :type '(choice (const :tag "Display packages" package) | |
61 | (const :tag "Display outputs" output)) | |
62 | :group 'guix-package) | |
63 | ||
c80ce104 AK |
64 | (defun guix-package-get-display (profile search-type &rest search-values) |
65 | "Search for packages/outputs and show results. | |
66 | ||
67 | If PROFILE is nil, use `guix-current-profile'. | |
68 | ||
69 | See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and | |
70 | SEARCH-VALUES. | |
71 | ||
72 | Results are displayed in the list buffer, unless a single package | |
73 | is found and `guix-package-list-single' is nil." | |
74 | (let* ((args (cl-list* (or profile guix-current-profile) | |
75 | search-type search-values)) | |
76 | (entries (guix-buffer-get-entries | |
77 | 'list guix-package-list-type args))) | |
78 | (if (or guix-package-list-single | |
79 | (null entries) | |
80 | (cdr entries)) | |
81 | (guix-buffer-display-entries | |
82 | entries 'list guix-package-list-type args 'add) | |
83 | (guix-buffer-get-display-entries | |
84 | 'info guix-package-info-type args 'add)))) | |
85 | ||
86 | (defun guix-package-entry->name-specification (entry &optional output) | |
87 | "Return name specification of the package ENTRY and OUTPUT." | |
88 | (guix-package-name-specification | |
89 | (guix-entry-value entry 'name) | |
90 | (guix-entry-value entry 'version) | |
91 | (or output (guix-entry-value entry 'output)))) | |
92 | ||
93 | (defun guix-package-entries->name-specifications (entries) | |
94 | "Return name specifications by the package or output ENTRIES." | |
95 | (cl-remove-duplicates (mapcar #'guix-package-entry->name-specification | |
96 | entries) | |
97 | :test #'string=)) | |
98 | ||
99 | (defun guix-package-installed-outputs (entry) | |
100 | "Return a list of installed outputs for the package ENTRY." | |
101 | (mapcar (lambda (installed-entry) | |
102 | (guix-entry-value installed-entry 'output)) | |
103 | (guix-entry-value entry 'installed))) | |
104 | ||
105 | (defun guix-package-id-and-output-by-output-id (output-id) | |
106 | "Return a list (PACKAGE-ID OUTPUT) by OUTPUT-ID." | |
107 | (cl-multiple-value-bind (package-id-str output) | |
108 | (split-string output-id ":") | |
109 | (let ((package-id (string-to-number package-id-str))) | |
110 | (list (if (= 0 package-id) package-id-str package-id) | |
111 | output)))) | |
112 | ||
113 | \f | |
114 | ;;; Processing package actions | |
115 | ||
116 | (defun guix-process-package-actions (profile actions | |
117 | &optional operation-buffer) | |
118 | "Process package ACTIONS on PROFILE. | |
119 | Each action is a list of the form: | |
120 | ||
121 | (ACTION-TYPE PACKAGE-SPEC ...) | |
122 | ||
123 | ACTION-TYPE is one of the following symbols: `install', | |
124 | `upgrade', `remove'/`delete'. | |
125 | PACKAGE-SPEC should have the following form: (ID [OUTPUT] ...)." | |
126 | (let (install upgrade remove) | |
127 | (mapc (lambda (action) | |
128 | (let ((action-type (car action)) | |
129 | (specs (cdr action))) | |
130 | (cl-case action-type | |
131 | (install (setq install (append install specs))) | |
132 | (upgrade (setq upgrade (append upgrade specs))) | |
133 | ((remove delete) (setq remove (append remove specs)))))) | |
134 | actions) | |
135 | (when (guix-continue-package-operation-p | |
136 | profile | |
137 | :install install :upgrade upgrade :remove remove) | |
138 | (guix-eval-in-repl | |
139 | (guix-make-guile-expression | |
140 | 'process-package-actions profile | |
141 | :install install :upgrade upgrade :remove remove | |
142 | :use-substitutes? (or guix-use-substitutes 'f) | |
143 | :dry-run? (or guix-dry-run 'f)) | |
144 | (and (not guix-dry-run) operation-buffer))))) | |
145 | ||
146 | (cl-defun guix-continue-package-operation-p (profile | |
147 | &key install upgrade remove) | |
148 | "Return non-nil if a package operation should be continued. | |
149 | Ask a user if needed (see `guix-operation-confirm'). | |
150 | INSTALL, UPGRADE, REMOVE are 'package action specifications'. | |
151 | See `guix-process-package-actions' for details." | |
152 | (or (null guix-operation-confirm) | |
153 | (let* ((entries (guix-ui-get-entries | |
154 | profile 'package 'id | |
155 | (append (mapcar #'car install) | |
156 | (mapcar #'car upgrade) | |
157 | (mapcar #'car remove)) | |
158 | '(id name version location))) | |
159 | (install-strings (guix-get-package-strings install entries)) | |
160 | (upgrade-strings (guix-get-package-strings upgrade entries)) | |
161 | (remove-strings (guix-get-package-strings remove entries))) | |
162 | (if (or install-strings upgrade-strings remove-strings) | |
163 | (let ((buf (get-buffer-create guix-temp-buffer-name))) | |
164 | (with-current-buffer buf | |
165 | (setq-local cursor-type nil) | |
166 | (setq buffer-read-only nil) | |
167 | (erase-buffer) | |
168 | (insert "Profile: " profile "\n\n") | |
169 | (guix-insert-package-strings install-strings "install") | |
170 | (guix-insert-package-strings upgrade-strings "upgrade") | |
171 | (guix-insert-package-strings remove-strings "remove") | |
172 | (let ((win (temp-buffer-window-show | |
173 | buf | |
174 | '((display-buffer-reuse-window | |
175 | display-buffer-at-bottom) | |
176 | (window-height . fit-window-to-buffer))))) | |
177 | (prog1 (guix-operation-prompt) | |
178 | (quit-window nil win))))) | |
179 | (message "Nothing to be done. | |
180 | If Guix REPL was restarted, the data is not up-to-date.") | |
181 | nil)))) | |
182 | ||
183 | (defun guix-get-package-strings (specs entries) | |
184 | "Return short package descriptions for performing package actions. | |
185 | See `guix-process-package-actions' for the meaning of SPECS. | |
186 | ENTRIES is a list of package entries to get info about packages." | |
187 | (delq nil | |
188 | (mapcar | |
189 | (lambda (spec) | |
190 | (let* ((id (car spec)) | |
191 | (outputs (cdr spec)) | |
192 | (entry (guix-entry-by-id id entries))) | |
193 | (when entry | |
194 | (let ((location (guix-entry-value entry 'location))) | |
195 | (concat (guix-package-entry->name-specification entry) | |
196 | (when outputs | |
197 | (concat ":" | |
198 | (guix-concat-strings outputs ","))) | |
199 | (when location | |
200 | (concat "\t(" location ")"))))))) | |
201 | specs))) | |
202 | ||
203 | (defun guix-insert-package-strings (strings action) | |
204 | "Insert information STRINGS at point for performing package ACTION." | |
205 | (when strings | |
206 | (insert "Package(s) to " (propertize action 'face 'bold) ":\n") | |
207 | (mapc (lambda (str) | |
208 | (insert " " str "\n")) | |
209 | strings) | |
210 | (insert "\n"))) | |
211 | ||
212 | \f | |
213 | ;;; Package 'info' | |
214 | ||
215 | (guix-ui-info-define-interface package | |
216 | :buffer-name "*Guix Package Info*" | |
217 | :format '(guix-package-info-insert-heading | |
218 | ignore | |
219 | (synopsis ignore (simple guix-package-info-synopsis)) | |
220 | ignore | |
221 | (description ignore (simple guix-package-info-description)) | |
222 | ignore | |
223 | (outputs simple guix-package-info-insert-outputs) | |
224 | (source simple guix-package-info-insert-source) | |
225 | (location format (format guix-package-location)) | |
226 | (home-url format (format guix-url)) | |
cefb7aea | 227 | (license format (format guix-package-license)) |
0a5ec709 | 228 | (systems format guix-package-info-insert-systems) |
c80ce104 AK |
229 | (inputs format (format guix-package-input)) |
230 | (native-inputs format (format guix-package-native-input)) | |
231 | (propagated-inputs format | |
232 | (format guix-package-propagated-input))) | |
0a5ec709 AK |
233 | :titles '((home-url . "Home page") |
234 | (systems . "Supported systems")) | |
c80ce104 AK |
235 | :required '(id name version installed non-unique)) |
236 | ||
237 | (guix-info-define-interface installed-output | |
238 | :format '((path simple (indent guix-file)) | |
239 | (dependencies simple (indent guix-file))) | |
240 | :titles '((path . "Store directory")) | |
241 | :reduced? t) | |
242 | ||
243 | (defface guix-package-info-heading | |
244 | '((t :inherit guix-info-heading)) | |
245 | "Face for package name and version headings." | |
246 | :group 'guix-package-info-faces) | |
247 | ||
248 | (defface guix-package-info-name | |
249 | '((t :inherit font-lock-keyword-face)) | |
250 | "Face used for a name of a package." | |
251 | :group 'guix-package-info-faces) | |
252 | ||
253 | (defface guix-package-info-name-button | |
254 | '((t :inherit button)) | |
255 | "Face used for a full name that can be used to describe a package." | |
256 | :group 'guix-package-info-faces) | |
257 | ||
258 | (defface guix-package-info-version | |
259 | '((t :inherit font-lock-builtin-face)) | |
260 | "Face used for a version of a package." | |
261 | :group 'guix-package-info-faces) | |
262 | ||
263 | (defface guix-package-info-synopsis | |
264 | '((((type tty pc) (class color)) :weight bold) | |
265 | (t :height 1.1 :weight bold :inherit variable-pitch)) | |
266 | "Face used for a synopsis of a package." | |
267 | :group 'guix-package-info-faces) | |
268 | ||
269 | (defface guix-package-info-description | |
270 | '((t)) | |
271 | "Face used for a description of a package." | |
272 | :group 'guix-package-info-faces) | |
273 | ||
274 | (defface guix-package-info-license | |
275 | '((t :inherit font-lock-string-face)) | |
276 | "Face used for a license of a package." | |
277 | :group 'guix-package-info-faces) | |
278 | ||
279 | (defface guix-package-info-location | |
280 | '((t :inherit link)) | |
281 | "Face used for a location of a package." | |
282 | :group 'guix-package-info-faces) | |
283 | ||
284 | (defface guix-package-info-source | |
285 | '((t :inherit link :underline nil)) | |
286 | "Face used for a source URL of a package." | |
287 | :group 'guix-package-info-faces) | |
288 | ||
289 | (defface guix-package-info-installed-outputs | |
290 | '((default :weight bold) | |
291 | (((class color) (min-colors 88) (background light)) | |
292 | :foreground "ForestGreen") | |
293 | (((class color) (min-colors 88) (background dark)) | |
294 | :foreground "PaleGreen") | |
295 | (((class color) (min-colors 8)) | |
296 | :foreground "green") | |
297 | (t :underline t)) | |
298 | "Face used for installed outputs of a package." | |
299 | :group 'guix-package-info-faces) | |
300 | ||
301 | (defface guix-package-info-uninstalled-outputs | |
302 | '((t :weight bold)) | |
303 | "Face used for uninstalled outputs of a package." | |
304 | :group 'guix-package-info-faces) | |
305 | ||
306 | (defface guix-package-info-obsolete | |
307 | '((t :inherit error)) | |
308 | "Face used if a package is obsolete." | |
309 | :group 'guix-package-info-faces) | |
310 | ||
311 | (defcustom guix-package-info-auto-find-source nil | |
312 | "If non-nil, find a source file after pressing a \"Show\" button. | |
313 | If nil, just display the source file path without finding." | |
314 | :type 'boolean | |
315 | :group 'guix-package-info) | |
316 | ||
317 | (defcustom guix-package-info-auto-download-source t | |
318 | "If nil, do not automatically download a source file if it doesn't exist. | |
319 | After pressing a \"Show\" button, a derivation of the package | |
320 | source is calculated and a store file path is displayed. If this | |
321 | variable is non-nil and the source file does not exist in the | |
322 | store, it will be automatically downloaded (with a possible | |
323 | prompt depending on `guix-operation-confirm' variable)." | |
324 | :type 'boolean | |
325 | :group 'guix-package-info) | |
326 | ||
327 | (defvar guix-package-info-download-buffer nil | |
328 | "Buffer from which a current download operation was performed.") | |
329 | ||
330 | (defvar guix-package-info-output-format "%-10s" | |
331 | "String used to format output names of the packages. | |
332 | It should be a '%s'-sequence. After inserting an output name | |
333 | formatted with this string, an action button is inserted.") | |
334 | ||
335 | (defvar guix-package-info-obsolete-string "(This package is obsolete)" | |
336 | "String used if a package is obsolete.") | |
337 | ||
338 | (define-button-type 'guix-package-location | |
339 | :supertype 'guix | |
340 | 'face 'guix-package-info-location | |
341 | 'help-echo "Find location of this package" | |
342 | 'action (lambda (btn) | |
343 | (guix-find-location (button-label btn)))) | |
344 | ||
cefb7aea AK |
345 | (define-button-type 'guix-package-license |
346 | :supertype 'guix | |
347 | 'face 'guix-package-info-license | |
348 | 'help-echo "Browse license URL" | |
349 | 'action (lambda (btn) | |
350 | (guix-browse-license-url (button-label btn)))) | |
351 | ||
c80ce104 AK |
352 | (define-button-type 'guix-package-name |
353 | :supertype 'guix | |
354 | 'face 'guix-package-info-name-button | |
355 | 'help-echo "Describe this package" | |
356 | 'action (lambda (btn) | |
357 | (guix-buffer-get-display-entries-current | |
358 | 'info guix-package-info-type | |
359 | (list (guix-ui-current-profile) | |
c292a6f3 AK |
360 | 'name (or (button-get btn 'spec) |
361 | (button-label btn))) | |
c80ce104 AK |
362 | 'add))) |
363 | ||
6b3a1ce8 AK |
364 | (define-button-type 'guix-package-heading |
365 | :supertype 'guix-package-name | |
366 | 'face 'guix-package-info-heading) | |
367 | ||
c80ce104 AK |
368 | (define-button-type 'guix-package-source |
369 | :supertype 'guix | |
370 | 'face 'guix-package-info-source | |
371 | 'help-echo "" | |
372 | 'action (lambda (_) | |
373 | ;; As a source may not be a real URL (e.g., "mirror://..."), | |
374 | ;; no action is bound to a source button. | |
375 | (message "Yes, this is the source URL. What did you expect?"))) | |
376 | ||
377 | (defun guix-package-info-insert-heading (entry) | |
c292a6f3 | 378 | "Insert package ENTRY heading (name and version) at point." |
c80ce104 | 379 | (guix-insert-button |
c292a6f3 AK |
380 | (concat (guix-entry-value entry 'name) " " |
381 | (guix-entry-value entry 'version)) | |
382 | 'guix-package-heading | |
383 | 'spec (guix-package-entry->name-specification entry))) | |
c80ce104 | 384 | |
0a5ec709 AK |
385 | (defun guix-package-info-insert-systems (systems entry) |
386 | "Insert supported package SYSTEMS at point." | |
387 | (guix-info-insert-value-format | |
388 | systems 'guix-hydra-build-system | |
389 | 'action (lambda (btn) | |
390 | (let ((args (guix-hydra-build-latest-prompt-args | |
391 | :job (button-get btn 'job-name) | |
392 | :system (button-label btn)))) | |
393 | (apply #'guix-hydra-build-get-display | |
394 | 'latest args))) | |
b8fa5a2a | 395 | 'job-name (guix-hydra-job-name-specification |
0a5ec709 AK |
396 | (guix-entry-value entry 'name) |
397 | (guix-entry-value entry 'version)))) | |
398 | ||
c80ce104 AK |
399 | (defmacro guix-package-info-define-insert-inputs (&optional type) |
400 | "Define a face and a function for inserting package inputs. | |
401 | TYPE is a type of inputs. | |
402 | Function name is `guix-package-info-insert-TYPE-inputs'. | |
403 | Face name is `guix-package-info-TYPE-inputs'." | |
404 | (let* ((type-str (symbol-name type)) | |
405 | (type-name (and type (concat type-str "-"))) | |
406 | (type-desc (and type (concat type-str " "))) | |
407 | (face (intern (concat "guix-package-info-" type-name "inputs"))) | |
408 | (btn (intern (concat "guix-package-" type-name "input")))) | |
409 | `(progn | |
410 | (defface ,face | |
411 | '((t :inherit guix-package-info-name-button)) | |
412 | ,(concat "Face used for " type-desc "inputs of a package.") | |
413 | :group 'guix-package-info-faces) | |
414 | ||
415 | (define-button-type ',btn | |
416 | :supertype 'guix-package-name | |
417 | 'face ',face)))) | |
418 | ||
419 | (guix-package-info-define-insert-inputs) | |
420 | (guix-package-info-define-insert-inputs native) | |
421 | (guix-package-info-define-insert-inputs propagated) | |
422 | ||
423 | (defun guix-package-info-insert-outputs (outputs entry) | |
424 | "Insert OUTPUTS from package ENTRY at point." | |
425 | (and (guix-entry-value entry 'obsolete) | |
426 | (guix-package-info-insert-obsolete-text)) | |
427 | (and (guix-entry-value entry 'non-unique) | |
428 | (guix-entry-value entry 'installed) | |
429 | (guix-package-info-insert-non-unique-text | |
430 | (guix-package-entry->name-specification entry))) | |
431 | (insert "\n") | |
432 | (dolist (output outputs) | |
433 | (guix-package-info-insert-output output entry))) | |
434 | ||
435 | (defun guix-package-info-insert-obsolete-text () | |
436 | "Insert a message about obsolete package at point." | |
437 | (guix-info-insert-indent) | |
438 | (guix-format-insert guix-package-info-obsolete-string | |
439 | 'guix-package-info-obsolete)) | |
440 | ||
441 | (defun guix-package-info-insert-non-unique-text (full-name) | |
442 | "Insert a message about non-unique package with FULL-NAME at point." | |
443 | (insert "\n") | |
444 | (guix-info-insert-indent) | |
445 | (insert "Installed outputs are displayed for a non-unique ") | |
446 | (guix-insert-button full-name 'guix-package-name) | |
447 | (insert " package.")) | |
448 | ||
449 | (defun guix-package-info-insert-output (output entry) | |
450 | "Insert OUTPUT at point. | |
451 | Make some fancy text with buttons and additional stuff if the | |
452 | current OUTPUT is installed (if there is such output in | |
453 | `installed' parameter of a package ENTRY)." | |
454 | (let* ((installed (guix-entry-value entry 'installed)) | |
455 | (obsolete (guix-entry-value entry 'obsolete)) | |
456 | (installed-entry (cl-find-if | |
457 | (lambda (entry) | |
458 | (string= (guix-entry-value entry 'output) | |
459 | output)) | |
460 | installed)) | |
260795b7 AK |
461 | (action-type (if installed-entry 'delete 'install)) |
462 | (profile (guix-ui-current-profile))) | |
c80ce104 AK |
463 | (guix-info-insert-indent) |
464 | (guix-format-insert output | |
465 | (if installed-entry | |
466 | 'guix-package-info-installed-outputs | |
467 | 'guix-package-info-uninstalled-outputs) | |
468 | guix-package-info-output-format) | |
260795b7 AK |
469 | ;; Do not allow a user to install/delete anything to/from a system |
470 | ;; profile, so add action buttons only for non-system profiles. | |
471 | (when (and profile | |
472 | (not (guix-system-profile? profile))) | |
473 | (guix-package-info-insert-action-button action-type entry output) | |
474 | (when obsolete | |
475 | (guix-info-insert-indent) | |
476 | (guix-package-info-insert-action-button 'upgrade entry output))) | |
c80ce104 AK |
477 | (insert "\n") |
478 | (when installed-entry | |
479 | (guix-info-insert-entry installed-entry 'installed-output 2)))) | |
480 | ||
481 | (defun guix-package-info-insert-action-button (type entry output) | |
482 | "Insert button to process an action on a package OUTPUT at point. | |
483 | TYPE is one of the following symbols: `install', `delete', `upgrade'. | |
484 | ENTRY is an alist with package info." | |
485 | (let ((type-str (capitalize (symbol-name type))) | |
486 | (full-name (guix-package-entry->name-specification entry output))) | |
487 | (guix-info-insert-action-button | |
488 | type-str | |
489 | (lambda (btn) | |
490 | (guix-process-package-actions | |
491 | (guix-ui-current-profile) | |
492 | `((,(button-get btn 'action-type) (,(button-get btn 'id) | |
493 | ,(button-get btn 'output)))) | |
494 | (current-buffer))) | |
495 | (concat type-str " '" full-name "'") | |
496 | 'action-type type | |
497 | 'id (or (guix-entry-value entry 'package-id) | |
498 | (guix-entry-id entry)) | |
499 | 'output output))) | |
500 | ||
501 | (defun guix-package-info-show-source (entry-id package-id) | |
502 | "Show file name of a package source in the current info buffer. | |
503 | Find the file if needed (see `guix-package-info-auto-find-source'). | |
504 | ENTRY-ID is an ID of the current entry (package or output). | |
505 | PACKAGE-ID is an ID of the package which source to show." | |
506 | (let* ((entries (guix-buffer-current-entries)) | |
507 | (entry (guix-entry-by-id entry-id entries)) | |
508 | (file (guix-package-source-path package-id))) | |
509 | (or file | |
510 | (error "Couldn't define file name of the package source")) | |
511 | (let* ((new-entry (cons (cons 'source-file file) | |
512 | entry)) | |
513 | (new-entries (guix-replace-entry entry-id new-entry entries))) | |
514 | (setf (guix-buffer-item-entries guix-buffer-item) | |
515 | new-entries) | |
516 | (guix-buffer-redisplay-goto-button) | |
517 | (if (file-exists-p file) | |
518 | (if guix-package-info-auto-find-source | |
519 | (guix-find-file file) | |
520 | (message "The source store path is displayed.")) | |
521 | (if guix-package-info-auto-download-source | |
522 | (guix-package-info-download-source package-id) | |
523 | (message "The source does not exist in the store.")))))) | |
524 | ||
525 | (defun guix-package-info-download-source (package-id) | |
526 | "Download a source of the package PACKAGE-ID." | |
527 | (setq guix-package-info-download-buffer (current-buffer)) | |
528 | (guix-package-source-build-derivation | |
529 | package-id | |
530 | "The source does not exist in the store. Download it?")) | |
531 | ||
532 | (defun guix-package-info-insert-source (source entry) | |
533 | "Insert SOURCE from package ENTRY at point. | |
534 | SOURCE is a list of URLs." | |
535 | (if (null source) | |
536 | (guix-format-insert nil) | |
537 | (let* ((source-file (guix-entry-value entry 'source-file)) | |
538 | (entry-id (guix-entry-id entry)) | |
539 | (package-id (or (guix-entry-value entry 'package-id) | |
540 | entry-id))) | |
541 | (if (null source-file) | |
542 | (guix-info-insert-action-button | |
543 | "Show" | |
544 | (lambda (btn) | |
545 | (guix-package-info-show-source (button-get btn 'entry-id) | |
546 | (button-get btn 'package-id))) | |
547 | "Show the source store directory of the current package" | |
548 | 'entry-id entry-id | |
549 | 'package-id package-id) | |
550 | (unless (file-exists-p source-file) | |
551 | (guix-info-insert-action-button | |
552 | "Download" | |
553 | (lambda (btn) | |
554 | (guix-package-info-download-source | |
555 | (button-get btn 'package-id))) | |
556 | "Download the source into the store" | |
557 | 'package-id package-id)) | |
558 | (guix-info-insert-value-indent source-file 'guix-file)) | |
559 | (guix-info-insert-value-indent source 'guix-package-source)))) | |
560 | ||
561 | (defun guix-package-info-redisplay-after-download () | |
562 | "Redisplay an 'info' buffer after downloading the package source. | |
563 | This function is used to hide a \"Download\" button if needed." | |
564 | (when (buffer-live-p guix-package-info-download-buffer) | |
565 | (with-current-buffer guix-package-info-download-buffer | |
566 | (guix-buffer-redisplay-goto-button)) | |
567 | (setq guix-package-info-download-buffer nil))) | |
568 | ||
569 | (add-hook 'guix-after-source-download-hook | |
570 | 'guix-package-info-redisplay-after-download) | |
571 | ||
572 | \f | |
573 | ;;; Package 'list' | |
574 | ||
575 | (guix-ui-list-define-interface package | |
576 | :buffer-name "*Guix Package List*" | |
577 | :format '((name guix-package-list-get-name 20 t) | |
578 | (version nil 10 nil) | |
579 | (outputs nil 13 t) | |
580 | (installed guix-package-list-get-installed-outputs 13 t) | |
581 | (synopsis guix-list-get-one-line 30 nil)) | |
582 | :sort-key '(name) | |
583 | :marks '((install . ?I) | |
584 | (upgrade . ?U) | |
585 | (delete . ?D))) | |
586 | ||
587 | (let ((map guix-package-list-mode-map)) | |
5c8994d9 | 588 | (define-key map (kbd "B") 'guix-package-list-latest-builds) |
c80ce104 AK |
589 | (define-key map (kbd "e") 'guix-package-list-edit) |
590 | (define-key map (kbd "x") 'guix-package-list-execute) | |
591 | (define-key map (kbd "i") 'guix-package-list-mark-install) | |
592 | (define-key map (kbd "d") 'guix-package-list-mark-delete) | |
593 | (define-key map (kbd "U") 'guix-package-list-mark-upgrade) | |
594 | (define-key map (kbd "^") 'guix-package-list-mark-upgrades)) | |
595 | ||
596 | (defface guix-package-list-installed | |
597 | '((t :inherit guix-package-info-installed-outputs)) | |
598 | "Face used if there are installed outputs for the current package." | |
599 | :group 'guix-package-list-faces) | |
600 | ||
601 | (defface guix-package-list-obsolete | |
602 | '((t :inherit guix-package-info-obsolete)) | |
603 | "Face used if a package is obsolete." | |
604 | :group 'guix-package-list-faces) | |
605 | ||
606 | (defcustom guix-package-list-generation-marking-enabled nil | |
607 | "If non-nil, allow putting marks in a list with 'generation packages'. | |
608 | ||
609 | By default this is disabled, because it may be confusing. For | |
610 | example, a package is installed in some generation, so a user can | |
611 | mark it for deletion in the list of packages from this | |
612 | generation, but the package may not be installed in the latest | |
613 | generation, so actually it cannot be deleted. | |
614 | ||
615 | If you managed to understand the explanation above or if you | |
616 | really know what you do or if you just don't care, you can set | |
617 | this variable to t. It should not do much harm anyway (most | |
618 | likely)." | |
619 | :type 'boolean | |
620 | :group 'guix-package-list) | |
621 | ||
622 | (defun guix-package-list-get-name (name entry) | |
623 | "Return NAME of the package ENTRY. | |
624 | Colorize it with `guix-package-list-installed' or | |
625 | `guix-package-list-obsolete' if needed." | |
626 | (guix-get-string name | |
627 | (cond ((guix-entry-value entry 'obsolete) | |
628 | 'guix-package-list-obsolete) | |
629 | ((guix-entry-value entry 'installed) | |
630 | 'guix-package-list-installed)))) | |
631 | ||
632 | (defun guix-package-list-get-installed-outputs (installed &optional _) | |
633 | "Return string with outputs from INSTALLED entries." | |
634 | (guix-get-string | |
635 | (mapcar (lambda (entry) | |
636 | (guix-entry-value entry 'output)) | |
637 | installed))) | |
638 | ||
639 | (defun guix-package-list-marking-check () | |
640 | "Signal an error if marking is disabled for the current buffer." | |
641 | (when (and (not guix-package-list-generation-marking-enabled) | |
642 | (or (derived-mode-p 'guix-package-list-mode) | |
643 | (derived-mode-p 'guix-output-list-mode)) | |
644 | (eq (guix-ui-current-search-type) 'generation)) | |
645 | (error "Action marks are disabled for lists of 'generation packages'"))) | |
646 | ||
647 | (defun guix-package-list-mark-outputs (mark default | |
648 | &optional prompt available) | |
649 | "Mark the current package with MARK and move to the next line. | |
650 | If PROMPT is non-nil, use it to ask a user for outputs from | |
651 | AVAILABLE list, otherwise mark all DEFAULT outputs." | |
652 | (let ((outputs (if prompt | |
653 | (guix-completing-read-multiple | |
654 | prompt available nil t) | |
655 | default))) | |
656 | (apply #'guix-list--mark mark t outputs))) | |
657 | ||
658 | (defun guix-package-list-mark-install (&optional arg) | |
659 | "Mark the current package for installation and move to the next line. | |
660 | With ARG, prompt for the outputs to install (several outputs may | |
661 | be separated with \",\")." | |
662 | (interactive "P") | |
663 | (guix-package-list-marking-check) | |
664 | (let* ((entry (guix-list-current-entry)) | |
665 | (all (guix-entry-value entry 'outputs)) | |
666 | (installed (guix-package-installed-outputs entry)) | |
667 | (available (cl-set-difference all installed :test #'string=))) | |
668 | (or available | |
669 | (user-error "This package is already installed")) | |
670 | (guix-package-list-mark-outputs | |
671 | 'install '("out") | |
672 | (and arg "Output(s) to install: ") | |
673 | available))) | |
674 | ||
675 | (defun guix-package-list-mark-delete (&optional arg) | |
676 | "Mark the current package for deletion and move to the next line. | |
677 | With ARG, prompt for the outputs to delete (several outputs may | |
678 | be separated with \",\")." | |
679 | (interactive "P") | |
680 | (guix-package-list-marking-check) | |
681 | (let* ((entry (guix-list-current-entry)) | |
682 | (installed (guix-package-installed-outputs entry))) | |
683 | (or installed | |
684 | (user-error "This package is not installed")) | |
685 | (guix-package-list-mark-outputs | |
686 | 'delete installed | |
687 | (and arg "Output(s) to delete: ") | |
688 | installed))) | |
689 | ||
690 | (defun guix-package-list-mark-upgrade (&optional arg) | |
691 | "Mark the current package for upgrading and move to the next line. | |
692 | With ARG, prompt for the outputs to upgrade (several outputs may | |
693 | be separated with \",\")." | |
694 | (interactive "P") | |
695 | (guix-package-list-marking-check) | |
696 | (let* ((entry (guix-list-current-entry)) | |
697 | (installed (guix-package-installed-outputs entry))) | |
698 | (or installed | |
699 | (user-error "This package is not installed")) | |
700 | (when (or (guix-entry-value entry 'obsolete) | |
701 | (y-or-n-p "This package is not obsolete. Try to upgrade it anyway? ")) | |
702 | (guix-package-list-mark-outputs | |
703 | 'upgrade installed | |
704 | (and arg "Output(s) to upgrade: ") | |
705 | installed)))) | |
706 | ||
707 | (defun guix-package-mark-upgrades (fun) | |
708 | "Mark all obsolete packages for upgrading. | |
709 | Use FUN to perform marking of the current line. FUN should | |
710 | take an entry as argument." | |
711 | (guix-package-list-marking-check) | |
712 | (let ((obsolete (cl-remove-if-not | |
713 | (lambda (entry) | |
714 | (guix-entry-value entry 'obsolete)) | |
715 | (guix-buffer-current-entries)))) | |
716 | (guix-list-for-each-line | |
717 | (lambda () | |
718 | (let* ((id (guix-list-current-id)) | |
719 | (entry (cl-find-if | |
720 | (lambda (entry) | |
721 | (equal id (guix-entry-id entry))) | |
722 | obsolete))) | |
723 | (when entry | |
724 | (funcall fun entry))))))) | |
725 | ||
726 | (defun guix-package-list-mark-upgrades () | |
727 | "Mark all obsolete packages for upgrading." | |
728 | (interactive) | |
729 | (guix-package-mark-upgrades | |
730 | (lambda (entry) | |
731 | (apply #'guix-list--mark | |
732 | 'upgrade nil | |
733 | (guix-package-installed-outputs entry))))) | |
734 | ||
260795b7 AK |
735 | (defun guix-package-assert-non-system-profile () |
736 | "Verify that the current profile is not a system one. | |
737 | The current profile is the one used by the current buffer." | |
738 | (let ((profile (guix-ui-current-profile))) | |
739 | (and profile | |
740 | (guix-system-profile? profile) | |
741 | (user-error "Packages cannot be installed or removed to/from \ | |
742 | profile '%s'. | |
743 | Use 'guix system reconfigure' shell command to modify a system profile." | |
744 | profile)))) | |
745 | ||
c80ce104 AK |
746 | (defun guix-package-execute-actions (fun) |
747 | "Perform actions on the marked packages. | |
748 | Use FUN to define actions suitable for `guix-process-package-actions'. | |
749 | FUN should take action-type as argument." | |
260795b7 | 750 | (guix-package-assert-non-system-profile) |
c80ce104 AK |
751 | (let ((actions (delq nil |
752 | (mapcar fun '(install delete upgrade))))) | |
753 | (if actions | |
754 | (guix-process-package-actions (guix-ui-current-profile) | |
755 | actions (current-buffer)) | |
756 | (user-error "No operations specified")))) | |
757 | ||
758 | (defun guix-package-list-execute () | |
759 | "Perform actions on the marked packages." | |
760 | (interactive) | |
761 | (guix-package-execute-actions #'guix-package-list-make-action)) | |
762 | ||
763 | (defun guix-package-list-make-action (action-type) | |
764 | "Return action specification for the packages marked with ACTION-TYPE. | |
765 | Return nil, if there are no packages marked with ACTION-TYPE. | |
766 | The specification is suitable for `guix-process-package-actions'." | |
767 | (let ((specs (guix-list-get-marked-args action-type))) | |
768 | (and specs (cons action-type specs)))) | |
769 | ||
2c04e2ee AK |
770 | (defun guix-package-list-edit (&optional directory) |
771 | "Go to the location of the current package. | |
772 | See `guix-find-location' for the meaning of DIRECTORY." | |
773 | (interactive (list (guix-read-directory))) | |
774 | (guix-edit (guix-list-current-id) directory)) | |
c80ce104 | 775 | |
5c8994d9 AK |
776 | (defun guix-package-list-latest-builds (number &rest args) |
777 | "Display latest NUMBER of Hydra builds of the current package. | |
778 | Interactively, prompt for NUMBER. With prefix argument, prompt | |
779 | for all ARGS." | |
780 | (interactive | |
781 | (let ((entry (guix-list-current-entry))) | |
782 | (guix-hydra-build-latest-prompt-args | |
b8fa5a2a | 783 | :job (guix-hydra-job-name-specification |
5c8994d9 AK |
784 | (guix-entry-value entry 'name) |
785 | (guix-entry-value entry 'version))))) | |
786 | (apply #'guix-hydra-latest-builds number args)) | |
787 | ||
c80ce104 AK |
788 | \f |
789 | ;;; Output 'info' | |
790 | ||
791 | (guix-ui-info-define-interface output | |
792 | :buffer-name "*Guix Package Info*" | |
793 | :format '((name format (format guix-package-info-name)) | |
794 | (version format guix-output-info-insert-version) | |
795 | (output format guix-output-info-insert-output) | |
796 | (synopsis simple (indent guix-package-info-synopsis)) | |
797 | (source simple guix-package-info-insert-source) | |
798 | (path simple (indent guix-file)) | |
799 | (dependencies simple (indent guix-file)) | |
800 | (location format (format guix-package-location)) | |
801 | (home-url format (format guix-url)) | |
cefb7aea | 802 | (license format (format guix-package-license)) |
0a5ec709 | 803 | (systems format guix-package-info-insert-systems) |
c80ce104 AK |
804 | (inputs format (format guix-package-input)) |
805 | (native-inputs format (format guix-package-native-input)) | |
806 | (propagated-inputs format | |
807 | (format guix-package-propagated-input)) | |
808 | (description simple (indent guix-package-info-description))) | |
809 | :titles guix-package-info-titles | |
810 | :required '(id package-id installed non-unique)) | |
811 | ||
812 | (defun guix-output-info-insert-version (version entry) | |
813 | "Insert output VERSION and obsolete text if needed at point." | |
814 | (guix-info-insert-value-format version | |
815 | 'guix-package-info-version) | |
816 | (and (guix-entry-value entry 'obsolete) | |
817 | (guix-package-info-insert-obsolete-text))) | |
818 | ||
819 | (defun guix-output-info-insert-output (output entry) | |
820 | "Insert OUTPUT and action buttons at point." | |
821 | (let* ((installed (guix-entry-value entry 'installed)) | |
822 | (obsolete (guix-entry-value entry 'obsolete)) | |
823 | (action-type (if installed 'delete 'install))) | |
824 | (guix-info-insert-value-format | |
825 | output | |
826 | (if installed | |
827 | 'guix-package-info-installed-outputs | |
828 | 'guix-package-info-uninstalled-outputs)) | |
829 | (guix-info-insert-indent) | |
830 | (guix-package-info-insert-action-button action-type entry output) | |
831 | (when obsolete | |
832 | (guix-info-insert-indent) | |
833 | (guix-package-info-insert-action-button 'upgrade entry output)))) | |
834 | ||
835 | \f | |
836 | ;;; Output 'list' | |
837 | ||
838 | (guix-ui-list-define-interface output | |
839 | :buffer-name "*Guix Package List*" | |
840 | :describe-function 'guix-output-list-describe | |
841 | :format '((name guix-package-list-get-name 20 t) | |
842 | (version nil 10 nil) | |
843 | (output nil 9 t) | |
844 | (installed nil 12 t) | |
845 | (synopsis guix-list-get-one-line 30 nil)) | |
846 | :required '(id package-id) | |
847 | :sort-key '(name) | |
848 | :marks '((install . ?I) | |
849 | (upgrade . ?U) | |
850 | (delete . ?D))) | |
851 | ||
852 | (let ((map guix-output-list-mode-map)) | |
5c8994d9 | 853 | (define-key map (kbd "B") 'guix-package-list-latest-builds) |
c80ce104 AK |
854 | (define-key map (kbd "e") 'guix-output-list-edit) |
855 | (define-key map (kbd "x") 'guix-output-list-execute) | |
856 | (define-key map (kbd "i") 'guix-output-list-mark-install) | |
857 | (define-key map (kbd "d") 'guix-output-list-mark-delete) | |
858 | (define-key map (kbd "U") 'guix-output-list-mark-upgrade) | |
859 | (define-key map (kbd "^") 'guix-output-list-mark-upgrades)) | |
860 | ||
861 | (defun guix-output-list-mark-install () | |
862 | "Mark the current output for installation and move to the next line." | |
863 | (interactive) | |
864 | (guix-package-list-marking-check) | |
865 | (let* ((entry (guix-list-current-entry)) | |
866 | (installed (guix-entry-value entry 'installed))) | |
867 | (if installed | |
868 | (user-error "This output is already installed") | |
869 | (guix-list--mark 'install t)))) | |
870 | ||
871 | (defun guix-output-list-mark-delete () | |
872 | "Mark the current output for deletion and move to the next line." | |
873 | (interactive) | |
874 | (guix-package-list-marking-check) | |
875 | (let* ((entry (guix-list-current-entry)) | |
876 | (installed (guix-entry-value entry 'installed))) | |
877 | (if installed | |
878 | (guix-list--mark 'delete t) | |
879 | (user-error "This output is not installed")))) | |
880 | ||
881 | (defun guix-output-list-mark-upgrade () | |
882 | "Mark the current output for upgrading and move to the next line." | |
883 | (interactive) | |
884 | (guix-package-list-marking-check) | |
885 | (let* ((entry (guix-list-current-entry)) | |
886 | (installed (guix-entry-value entry 'installed))) | |
887 | (or installed | |
888 | (user-error "This output is not installed")) | |
889 | (when (or (guix-entry-value entry 'obsolete) | |
890 | (y-or-n-p "This output is not obsolete. Try to upgrade it anyway? ")) | |
891 | (guix-list--mark 'upgrade t)))) | |
892 | ||
893 | (defun guix-output-list-mark-upgrades () | |
894 | "Mark all obsolete package outputs for upgrading." | |
895 | (interactive) | |
896 | (guix-package-mark-upgrades | |
897 | (lambda (_) (guix-list--mark 'upgrade)))) | |
898 | ||
899 | (defun guix-output-list-execute () | |
900 | "Perform actions on the marked outputs." | |
901 | (interactive) | |
902 | (guix-package-execute-actions #'guix-output-list-make-action)) | |
903 | ||
904 | (defun guix-output-list-make-action (action-type) | |
905 | "Return action specification for the outputs marked with ACTION-TYPE. | |
906 | Return nil, if there are no outputs marked with ACTION-TYPE. | |
907 | The specification is suitable for `guix-process-output-actions'." | |
908 | (let ((ids (guix-list-get-marked-id-list action-type))) | |
909 | (and ids (cons action-type | |
910 | (mapcar #'guix-package-id-and-output-by-output-id | |
911 | ids))))) | |
912 | ||
913 | (defun guix-output-list-describe (ids) | |
914 | "Describe outputs with IDS (list of output identifiers). | |
915 | See `guix-package-info-type'." | |
916 | (if (eq guix-package-info-type 'output) | |
917 | (guix-buffer-get-display-entries | |
918 | 'info 'output | |
919 | (cl-list* (guix-ui-current-profile) 'id ids) | |
920 | 'add) | |
921 | (let ((pids (mapcar (lambda (oid) | |
922 | (car (guix-package-id-and-output-by-output-id | |
923 | oid))) | |
924 | ids))) | |
925 | (guix-buffer-get-display-entries | |
926 | 'info 'package | |
927 | (cl-list* (guix-ui-current-profile) | |
928 | 'id (cl-remove-duplicates pids)) | |
929 | 'add)))) | |
930 | ||
2c04e2ee AK |
931 | (defun guix-output-list-edit (&optional directory) |
932 | "Go to the location of the current package. | |
933 | See `guix-find-location' for the meaning of DIRECTORY." | |
934 | (interactive (list (guix-read-directory))) | |
c80ce104 | 935 | (guix-edit (guix-entry-value (guix-list-current-entry) |
2c04e2ee AK |
936 | 'package-id) |
937 | directory)) | |
c80ce104 AK |
938 | |
939 | \f | |
940 | ;;; Interactive commands | |
941 | ||
942 | (defvar guix-package-search-params '(name synopsis description) | |
943 | "Default list of package parameters for searching by regexp.") | |
944 | ||
945 | (defvar guix-package-search-history nil | |
946 | "A history of minibuffer prompts.") | |
947 | ||
948 | ;;;###autoload | |
e119ba90 AK |
949 | (defun guix-packages-by-name (name &optional profile) |
950 | "Display Guix packages with NAME. | |
c80ce104 | 951 | NAME is a string with name specification. It may optionally contain |
db0c709b | 952 | a version number. Examples: \"guile\", \"guile@2.0.11\". |
c80ce104 AK |
953 | |
954 | If PROFILE is nil, use `guix-current-profile'. | |
955 | Interactively with prefix, prompt for PROFILE." | |
956 | (interactive | |
e119ba90 | 957 | (list (guix-read-package-name) |
494a62f2 | 958 | (guix-ui-read-profile))) |
c80ce104 AK |
959 | (guix-package-get-display profile 'name name)) |
960 | ||
83aab70b AK |
961 | ;;;###autoload |
962 | (defun guix-packages-by-license (license &optional profile) | |
963 | "Display Guix packages with LICENSE. | |
964 | LICENSE is a license name string. | |
965 | If PROFILE is nil, use `guix-current-profile'. | |
966 | Interactively with prefix, prompt for PROFILE." | |
967 | (interactive | |
968 | (list (guix-read-license-name) | |
969 | (guix-ui-read-profile))) | |
970 | (guix-package-get-display profile 'license license)) | |
971 | ||
c80ce104 AK |
972 | ;;;###autoload |
973 | (defun guix-search-by-regexp (regexp &optional params profile) | |
974 | "Search for Guix packages by REGEXP. | |
975 | PARAMS are package parameters that should be searched. | |
976 | If PARAMS are not specified, use `guix-package-search-params'. | |
977 | ||
978 | If PROFILE is nil, use `guix-current-profile'. | |
979 | Interactively with prefix, prompt for PROFILE." | |
980 | (interactive | |
981 | (list (read-regexp "Regexp: " nil 'guix-package-search-history) | |
494a62f2 | 982 | nil (guix-ui-read-profile))) |
c80ce104 AK |
983 | (guix-package-get-display profile 'regexp regexp |
984 | (or params guix-package-search-params))) | |
985 | ||
27a2e483 AK |
986 | ;;;###autoload |
987 | (defun guix-search-by-name (regexp &optional profile) | |
988 | "Search for Guix packages matching REGEXP in a package name. | |
989 | If PROFILE is nil, use `guix-current-profile'. | |
990 | Interactively with prefix, prompt for PROFILE." | |
991 | (interactive | |
992 | (list (read-string "Package name by regexp: " | |
993 | nil 'guix-package-search-history) | |
994 | (guix-ui-read-profile))) | |
995 | (guix-search-by-regexp regexp '(name) profile)) | |
996 | ||
c80ce104 AK |
997 | ;;;###autoload |
998 | (defun guix-installed-packages (&optional profile) | |
999 | "Display information about installed Guix packages. | |
1000 | If PROFILE is nil, use `guix-current-profile'. | |
1001 | Interactively with prefix, prompt for PROFILE." | |
494a62f2 | 1002 | (interactive (list (guix-ui-read-profile))) |
c80ce104 AK |
1003 | (guix-package-get-display profile 'installed)) |
1004 | ||
1005 | ;;;###autoload | |
cfb1c62a AK |
1006 | (defun guix-installed-user-packages () |
1007 | "Display information about Guix packages installed in a user profile." | |
1008 | (interactive) | |
1009 | (guix-installed-packages guix-user-profile)) | |
1010 | ||
1011 | ;;;###autoload | |
1012 | (defun guix-installed-system-packages () | |
1013 | "Display information about Guix packages installed in a system profile." | |
1014 | (interactive) | |
1015 | (guix-installed-packages | |
1016 | (guix-packages-profile guix-system-profile nil t))) | |
1017 | ||
1018 | ;;;###autoload | |
c80ce104 AK |
1019 | (defun guix-obsolete-packages (&optional profile) |
1020 | "Display information about obsolete Guix packages. | |
1021 | If PROFILE is nil, use `guix-current-profile'. | |
1022 | Interactively with prefix, prompt for PROFILE." | |
494a62f2 | 1023 | (interactive (list (guix-ui-read-profile))) |
c80ce104 AK |
1024 | (guix-package-get-display profile 'obsolete)) |
1025 | ||
1026 | ;;;###autoload | |
1027 | (defun guix-all-available-packages (&optional profile) | |
1028 | "Display information about all available Guix packages. | |
1029 | If PROFILE is nil, use `guix-current-profile'. | |
1030 | Interactively with prefix, prompt for PROFILE." | |
494a62f2 | 1031 | (interactive (list (guix-ui-read-profile))) |
c80ce104 AK |
1032 | (guix-package-get-display profile 'all-available)) |
1033 | ||
1034 | ;;;###autoload | |
1035 | (defun guix-newest-available-packages (&optional profile) | |
1036 | "Display information about the newest available Guix packages. | |
1037 | If PROFILE is nil, use `guix-current-profile'. | |
1038 | Interactively with prefix, prompt for PROFILE." | |
494a62f2 | 1039 | (interactive (list (guix-ui-read-profile))) |
c80ce104 AK |
1040 | (guix-package-get-display profile 'newest-available)) |
1041 | ||
1042 | (provide 'guix-ui-package) | |
1043 | ||
1044 | ;;; guix-ui-package.el ends here |