1 ;;; guix-info.el --- Info buffers for displaying entries -*- lexical-binding: t -*-
3 ;; Copyright © 2014, 2015 Alex Kost <alezost@gmail.com>
4 ;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
6 ;; This file is part of GNU Guix.
8 ;; GNU Guix is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
13 ;; GNU Guix is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
23 ;; This file provides a help-like buffer for displaying information
24 ;; about Guix packages and generations.
33 (defgroup guix-info nil
34 "General settings for info buffers."
38 (defgroup guix-info-faces nil
39 "Faces for info buffers."
43 (defface guix-info-heading
44 '((((type tty pc
) (class color
)) :weight bold
)
45 (t :height
1.6 :weight bold
:inherit variable-pitch
))
47 :group
'guix-info-faces
)
49 (defface guix-info-param-title
50 '((t :inherit font-lock-type-face
))
51 "Face used for titles of parameters."
52 :group
'guix-info-faces
)
54 (defface guix-info-file-path
56 "Face used for file paths."
57 :group
'guix-info-faces
)
59 (defface guix-info-url
62 :group
'guix-info-faces
)
64 (defface guix-info-time
65 '((t :inherit font-lock-constant-face
))
66 "Face used for timestamps."
67 :group
'guix-info-faces
)
69 (defface guix-info-action-button
70 '((((type x w32 ns
) (class color
))
71 :box
(:line-width
2 :style released-button
)
72 :background
"lightgrey" :foreground
"black")
74 "Face used for action buttons."
75 :group
'guix-info-faces
)
77 (defface guix-info-action-button-mouse
78 '((((type x w32 ns
) (class color
))
79 :box
(:line-width
2 :style released-button
)
80 :background
"grey90" :foreground
"black")
81 (t :inherit highlight
))
82 "Mouse face used for action buttons."
83 :group
'guix-info-faces
)
85 (defcustom guix-info-ignore-empty-values nil
86 "If non-nil, do not display parameters with nil values."
90 (defcustom guix-info-fill t
91 "If non-nil, fill string parameters to fit the window.
92 If nil, insert text parameters (like synopsis or description) in
97 (defvar guix-info-param-title-format
"%-18s: "
98 "String used to format a title of a parameter.
99 It should be a '%s'-sequence. After inserting a title formatted
100 with this string, a value of the parameter is inserted.
101 This string is used by `guix-info-insert-title-format'.")
103 (defvar guix-info-multiline-prefix
104 (make-string (length (format guix-info-param-title-format
" "))
106 "String used to format multi-line parameter values.
107 If a value occupies more than one line, this string is inserted
108 in the beginning of each line after the first one.
109 This string is used by `guix-info-insert-value-format'.")
111 (defvar guix-info-indent
2
112 "Number of spaces used to indent various parts of inserted text.")
114 (defvar guix-info-delimiter
"\n\f\n"
115 "String used to separate entries.")
118 ;;; Wrappers for 'info' variables
120 (defvar guix-info-data nil
121 "Alist with 'info' data.
122 This alist is filled by `guix-info-define-interface' macro.")
124 (defun guix-info-value (entry-type symbol
)
125 "Return SYMBOL's value for ENTRY-TYPE from `guix-info-data'."
126 (symbol-value (guix-assq-value guix-info-data entry-type symbol
)))
128 (defun guix-info-param-title (entry-type param
)
129 "Return a title of an ENTRY-TYPE parameter PARAM."
130 (guix-buffer-param-title 'info entry-type param
))
132 (defun guix-info-format (entry-type)
133 "Return 'info' format for ENTRY-TYPE."
134 (guix-info-value entry-type
'format
))
136 (defun guix-info-displayed-params (entry-type)
137 "Return a list of ENTRY-TYPE parameters that should be displayed."
139 (mapcar (lambda (spec)
141 (`(,param .
,_
) param
)))
142 (guix-info-format entry-type
))))
145 ;;; Inserting entries
147 (defvar guix-info-title-aliases
148 '((format . guix-info-insert-title-format
)
149 (simple . guix-info-insert-title-simple
))
150 "Alist of aliases and functions to insert titles.")
152 (defvar guix-info-value-aliases
153 '((format . guix-info-insert-value-format
)
154 (indent . guix-info-insert-value-indent
)
155 (simple . guix-info-insert-value-simple
)
156 (time . guix-info-insert-time
))
157 "Alist of aliases and functions to insert values.")
159 (defun guix-info-title-function (fun-or-alias)
160 "Convert FUN-OR-ALIAS into a function to insert a title."
161 (or (guix-assq-value guix-info-title-aliases fun-or-alias
)
164 (defun guix-info-value-function (fun-or-alias)
165 "Convert FUN-OR-ALIAS into a function to insert a value."
166 (or (guix-assq-value guix-info-value-aliases fun-or-alias
)
169 (defun guix-info-title-method->function
(method)
170 "Convert title METHOD into a function to insert a title."
172 ((pred null
) #'ignore
)
173 ((pred symbolp
) (guix-info-title-function method
))
174 (`(,fun-or-alias .
,rest-args
)
176 (apply (guix-info-title-function fun-or-alias
)
178 (_ (error "Unknown title method '%S'" method
))))
180 (defun guix-info-value-method->function
(method)
181 "Convert value METHOD into a function to insert a value."
183 ((pred null
) #'ignore
)
184 ((pred functionp
) method
)
185 (`(,fun-or-alias .
,rest-args
)
187 (apply (guix-info-value-function fun-or-alias
)
189 (_ (error "Unknown value method '%S'" method
))))
191 (defun guix-info-fill-column ()
192 "Return fill column for the current window."
193 (min (window-width) fill-column
))
195 (defun guix-info-get-indent (&optional level
)
196 "Return `guix-info-indent' \"multiplied\" by LEVEL spaces.
197 LEVEL is 1 by default."
198 (make-string (* guix-info-indent
(or level
1)) ?\s
))
200 (defun guix-info-insert-indent (&optional level
)
201 "Insert `guix-info-indent' spaces LEVEL times (1 by default)."
202 (insert (guix-info-get-indent level
)))
204 (defun guix-info-insert-entries (entries entry-type
)
205 "Display ENTRIES of ENTRY-TYPE in the current info buffer.
206 ENTRIES should have a form of `guix-entries'."
207 (guix-mapinsert (lambda (entry)
208 (guix-info-insert-entry entry entry-type
))
210 guix-info-delimiter
))
212 (defun guix-info-insert-entry (entry entry-type
&optional indent-level
)
213 "Insert ENTRY of ENTRY-TYPE into the current info buffer.
214 If INDENT-LEVEL is non-nil, indent displayed data by this number
215 of `guix-info-indent' spaces."
216 (guix-with-indent (* (or indent-level
0)
218 (dolist (spec (guix-info-format entry-type
))
219 (guix-info-insert-entry-unit spec entry entry-type
))))
221 (defun guix-info-insert-entry-unit (format-spec entry entry-type
)
222 "Insert title and value of a PARAM at point.
223 ENTRY is alist with parameters and their values.
224 ENTRY-TYPE is a type of ENTRY."
227 (funcall format-spec entry
)
229 (`(,param
,title-method
,value-method
)
230 (let ((value (guix-entry-value entry param
)))
231 (unless (and guix-info-ignore-empty-values
(null value
))
232 (let ((title (guix-info-param-title entry-type param
))
233 (insert-title (guix-info-title-method->function title-method
))
234 (insert-value (guix-info-value-method->function value-method
)))
235 (funcall insert-title title
)
236 (funcall insert-value value entry
)
238 (_ (error "Unknown format specification '%S'" format-spec
))))
240 (defun guix-info-insert-title-simple (title &optional face
)
241 "Insert \"TITLE: \" string at point.
242 If FACE is nil, use `guix-info-param-title'."
243 (guix-format-insert title
244 (or face
'guix-info-param-title
)
247 (defun guix-info-insert-title-format (title &optional face
)
248 "Insert TITLE using `guix-info-param-title-format' at point.
249 If FACE is nil, use `guix-info-param-title'."
250 (guix-format-insert title
251 (or face
'guix-info-param-title
)
252 guix-info-param-title-format
))
254 (defun guix-info-insert-value-simple (value &optional button-or-face indent
)
255 "Format and insert parameter VALUE at point.
257 VALUE may be split into several short lines to fit the current
258 window, depending on `guix-info-fill', and each line is indented
259 with INDENT number of spaces.
261 If BUTTON-OR-FACE is a button type symbol, transform VALUE into
262 this (these) button(s) and insert each one on a new line. If it
263 is a face symbol, propertize inserted line(s) with this face."
264 (or indent
(setq indent
0))
265 (guix-with-indent indent
266 (let* ((button?
(guix-button-type? button-or-face
))
267 (face (unless button? button-or-face
))
268 (fill-col (unless (or button?
270 (not guix-info-fill
)))
271 (- (guix-info-fill-column) indent
)))
272 (value (if (and value button?
)
273 (guix-buttonize value button-or-face
"\n")
275 (guix-split-insert value face fill-col
"\n"))))
277 (defun guix-info-insert-value-indent (value &optional button-or-face
)
278 "Format and insert parameter VALUE at point.
280 This function is intended to be called after inserting a title
281 with `guix-info-insert-title-simple'.
283 VALUE may be split into several short lines to fit the current
284 window, depending on `guix-info-fill', and each line is indented
285 with `guix-info-indent'.
287 For the meaning of BUTTON-OR-FACE, see `guix-info-insert-value-simple'."
288 (when value
(insert "\n"))
289 (guix-info-insert-value-simple value button-or-face guix-info-indent
))
291 (defun guix-info-insert-value-format (value &optional button-or-face
292 &rest button-properties
)
293 "Format and insert parameter VALUE at point.
295 This function is intended to be called after inserting a title
296 with `guix-info-insert-title-format'.
298 VALUE may be split into several short lines to fit the current
299 window, depending on `guix-info-fill' and
300 `guix-info-multiline-prefix'. If VALUE is a list, its elements
301 will be separated with `guix-list-separator'.
303 If BUTTON-OR-FACE is a button type symbol, transform VALUE into
304 this (these) button(s). If it is a face symbol, propertize
305 inserted line(s) with this face.
307 BUTTON-PROPERTIES are passed to `guix-buttonize' (only if
308 BUTTON-OR-FACE is a button type)."
309 (let* ((button?
(guix-button-type? button-or-face
))
310 (face (unless button? button-or-face
))
311 (fill-col (when (or button?
313 (not (stringp value
)))
314 (- (guix-info-fill-column)
315 (length guix-info-multiline-prefix
))))
316 (value (if (and value button?
)
317 (apply #'guix-buttonize
318 value button-or-face guix-list-separator
321 (guix-split-insert value face fill-col
322 (concat "\n" guix-info-multiline-prefix
))))
324 (defun guix-info-insert-time (seconds &optional face
)
325 "Insert formatted time string using SECONDS at point."
326 (guix-format-insert (guix-get-time-string seconds
)
327 (or face
'guix-info-time
)))
332 (defvar guix-info-button-map
333 (let ((map (make-sparse-keymap)))
334 (set-keymap-parent map button-map
)
335 (define-key map
(kbd "c") 'guix-info-button-copy-label
)
337 "Keymap for buttons in info buffers.")
339 (define-button-type 'guix
340 'keymap guix-info-button-map
343 (define-button-type 'guix-action
345 'face
'guix-info-action-button
346 'mouse-face
'guix-info-action-button-mouse
)
348 (define-button-type 'guix-file
350 'face
'guix-info-file-path
351 'help-echo
"Find file"
352 'action
(lambda (btn)
353 (guix-find-file (button-label btn
))))
355 (define-button-type 'guix-url
358 'help-echo
"Browse URL"
359 'action
(lambda (btn)
360 (browse-url (button-label btn
))))
362 (define-button-type 'guix-package-location
364 'face
'guix-package-info-location
365 'help-echo
"Find location of this package"
366 'action
(lambda (btn)
367 (guix-find-location (button-label btn
))))
369 (define-button-type 'guix-package-name
371 'face
'guix-package-info-name-button
372 'help-echo
"Describe this package"
373 'action
(lambda (btn)
374 (guix-get-show-entries guix-profile
'info guix-package-info-type
375 'name
(button-label btn
))))
377 (defun guix-info-button-copy-label (&optional pos
)
378 "Copy a label of the button at POS into kill ring.
379 If POS is nil, use the current point position."
381 (let ((button (button-at (or pos
(point)))))
383 (guix-copy-as-kill (button-label button
)))))
385 (defun guix-info-insert-action-button (label action
&optional message
387 "Make action button with LABEL and insert it at point.
388 ACTION is a function called when the button is pressed. It
389 should accept button as the argument.
390 MESSAGE is a button message.
391 See `insert-text-button' for the meaning of PROPERTIES."
392 (apply #'guix-insert-button
399 ;;; Major mode and interface definer
401 (defvar guix-info-mode-map
402 (let ((map (make-sparse-keymap)))
404 map
(make-composed-keymap (list guix-buffer-map button-buffer-map
)
407 "Keymap for `guix-info-mode' buffers.")
409 (define-derived-mode guix-info-mode special-mode
"Guix-Info"
410 "Parent mode for displaying information in info buffers.")
412 (defun guix-info-mode-initialize ()
413 "Set up the current 'info' buffer."
414 ;; Without this, syntactic fontification is performed, and it may
415 ;; break our highlighting. For example, description of "emacs-typo"
416 ;; package contains a single " (double-quote) character, so the
417 ;; default syntactic fontification highlights the rest text after it
418 ;; as a string. See (info "(elisp) Font Lock Basics") for details.
419 (setq font-lock-defaults
'(nil t
)))
421 (defmacro guix-info-define-interface
(entry-type &rest args
)
422 "Define 'info' interface for displaying ENTRY-TYPE entries.
423 Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ...
427 - `:format' - default value of the generated
428 `guix-ENTRY-TYPE-info-format' variable.
430 The rest keyword arguments are passed to
431 `guix-buffer-define-interface' macro."
433 (let* ((entry-type-str (symbol-name entry-type
))
434 (prefix (concat "guix-" entry-type-str
"-info"))
435 (group (intern prefix
))
436 (format-var (intern (concat prefix
"-format"))))
437 (guix-keyword-args-let args
438 ((format-val :format
))
440 (defcustom ,format-var
,format-val
442 List of methods for inserting '%s' entry.
443 Each METHOD should be either a function or should have the
446 (PARAM INSERT-TITLE INSERT-VALUE)
448 If METHOD is a function, it is called with an entry as argument.
450 PARAM is a name of '%s' entry parameter.
452 INSERT-TITLE may be either a symbol or a list. If it is a
453 symbol, it should be a function or an alias from
454 `guix-info-title-aliases', in which case it is called with title
455 as argument. If it is a list, it should have a
456 form (FUN-OR-ALIAS [ARGS ...]), in which case FUN-OR-ALIAS is
457 called with title and ARGS as arguments.
459 INSERT-VALUE may be either a symbol or a list. If it is a
460 symbol, it should be a function or an alias from
461 `guix-info-value-aliases', in which case it is called with value
462 and entry as arguments. If it is a list, it should have a
463 form (FUN-OR-ALIAS [ARGS ...]), in which case FUN-OR-ALIAS is
464 called with value and ARGS as arguments.
466 Parameters are inserted in the same order as defined by this list.
467 After calling each METHOD, a new line is inserted."
468 entry-type-str entry-type-str
)
473 '((format .
,format-var
))
474 'guix-info-data
',entry-type
)
476 (guix-buffer-define-interface info
,entry-type
477 :mode-init-function
'guix-info-mode-initialize
481 ;;; Displaying packages
483 (guix-ui-info-define-interface package
484 :buffer-name
"*Guix Package Info*"
485 :format
'(guix-package-info-insert-heading
487 (synopsis ignore
(simple guix-package-info-synopsis
))
489 (description ignore
(simple guix-package-info-description
))
491 (outputs simple guix-package-info-insert-outputs
)
492 (source simple guix-package-info-insert-source
)
493 (location format
(format guix-package-location
))
494 (home-url format
(format guix-url
))
495 (license format
(format guix-package-info-license
))
496 (inputs format
(format guix-package-input
))
497 (native-inputs format
(format guix-package-native-input
))
498 (propagated-inputs format
499 (format guix-package-propagated-input
)))
500 :titles
'((home-url .
"Home page"))
501 :required
'(id name version installed non-unique
))
503 (guix-info-define-interface installed-output
504 :format
'((path simple
(indent guix-file
))
505 (dependencies simple
(indent guix-file
)))
506 :titles
'((path .
"Store directory"))
509 (defface guix-package-info-heading
510 '((t :inherit guix-info-heading
))
511 "Face for package name and version headings."
512 :group
'guix-package-info-faces
)
514 (defface guix-package-info-name
515 '((t :inherit font-lock-keyword-face
))
516 "Face used for a name of a package."
517 :group
'guix-package-info-faces
)
519 (defface guix-package-info-name-button
520 '((t :inherit button
))
521 "Face used for a full name that can be used to describe a package."
522 :group
'guix-package-info-faces
)
524 (defface guix-package-info-version
525 '((t :inherit font-lock-builtin-face
))
526 "Face used for a version of a package."
527 :group
'guix-package-info-faces
)
529 (defface guix-package-info-synopsis
530 '((((type tty pc
) (class color
)) :weight bold
)
531 (t :height
1.1 :weight bold
:inherit variable-pitch
))
532 "Face used for a synopsis of a package."
533 :group
'guix-package-info-faces
)
535 (defface guix-package-info-description
537 "Face used for a description of a package."
538 :group
'guix-package-info-faces
)
540 (defface guix-package-info-license
541 '((t :inherit font-lock-string-face
))
542 "Face used for a license of a package."
543 :group
'guix-package-info-faces
)
545 (defface guix-package-info-location
547 "Face used for a location of a package."
548 :group
'guix-package-info-faces
)
550 (defface guix-package-info-installed-outputs
551 '((default :weight bold
)
552 (((class color
) (min-colors 88) (background light
))
553 :foreground
"ForestGreen")
554 (((class color
) (min-colors 88) (background dark
))
555 :foreground
"PaleGreen")
556 (((class color
) (min-colors 8))
559 "Face used for installed outputs of a package."
560 :group
'guix-package-info-faces
)
562 (defface guix-package-info-uninstalled-outputs
564 "Face used for uninstalled outputs of a package."
565 :group
'guix-package-info-faces
)
567 (defface guix-package-info-obsolete
568 '((t :inherit error
))
569 "Face used if a package is obsolete."
570 :group
'guix-package-info-faces
)
572 (defun guix-package-info-insert-heading (entry)
573 "Insert package ENTRY heading (name specification) at point."
575 (guix-package-entry->name-specification entry
)
577 'face
'guix-package-info-heading
))
579 (defmacro guix-package-info-define-insert-inputs
(&optional type
)
580 "Define a face and a function for inserting package inputs.
581 TYPE is a type of inputs.
582 Function name is `guix-package-info-insert-TYPE-inputs'.
583 Face name is `guix-package-info-TYPE-inputs'."
584 (let* ((type-str (symbol-name type
))
585 (type-name (and type
(concat type-str
"-")))
586 (type-desc (and type
(concat type-str
" ")))
587 (face (intern (concat "guix-package-info-" type-name
"inputs")))
588 (btn (intern (concat "guix-package-" type-name
"input"))))
591 '((t :inherit guix-package-info-name-button
))
592 ,(concat "Face used for " type-desc
"inputs of a package.")
593 :group
'guix-package-info-faces
)
595 (define-button-type ',btn
596 :supertype
'guix-package-name
599 (guix-package-info-define-insert-inputs)
600 (guix-package-info-define-insert-inputs native
)
601 (guix-package-info-define-insert-inputs propagated
)
604 ;;; Inserting outputs and installed parameters
606 (defvar guix-package-info-output-format
"%-10s"
607 "String used to format output names of the packages.
608 It should be a '%s'-sequence. After inserting an output name
609 formatted with this string, an action button is inserted.")
611 (defvar guix-package-info-obsolete-string
"(This package is obsolete)"
612 "String used if a package is obsolete.")
614 (defun guix-package-info-insert-outputs (outputs entry
)
615 "Insert OUTPUTS from package ENTRY at point."
616 (and (guix-entry-value entry
'obsolete
)
617 (guix-package-info-insert-obsolete-text))
618 (and (guix-entry-value entry
'non-unique
)
619 (guix-entry-value entry
'installed
)
620 (guix-package-info-insert-non-unique-text
621 (guix-package-entry->name-specification entry
)))
623 (mapc (lambda (output)
624 (guix-package-info-insert-output output entry
))
627 (defun guix-package-info-insert-obsolete-text ()
628 "Insert a message about obsolete package at point."
629 (guix-info-insert-indent)
630 (guix-format-insert guix-package-info-obsolete-string
631 'guix-package-info-obsolete
))
633 (defun guix-package-info-insert-non-unique-text (full-name)
634 "Insert a message about non-unique package with FULL-NAME at point."
636 (guix-info-insert-indent)
637 (insert "Installed outputs are displayed for a non-unique ")
638 (guix-insert-button full-name
'guix-package-name
)
639 (insert " package."))
641 (defun guix-package-info-insert-output (output entry
)
642 "Insert OUTPUT at point.
643 Make some fancy text with buttons and additional stuff if the
644 current OUTPUT is installed (if there is such output in
645 `installed' parameter of a package ENTRY)."
646 (let* ((installed (guix-entry-value entry
'installed
))
647 (obsolete (guix-entry-value entry
'obsolete
))
648 (installed-entry (cl-find-if
650 (string= (guix-entry-value entry
'output
)
653 (action-type (if installed-entry
'delete
'install
)))
654 (guix-info-insert-indent)
655 (guix-format-insert output
657 'guix-package-info-installed-outputs
658 'guix-package-info-uninstalled-outputs
)
659 guix-package-info-output-format
)
660 (guix-package-info-insert-action-button action-type entry output
)
662 (guix-info-insert-indent)
663 (guix-package-info-insert-action-button 'upgrade entry output
))
665 (when installed-entry
666 (guix-info-insert-entry installed-entry
'installed-output
2))))
668 (defun guix-package-info-insert-action-button (type entry output
)
669 "Insert button to process an action on a package OUTPUT at point.
670 TYPE is one of the following symbols: `install', `delete', `upgrade'.
671 ENTRY is an alist with package info."
672 (let ((type-str (capitalize (symbol-name type
)))
673 (full-name (guix-package-entry->name-specification entry output
)))
674 (guix-info-insert-action-button
677 (guix-process-package-actions
679 `((,(button-get btn
'action-type
) (,(button-get btn
'id
)
680 ,(button-get btn
'output
))))
682 (concat type-str
" '" full-name
"'")
684 'id
(or (guix-entry-value entry
'package-id
)
685 (guix-entry-id entry
))
689 ;;; Inserting a source
691 (defface guix-package-info-source
692 '((t :inherit link
:underline nil
))
693 "Face used for a source URL of a package."
694 :group
'guix-package-info-faces
)
696 (defcustom guix-package-info-auto-find-source nil
697 "If non-nil, find a source file after pressing a \"Show\" button.
698 If nil, just display the source file path without finding."
700 :group
'guix-package-info
)
702 (defcustom guix-package-info-auto-download-source t
703 "If nil, do not automatically download a source file if it doesn't exist.
704 After pressing a \"Show\" button, a derivation of the package
705 source is calculated and a store file path is displayed. If this
706 variable is non-nil and the source file does not exist in the
707 store, it will be automatically downloaded (with a possible
708 prompt depending on `guix-operation-confirm' variable)."
710 :group
'guix-package-info
)
712 (defvar guix-package-info-download-buffer nil
713 "Buffer from which a current download operation was performed.")
715 (define-button-type 'guix-package-source
717 'face
'guix-package-info-source
720 ;; As a source may not be a real URL (e.g., "mirror://..."),
721 ;; no action is bound to a source button.
722 (message "Yes, this is the source URL. What did you expect?")))
724 (defun guix-package-info-show-source (entry-id package-id
)
725 "Show file name of a package source in the current info buffer.
726 Find the file if needed (see `guix-package-info-auto-find-source').
727 ENTRY-ID is an ID of the current entry (package or output).
728 PACKAGE-ID is an ID of the package which source to show."
729 (let* ((entries guix-entries
)
730 (entry (guix-entry-by-id entry-id guix-entries
))
731 (file (guix-package-source-path package-id
)))
733 (error "Couldn't define file name of the package source"))
734 (let* ((new-entry (cons (cons 'source-file file
)
736 (new-entries (guix-replace-entry entry-id new-entry entries
)))
737 (setq guix-entries new-entries
)
738 (guix-buffer-redisplay-goto-button)
739 (if (file-exists-p file
)
740 (if guix-package-info-auto-find-source
741 (guix-find-file file
)
742 (message "The source store path is displayed."))
743 (if guix-package-info-auto-download-source
744 (guix-package-info-download-source package-id
)
745 (message "The source does not exist in the store."))))))
747 (defun guix-package-info-download-source (package-id)
748 "Download a source of the package PACKAGE-ID."
749 (setq guix-package-info-download-buffer
(current-buffer))
750 (guix-package-source-build-derivation
752 "The source does not exist in the store. Download it?"))
754 (defun guix-package-info-insert-source (source entry
)
755 "Insert SOURCE from package ENTRY at point.
756 SOURCE is a list of URLs."
758 (guix-format-insert nil
)
759 (let* ((source-file (guix-entry-value entry
'source-file
))
760 (entry-id (guix-entry-id entry
))
761 (package-id (or (guix-entry-value entry
'package-id
)
763 (if (null source-file
)
764 (guix-info-insert-action-button
767 (guix-package-info-show-source (button-get btn
'entry-id
)
768 (button-get btn
'package-id
)))
769 "Show the source store directory of the current package"
771 'package-id package-id
)
772 (unless (file-exists-p source-file
)
773 (guix-info-insert-action-button
776 (guix-package-info-download-source
777 (button-get btn
'package-id
)))
778 "Download the source into the store"
779 'package-id package-id
))
780 (guix-info-insert-value-indent source-file
'guix-file
))
781 (guix-info-insert-value-indent source
'guix-package-source
))))
783 (defun guix-package-info-redisplay-after-download ()
784 "Redisplay an 'info' buffer after downloading the package source.
785 This function is used to hide a \"Download\" button if needed."
786 (when (buffer-live-p guix-package-info-download-buffer
)
787 (with-current-buffer guix-package-info-download-buffer
788 (guix-buffer-redisplay-goto-button))
789 (setq guix-package-info-download-buffer nil
)))
791 (add-hook 'guix-after-source-download-hook
792 'guix-package-info-redisplay-after-download
)
795 ;;; Displaying outputs
797 (guix-ui-info-define-interface output
798 :buffer-name
"*Guix Package Info*"
799 :format
'((name format
(format guix-package-info-name
))
800 (version format guix-output-info-insert-version
)
801 (output format guix-output-info-insert-output
)
802 (synopsis simple
(indent guix-package-info-synopsis
))
803 (source simple guix-package-info-insert-source
)
804 (path simple
(indent guix-file
))
805 (dependencies simple
(indent guix-file
))
806 (location format
(format guix-package-location
))
807 (home-url format
(format guix-url
))
808 (license format
(format guix-package-info-license
))
809 (inputs format
(format guix-package-input
))
810 (native-inputs format
(format guix-package-native-input
))
811 (propagated-inputs format
812 (format guix-package-propagated-input
))
813 (description simple
(indent guix-package-info-description
)))
814 :titles guix-package-info-titles
815 :required
'(id package-id installed non-unique
))
817 (defun guix-output-info-insert-version (version entry
)
818 "Insert output VERSION and obsolete text if needed at point."
819 (guix-info-insert-value-format version
820 'guix-package-info-version
)
821 (and (guix-entry-value entry
'obsolete
)
822 (guix-package-info-insert-obsolete-text)))
824 (defun guix-output-info-insert-output (output entry
)
825 "Insert OUTPUT and action buttons at point."
826 (let* ((installed (guix-entry-value entry
'installed
))
827 (obsolete (guix-entry-value entry
'obsolete
))
828 (action-type (if installed
'delete
'install
)))
829 (guix-info-insert-value-format
832 'guix-package-info-installed-outputs
833 'guix-package-info-uninstalled-outputs
))
834 (guix-info-insert-indent)
835 (guix-package-info-insert-action-button action-type entry output
)
837 (guix-info-insert-indent)
838 (guix-package-info-insert-action-button 'upgrade entry output
))))
841 ;;; Displaying generations
843 (guix-ui-info-define-interface generation
844 :buffer-name
"*Guix Generation Info*"
845 :format
'((number format guix-generation-info-insert-number
)
846 (prev-number format
(format))
847 (current format guix-generation-info-insert-current
)
848 (path simple
(indent guix-file
))
849 (time format
(time)))
850 :titles
'((path .
"File name")
851 (prev-number .
"Previous number")))
853 (defface guix-generation-info-number
854 '((t :inherit font-lock-keyword-face
))
855 "Face used for a number of a generation."
856 :group
'guix-generation-info-faces
)
858 (defface guix-generation-info-current
859 '((t :inherit guix-package-info-installed-outputs
))
860 "Face used if a generation is the current one."
861 :group
'guix-generation-info-faces
)
863 (defface guix-generation-info-not-current
865 "Face used if a generation is not the current one."
866 :group
'guix-generation-info-faces
)
868 (defun guix-generation-info-insert-number (number &optional _
)
869 "Insert generation NUMBER and action buttons."
870 (guix-info-insert-value-format number
'guix-generation-info-number
)
871 (guix-info-insert-indent)
872 (guix-info-insert-action-button
875 (guix-get-show-entries guix-profile
'list guix-package-list-type
876 'generation
(button-get btn
'number
)))
877 "Show installed packages for this generation"
879 (guix-info-insert-indent)
880 (guix-info-insert-action-button
883 (guix-delete-generations guix-profile
(list (button-get btn
'number
))
885 "Delete this generation"
888 (defun guix-generation-info-insert-current (val entry
)
889 "Insert boolean value VAL showing whether this generation is current."
891 (guix-info-insert-value-format "Yes" 'guix-generation-info-current
)
892 (guix-info-insert-value-format "No" 'guix-generation-info-not-current
)
893 (guix-info-insert-indent)
894 (guix-info-insert-action-button
897 (guix-switch-to-generation guix-profile
(button-get btn
'number
)
899 "Switch to this generation (make it the current one)"
900 'number
(guix-entry-value entry
'number
))))
903 (defvar guix-info-font-lock-keywords
905 `((,(rx "(" (group "guix-info-define-interface")
909 (font-lock-add-keywords 'emacs-lisp-mode guix-info-font-lock-keywords
)
913 ;;; guix-info.el ends here