123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722 |
- ;;; guix-ui-package.el --- Interface for displaying packages -*- lexical-binding: t -*-
- ;; Copyright © 2014–2019, 2021 Alex Kost <alezost@gmail.com>
- ;; This file is part of Emacs-Guix.
- ;; Emacs-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.
- ;;
- ;; Emacs-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 Emacs-Guix. If not, see <http://www.gnu.org/licenses/>.
- ;;; Commentary:
- ;; This file provides an interface for displaying packages and outputs
- ;; in 'list' and 'info' buffers, and commands for working with them.
- ;;; Code:
- (require 'cl-lib)
- (require 'dash)
- (require 'bui)
- (require 'guix nil t)
- (require 'guix-ui)
- (require 'guix-ui-store-item)
- (require 'guix-misc)
- (require 'guix-repl)
- (require 'guix-guile)
- (require 'guix-utils)
- (require 'guix-read)
- (require 'guix-license)
- (require 'guix-location)
- (require 'guix-package)
- (require 'guix-profiles)
- (guix-ui-define-entry-type package)
- (guix-ui-define-entry-type output)
- (defcustom guix-package-list-type 'output
- "Define how to display packages in 'list' buffer.
- Should be a symbol `package' or `output' (if `output', display each
- output on a separate line; if `package', display each package on
- a separate line)."
- :type '(choice (const :tag "List of packages" package)
- (const :tag "List of outputs" output))
- :group 'guix-package)
- (defcustom guix-package-use-name-at-point t
- "If non-nil, \\[guix-packages-by-name] uses symbol at point as default
- if it is a package name.
- If nil, then no default name is used."
- :type 'boolean
- :group 'guix-package)
- (defun guix-package-list-type ()
- "Return BUI list entry-type by `guix-package-list-type' variable."
- (guix-make-symbol guix-package-list-type))
- ;; To avoid compilation warning: this variable is actually defined later
- ;; along with the rest "package-list" interface stuff.
- (defvar guix-package-list-show-single)
- (defun guix-package-get-display (profile search-type &rest search-values)
- "Search for packages/outputs and show results.
- If PROFILE is nil, use `guix-current-profile'.
- See `guix-ui-get-entries' for the meaning of SEARCH-TYPE and
- SEARCH-VALUES.
- Results are displayed in the list buffer, unless a single package
- is found and `guix-package-list-show-single' is nil."
- (let* ((args (cl-list* (guix-package-profile
- (or profile guix-current-profile))
- search-type search-values))
- (entries (bui-get-entries (guix-package-list-type) 'list args)))
- (if (or guix-package-list-show-single
- (null entries)
- (cdr entries))
- (bui-display-entries entries (guix-package-list-type) 'list args)
- (bui-get-display-entries 'guix-package 'info args))))
- (defun guix-package-entry->name-specification (entry &optional output)
- "Return name specification of the package ENTRY and OUTPUT."
- (guix-package-name-specification
- (bui-entry-non-void-value entry 'name)
- (bui-entry-non-void-value entry 'version)
- (or output (bui-entry-non-void-value entry 'output))))
- (defun guix-package-entries->name-specifications (entries)
- "Return name specifications by the package or output ENTRIES."
- (cl-remove-duplicates (mapcar #'guix-package-entry->name-specification
- entries)
- :test #'string=))
- (defun guix-package-entry-installed-outputs (entry)
- "Return a list of installed outputs for the package ENTRY."
- (--map (bui-entry-non-void-value it 'output)
- (bui-entry-non-void-value entry 'installed)))
- (defun guix-read-package-name-from-entries (entries)
- "Prompt for a package name and return it.
- Names are completed from package ENTRIES."
- (completing-read "Package: "
- (--map (bui-entry-value it 'name) entries)))
- (defun guix-read-package-entry-by-name (&optional entries)
- "Return an entry from package ENTRIES (current entries by default).
- If there is only one entry, return it. If there are multiple
- entries, prompt for a package name and return an entry with this
- name."
- (or entries (setq entries (bui-current-entries)))
- ;; There is unavoidable (?) downside: when there are several packages
- ;; with the same name, they cannot be distinguished, so the first one
- ;; from ENTRIES will be returned.
- (pcase entries
- (`(,entry) entry)
- (_ (bui-entry-by-param entries 'name
- (guix-read-package-name-from-entries entries)))))
- (defun guix-read-package-output (outputs)
- "Return an output from package OUTPUTS.
- If there is only one output, return it. If there are multiple
- OUTPUTS, prompt for it."
- (or outputs
- (error "No package outputs!"))
- (if (cdr outputs)
- (completing-read "Output: " outputs nil t)
- (car outputs)))
- (defun guix-read-package-entry-and-output (&optional entries)
- "Return a list with package entry and output.
- See `guix-read-package-entry-by-name' and
- `guix-read-package-output' for details."
- (let* ((entry (guix-read-package-entry-by-name entries))
- (outputs (bui-entry-non-void-value entry 'outputs))
- (output (guix-read-package-output outputs)))
- (list entry output)))
- ;;; Processing package actions
- (defvar guix-package-name-width 40
- "Width of a package name \"column\".
- This variable is used in a buffer to confirm operation.")
- (defun guix-process-package-actions (profile actions
- &optional operation-buffer)
- "Process package ACTIONS on PROFILE.
- Each action is a list of the form:
- (ACTION-TYPE PACKAGE-SPEC ...)
- ACTION-TYPE is one of the following symbols: `install',
- `upgrade', `remove'/`delete'.
- PACKAGE-SPEC should have the following form: (ID [OUTPUT] ...)."
- (let (install upgrade remove)
- (mapc (lambda (action)
- (let ((action-type (car action))
- (specs (cdr action)))
- (cl-case action-type
- (install (setq install (append install specs)))
- (upgrade (setq upgrade (append upgrade specs)))
- ((remove delete) (setq remove (append remove specs))))))
- actions)
- (when (guix-continue-package-operation-p
- profile
- :install install :upgrade upgrade :remove remove)
- (guix-eval-in-repl
- (guix-make-guile-expression
- 'process-package-actions profile
- :install install :upgrade upgrade :remove remove
- :use-substitutes? (or guix-use-substitutes 'f)
- :dry-run? (or guix-dry-run 'f))
- (and (not guix-dry-run) operation-buffer)))))
- (cl-defun guix-continue-package-operation-p (profile
- &key install upgrade remove)
- "Return non-nil if a package operation should be continued.
- Ask a user if needed (see `guix-operation-confirm').
- INSTALL, UPGRADE, REMOVE are 'package action specifications'.
- See `guix-process-package-actions' for details."
- (or (null guix-operation-confirm)
- (let* ((entries (guix-ui-get-entries
- profile 'package 'id
- (append (mapcar #'car install)
- (mapcar #'car upgrade)
- (mapcar #'car remove))
- '(id name version location)))
- (install-strings (guix-get-package-strings install entries))
- (upgrade-strings (guix-get-package-strings upgrade entries))
- (remove-strings (guix-get-package-strings remove entries)))
- (if (or install-strings upgrade-strings remove-strings)
- (let ((buf (get-buffer-create guix-temp-buffer-name)))
- (with-current-buffer buf
- (setq-local cursor-type nil)
- (setq buffer-read-only nil)
- (erase-buffer)
- (insert (propertize "Profile" 'face 'bold)
- ": " profile "\n\n")
- (guix-insert-package-strings install-strings "install")
- (guix-insert-package-strings upgrade-strings "upgrade")
- (guix-insert-package-strings remove-strings "remove")
- (let ((win (temp-buffer-window-show
- buf
- '((display-buffer-reuse-window
- display-buffer-at-bottom)
- (window-height . fit-window-to-buffer)))))
- (prog1 (guix-operation-prompt)
- (quit-window nil win)))))
- (message "Nothing to be done.
- If Guix REPL was restarted, the data is not up-to-date.")
- nil))))
- (defun guix-get-package-strings (specs entries)
- "Return short package descriptions for performing package actions.
- See `guix-process-package-actions' for the meaning of SPECS.
- ENTRIES is a list of package entries to get info about packages."
- (-non-nil
- (-map (-lambda ((id . outputs))
- (--when-let (bui-entry-by-id entries id)
- (let ((location (bui-entry-non-void-value it 'location))
- (name (guix-package-entry->name-specification it)))
- (with-temp-buffer
- (insert name)
- (when outputs
- (insert ":" (guix-concat-strings outputs ",")))
- (when location
- (indent-to guix-package-name-width 2)
- (insert "(" location ")"))
- (buffer-string)))))
- specs)))
- (defun guix-insert-package-strings (strings action)
- "Insert information STRINGS at point for performing package ACTION."
- (when strings
- (insert "Package(s) to " (propertize action 'face 'bold) ":\n")
- (dolist (str strings)
- (insert " " str "\n"))
- (bui-newline)))
- ;;; Package 'info'
- (guix-ui-define-interface package info
- :mode-name "Package-Info"
- :buffer-name "*Guix Package Info*"
- :get-entries-function 'guix-package-info-get-entries
- :format '(guix-package-info-insert-heading
- nil
- guix-package-info-insert-additional-text
- (synopsis nil (simple guix-package-info-synopsis))
- nil
- (description nil (simple guix-package-info-description))
- nil
- (outputs simple guix-package-info-insert-outputs)
- guix-package-info-insert-misc
- (source simple guix-package-info-insert-source)
- (location simple guix-package-info-insert-location)
- (upstream-name format (format))
- (home-url format (format bui-url))
- (license format (format guix-package-license))
- (systems format guix-package-info-insert-systems)
- (inputs format (guix-package-info-insert-name-buttons
- guix-package-input))
- (native-inputs format (guix-package-info-insert-name-buttons
- guix-package-native-input))
- (propagated-inputs format (guix-package-info-insert-name-buttons
- guix-package-propagated-input)))
- :hint 'guix-package-info-hint
- :titles '((home-url . "Home page")
- (systems . "Supported systems"))
- :required '(id name version installed non-unique known-status
- superseded hidden))
- ;; Additional info for installed outputs.
- (bui-define-interface guix-installed-output info
- :format '((file-name simple (guix-info-insert-store-items))
- (dependencies simple (guix-info-insert-store-items)))
- :titles '((file-name . "Store directory"))
- :reduced? t)
- ;; Additional info for "guix pull"-ed 'guix' package.
- (bui-define-interface guix-pulled-package info
- :format '((url format (format bui-url))
- (branch format (format))
- (commit format (format)))
- :reduced? t)
- (let ((map guix-package-info-mode-map))
- (define-key map (kbd "i") 'guix-package-info-install)
- (define-key map (kbd "d") 'guix-package-info-delete)
- (define-key map (kbd "U") 'guix-package-info-upgrade)
- (define-key map (kbd "u") 'guix-package-info-upgrade)
- (define-key map (kbd "e") 'guix-package-info-edit)
- (define-key map (kbd "G") 'guix-package-info-graph)
- (define-key map (kbd "z") 'guix-package-info-size)
- (define-key map (kbd "L") 'guix-package-info-lint))
- (defvar guix-package-info-default-hint
- '(("\\[guix-package-info-edit]") " edit (go to) the package definition;\n"
- ("\\[guix-package-info-graph]") " view package graph; "
- ("\\[guix-package-info-size]") " view package size; "
- ("\\[guix-package-info-lint]") " lint;\n"
- ("\\[guix-package-info-install]") " install; "
- ("\\[guix-package-info-delete]") " delete; "
- ("\\[guix-package-info-upgrade]") " upgrade;\n"))
- (defun guix-package-info-hint ()
- (bui-format-hints
- guix-package-info-default-hint
- guix-ui-hint
- (bui-default-hint)))
- (defface guix-package-info-heading
- '((t :inherit bui-info-heading))
- "Face for package name and version headings."
- :group 'guix-package-info-faces)
- (defface guix-package-info-name-button
- '((t :inherit button))
- "Face used for a full name that can be used to describe a package."
- :group 'guix-package-info-faces)
- ;; Currently, `guix-package-info-name' and `guix-package-info-version'
- ;; faces are not used, but they were in the past and may be used in
- ;; future, so they were not removed.
- (defface guix-package-info-name
- '((t :inherit font-lock-keyword-face))
- "Face used for a name of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-version
- '((t :inherit font-lock-builtin-face))
- "Face used for a version of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-synopsis
- '((((type tty pc) (class color)) :weight bold)
- (t :height 1.1 :weight bold :inherit variable-pitch))
- "Face used for a synopsis of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-description
- '((t :inherit font-lock-doc-face))
- "Face used for a description of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-license
- '((t :inherit font-lock-string-face))
- "Face used for a license of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-source
- '((t :inherit bui-url :underline nil))
- "Face used for a source URL of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-installed-outputs
- '((t :inherit guix-true))
- "Face used for installed outputs of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-uninstalled-outputs
- '((t :inherit guix-false))
- "Face used for uninstalled outputs of a package."
- :group 'guix-package-info-faces)
- (defface guix-package-info-unknown
- '((t :inherit error))
- "Face used for unknown packages.
- 'Unknown' means there are no packages with this name among Guix
- packages. Perhaps you installed this package in the past, and it
- was renamed later."
- :group 'guix-package-info-faces)
- (defface guix-package-info-obsolete
- '((t :inherit font-lock-warning-face))
- "Face used for obsolete packages.
- 'Obsolete' means there is a Guix package with this name but with
- a newer version (probably it's time to update)."
- :group 'guix-package-info-faces)
- (defface guix-package-info-future
- '((default :inherit guix-package-info-installed-outputs)
- (((class color) (min-colors 88) (background light))
- :foreground "RoyalBlue3")
- (((class color) (min-colors 88) (background dark))
- :foreground "DeepSkyBlue")
- (((class color) (min-colors 8))
- :foreground "blue"))
- "Face used for packages from the future.
- 'From the future' means there is a Guix package with this name
- but with an older version, i.e. the installed package is newer
- than available from Guix! This is rather unusual, it may happen,
- for example, if you installed a package after 'guix pull' (see
- Info node `(guix) Invoking guix pull') and then you removed the
- pulled directory, so Guix searches for packages in its original
- directory with the old package recipes."
- :group 'guix-package-info-faces)
- (defface guix-package-info-superseded
- '((t :inherit shadow))
- "Face used for superseded packages."
- :group 'guix-package-info-faces)
- (defface guix-package-info-hidden
- '((t :inherit shadow))
- "Face used for hidden packages.
- Most likely you will never see these packages (because they are
- hidden and hard to find), so you may not bother customizing this
- face."
- :group 'guix-package-info-faces)
- (defcustom guix-package-info-auto-find-package t
- "If non-nil, open store directory after pressing \"Show\" package button.
- If nil, just display the store directory (or directories) without finding."
- :type 'boolean
- :group 'guix-package-info)
- (defcustom guix-package-info-auto-find-source nil
- "If non-nil, open source file after pressing \"Show\" source button.
- If nil, just display the source file name without finding."
- :type 'boolean
- :group 'guix-package-info)
- (defcustom guix-package-info-auto-download-source t
- "If nil, do not automatically download a source file if it doesn't exist.
- After pressing a \"Show\" button, a derivation of the package
- source is calculated and a store file name is displayed. If this
- variable is non-nil and the source file does not exist in the
- store, it will be automatically downloaded (with a possible
- prompt depending on `guix-operation-confirm' variable)."
- :type 'boolean
- :group 'guix-package-info)
- (defcustom guix-package-info-button-functions
- '(guix-package-info-insert-build-button
- guix-package-info-insert-build-log-button
- guix-package-info-insert-graph-button
- guix-package-info-insert-size-button
- guix-package-info-insert-lint-button)
- "List of functions used to insert package buttons in Info buffer.
- Each function is called with 2 arguments: package ID and full name."
- :type '(repeat function)
- :group 'guix-package-info)
- (defvar guix-package-info-download-buffer nil
- "Buffer from which a current download operation was performed.")
- (defvar guix-package-info-output-format "%-8s "
- "String used to format output names of the packages.
- It should be a '%s'-sequence. After inserting an output name
- formatted with this string, an action button is inserted.")
- (define-button-type 'guix-package-license
- :supertype 'bui
- 'face 'guix-package-info-license
- 'help-echo "Display license info"
- 'action (lambda (btn)
- (require 'guix-ui-license)
- (bui-get-display-entries
- 'guix-license 'info
- (list 'name (button-label btn))
- 'add)))
- (defun guix-package-button-action (button)
- "Display package info for package BUTTON."
- (let ((search-type 'id)
- (search-value (or (button-get button 'id)
- (button-get button 'spec)
- (button-label button))))
- (if (eq major-mode 'guix-package-info-mode)
- (bui-get-display-entries-current
- 'guix-package 'info
- (list (guix-ui-current-profile)
- search-type search-value))
- (bui-get-display-entries
- 'guix-package 'info
- (list guix-current-profile
- search-type search-value)))))
- (define-button-type 'guix-package-name
- :supertype 'bui
- 'face 'guix-package-info-name-button
- 'help-echo "Describe this package"
- 'action 'guix-package-button-action)
- (define-button-type 'guix-package-heading
- :supertype 'guix-package-name
- 'face 'guix-package-info-heading)
- (define-button-type 'guix-package-source
- :supertype 'bui
- 'face 'guix-package-info-source
- 'help-echo ""
- 'action (lambda (_)
- ;; As a source may not be a real URL (e.g., "mirror://..."),
- ;; no action is bound to a source button.
- (message "Yes, this is the source URL. What did you expect?")))
- (defun guix-package-info-get-entries (profile search-type
- &rest search-values)
- "Return 'package' entries for displaying them in 'info' buffer."
- (guix-eval-read
- (guix-make-guile-expression
- 'package/output-sexps
- profile 'package search-type search-values
- (cl-union guix-package-info-required-params
- (bui-info-displayed-params 'guix-package)))))
- (defun guix-package-info-insert-heading (entry)
- "Insert package ENTRY heading (name and version) at point."
- (bui-insert-button
- (concat (bui-entry-non-void-value entry 'name) " "
- (bui-entry-non-void-value entry 'version))
- 'guix-package-heading
- 'spec (guix-package-entry->name-specification entry))
- (bui-newline))
- (defun guix-package-info-insert-location (location &optional _)
- "Insert package LOCATION at point."
- (bui-insert-non-nil location
- (bui-info-insert-value-indent location 'guix-location)
- (let ((location-file (guix-location-file location)))
- ;; Do not show "Packages" button if a package 'from file' is displayed.
- (unless (eq (guix-ui-current-search-type) 'from-file)
- (bui-insert-indent)
- (bui-insert-action-button
- "Packages"
- (lambda (btn)
- (guix-package-get-display (guix-ui-current-profile)
- 'location
- (button-get btn 'location)))
- (format "Display packages from location '%s'" location-file)
- 'location location-file)))))
- (defun guix-package-info-insert-systems (systems entry)
- "Insert supported package SYSTEMS at point."
- (if (require 'build-farm-build nil t)
- (bui-info-insert-value-format
- systems 'build-farm-system
- 'job (build-farm-job-name-specification
- (bui-entry-non-void-value entry 'name)
- (bui-entry-non-void-value entry 'version)))
- (bui-info-insert-value-format systems)))
- (defmacro guix-package-info-define-insert-inputs (&optional type)
- "Define a face and a button for package inputs.
- TYPE is a type of inputs.
- Button name is `guix-package-TYPE-input'.
- Face name is `guix-package-info-TYPE-inputs'."
- (let* ((type-str (symbol-name type))
- (type-name (and type (concat type-str "-")))
- (type-desc (and type (concat type-str " ")))
- (face (intern (concat "guix-package-info-" type-name "inputs")))
- (btn (intern (concat "guix-package-" type-name "input"))))
- `(progn
- (defface ,face
- '((t :inherit guix-package-info-name-button))
- ,(concat "Face used for " type-desc "inputs of a package.")
- :group 'guix-package-info-faces)
- (define-button-type ',btn
- :supertype 'guix-package-name
- 'face ',face))))
- (guix-package-info-define-insert-inputs)
- (guix-package-info-define-insert-inputs native)
- (guix-package-info-define-insert-inputs propagated)
- (defun guix-package-info-insert-name-buttons (values &optional button)
- "Insert package name buttons at point.
- Each element from VALUES should either be a specification string
- or (id spec) list."
- (bui-insert-non-nil values
- (let* ((text (with-temp-buffer
- (bui-mapinsert
- (lambda (value)
- (let ((spec (if (listp value)
- (cadr value)
- value)))
- (bui-insert-button
- spec (or button 'guix-package-name)
- 'id value)))
- values
- bui-list-separator)
- (buffer-substring (point-min) (point-max))))
- (strings (bui-split-string
- text
- (- (bui-fill-column)
- (length bui-info-multiline-prefix)))))
- (bui-mapinsert #'insert
- strings
- (concat "\n" bui-info-multiline-prefix)))))
- (defun guix-package-info-insert-name-button (value &optional button)
- "Insert package name button at point."
- (guix-package-info-insert-name-buttons (list value) button))
- (defun guix-package-info-insert-outputs (outputs entry)
- "Insert OUTPUTS from package ENTRY at point."
- (bui-newline)
- (dolist (output outputs)
- (guix-package-info-insert-output output entry)))
- (defun guix-package-info-insert-additional-text (entry)
- "Insert some additional info for package ENTRY at point."
- (let ((name (bui-entry-non-void-value entry 'name)))
- (if (bui-entry-non-void-value entry 'hidden)
- (progn
- (guix-package-info-insert-hidden-text)
- (bui-newline))
- (cl-case (bui-entry-non-void-value entry 'known-status)
- (known
- (when (bui-entry-non-void-value entry 'non-unique)
- (guix-package-info-insert-non-unique-text
- (guix-package-entry->name-specification entry))
- (bui-newline))
- (--when-let (bui-entry-non-void-value entry 'superseded)
- (guix-package-info-insert-superseded-text it)
- (bui-newline)))
- (obsolete (guix-package-info-insert-obsolete-text name))
- (unknown (guix-package-info-insert-unknown-text name))
- (future (guix-package-info-insert-future-text name))
- (pull (guix-package-info-insert-pull-text name entry))))))
- (defun guix-package-info-insert-unknown-text (name)
- "Insert a message about unknown package at point."
- (insert "This package is ")
- (bui-format-insert "unknown" 'guix-package-info-unknown)
- (insert ", i.e. there are no packages with\n"
- "'" name "' name among the available package recipes."))
- (defun guix-package-info-insert-obsolete-text (name)
- "Insert a message about obsolete package NAME at point."
- (insert "This package is ")
- (bui-format-insert "obsolete" 'guix-package-info-obsolete)
- (insert ", i.e. a newer version of\n")
- (bui-insert-button name 'guix-package-name)
- (insert " package is available."))
- (defun guix-package-info-insert-future-text (name)
- "Insert a message about package NAME with a future VERSION at point."
- (insert "This package is ")
- (bui-format-insert "from the future" 'guix-package-info-future)
- (insert ", i.e. this (installed) package
- is newer than the available package recipe for ")
- (bui-insert-button name 'guix-package-name)
- (insert "."))
- (defun guix-package-info-insert-pull-text (name entry)
- "Insert a message that NAME package was 'guix pull'-ed."
- (insert "This ")
- (bui-insert-button name 'guix-package-name)
- (insert " package was installed by 'guix pull' command")
- (let ((repository (bui-entry-non-void-value entry 'repository)))
- (if (not repository)
- (insert ".")
- (insert "\nfrom the following repository:\n\n")
- (bui-info-insert-entry repository 'guix-pulled-package 1))))
- (defun guix-package-info-insert-non-unique-text (full-name)
- "Insert a message about non-unique package with FULL-NAME at point."
- (insert "Installed outputs are displayed for a non-unique ")
- (bui-insert-button full-name 'guix-package-name)
- (insert " package.")
- (bui-newline))
- (defun guix-package-info-insert-superseded-text (full-name)
- "Insert a message that current package is superseded by FULL-NAME."
- (insert "This package is ")
- (bui-format-insert "superseded" 'guix-package-info-superseded)
- (insert " by ")
- (bui-insert-button full-name 'guix-package-name)
- (insert " package.")
- (bui-newline))
- (defun guix-package-info-insert-hidden-text ()
- "Insert a message that current package is hidden."
- (insert "This package is ")
- (bui-format-insert "hidden" 'guix-package-info-hidden)
- (insert ", so do not tell anyone about it ;-)\n"))
- (defun guix-package-info-insert-output (output entry)
- "Insert OUTPUT at point.
- Make some fancy text with buttons and additional stuff if the
- current OUTPUT is installed (if there is such output in
- `installed' parameter of a package ENTRY)."
- (let* ((installed (bui-entry-non-void-value entry 'installed))
- (obsolete (eq (bui-entry-non-void-value entry 'known-status)
- 'obsolete))
- (installed-entry (--find
- (string= (bui-entry-non-void-value it 'output)
- output)
- installed))
- (action-type (if installed-entry 'delete 'install))
- (profile (guix-ui-current-profile)))
- (bui-insert-indent)
- (bui-format-insert output
- (if installed-entry
- 'guix-package-info-installed-outputs
- 'guix-package-info-uninstalled-outputs)
- guix-package-info-output-format)
- ;; Do not allow a user to install/delete anything to/from a system
- ;; profile or "guix pull"-ed profile, so add action buttons only for
- ;; usual profiles.
- (when (and profile
- (not (guix-system-profile? profile))
- (not (guix-pulled-profile? profile)))
- (guix-package-info-insert-action-button action-type entry output)
- (when obsolete
- (bui-insert-indent)
- (guix-package-info-insert-action-button 'upgrade entry output)))
- (bui-newline)
- (when installed-entry
- (bui-info-insert-entry installed-entry 'guix-installed-output 2))))
- (defun guix-package-info-insert-action-button (type entry output)
- "Insert button to process an action on a package OUTPUT at point.
- TYPE is one of the following symbols: `install', `delete', `upgrade'.
- ENTRY is an alist with package info."
- (let ((type-str (capitalize (symbol-name type)))
- (full-name (guix-package-entry->name-specification entry output)))
- (bui-insert-action-button
- type-str
- (lambda (btn)
- (guix-process-package-actions
- (guix-ui-current-profile)
- `((,(button-get btn 'action-type) (,(button-get btn 'id)
- ,(button-get btn 'output))))
- (current-buffer)))
- (concat type-str " '" full-name "'")
- 'action-type type
- 'id (or (bui-entry-non-void-value entry 'package-id)
- (bui-entry-id entry))
- 'output output)))
- (defun guix-package-info-show-store-path (entry-id package-id)
- "Show store directories of the package outputs in the current buffer.
- ENTRY-ID is an ID of the current entry (package or output).
- PACKAGE-ID is an ID of the package which store path to show."
- (let* ((entries (bui-current-entries))
- (entry (bui-entry-by-id entries entry-id))
- (dirs (guix-package-store-path package-id)))
- (or dirs
- (error "Couldn't define store directory of the package"))
- (let* ((new-entry (cons (cons 'store-path dirs)
- entry))
- (new-entries (bui-replace-entry entries entry-id new-entry)))
- (setf (bui-item-entries bui-item)
- new-entries)
- (bui-redisplay-goto-button)
- (let ((dir (car dirs)))
- (if (file-exists-p dir)
- (if guix-package-info-auto-find-package
- (find-file dir)
- (message nil))
- (message "'%s' does not exist.\nTry to build this package."
- dir))))))
- (defun guix-package-info-insert-misc (entry)
- "Insert various buttons and other info for package ENTRY at point."
- (when (eq (bui-entry-non-void-value entry 'known-status)
- 'known)
- (let* ((entry-id (bui-entry-id entry))
- (package-id (or (bui-entry-non-void-value entry 'package-id)
- entry-id))
- (full-name (guix-package-entry->name-specification entry))
- (store-path (bui-entry-non-void-value entry 'store-path)))
- (bui-info-insert-title-simple "Package")
- (if store-path
- (bui-info-insert-value-indent store-path 'bui-file)
- (bui-insert-action-button
- "Show"
- (lambda (btn)
- (guix-package-info-show-store-path
- (button-get btn 'entry-id)
- (button-get btn 'package-id)))
- "Show the store directory of the current package"
- 'entry-id entry-id
- 'package-id package-id))
- (when guix-package-info-button-functions
- (bui-newline)
- (bui-mapinsert (lambda (fun)
- (funcall fun package-id full-name))
- guix-package-info-button-functions
- (bui-get-indent)
- :indent bui-indent
- :column (bui-fill-column)))
- (bui-newline))))
- (defun guix-package-info-insert-build-button (id full-name)
- "Insert button to build a package defined by ID."
- (bui-insert-action-button
- "Build"
- (lambda (btn)
- (guix-build-package (button-get btn 'id)
- (format "Build '%s' package?" full-name)))
- "Build the current package"
- 'id id))
- (defun guix-package-info-insert-build-log-button (id _name)
- "Insert button to show build log of a package defined by ID."
- (bui-insert-action-button
- "Build Log"
- (lambda (btn)
- (guix-package-find-build-log (button-get btn 'id)))
- "View build log of the current package"
- 'id id))
- (declare-function guix-package-graph "guix-graph" t)
- (defun guix-package-info-insert-graph-button (id _name)
- "Insert button to show a graph of a package defined by ID."
- (bui-insert-action-button
- "Graph"
- (lambda (btn)
- (guix-package-graph (button-get btn 'id)
- (guix-read-graph-backend)
- (guix-read-graph-node-type)))
- "View graph of the current package"
- 'id id))
- (defun guix-package-info-insert-size-button (_id name)
- "Insert button to show a size of a package NAME."
- (bui-insert-action-button
- "Size"
- (lambda (btn)
- (guix-package-size (button-get btn 'name)
- (guix-read-package-size-type)))
- (format "View size of '%s' package" name)
- 'name name))
- (defun guix-package-info-insert-lint-button (id _name)
- "Insert button to lint a package defined by ID."
- (bui-insert-action-button
- "Lint"
- (lambda (btn)
- (guix-package-lint (list (button-get btn 'id))
- (and current-prefix-arg
- (guix-read-lint-checker-names))))
- "Lint the current package"
- 'id id))
- (defun guix-package-info-show-source (entry-id package-id)
- "Show file name of a package source in the current info buffer.
- Find the file if needed (see `guix-package-info-auto-find-source').
- ENTRY-ID is an ID of the current entry (package or output).
- PACKAGE-ID is an ID of the package which source to show."
- (let* ((entries (bui-current-entries))
- (entry (bui-entry-by-id entries entry-id))
- (file (guix-package-source-file-name package-id)))
- (or file
- (error "Couldn't define file name of the package source"))
- (let* ((new-entry (cons (cons 'source-file file)
- entry))
- (new-entries (bui-replace-entry entries entry-id new-entry)))
- (setf (bui-item-entries bui-item)
- new-entries)
- (bui-redisplay-goto-button)
- (if (file-exists-p file)
- (if guix-package-info-auto-find-source
- (guix-find-file file)
- (message "The file name of the package source is displayed."))
- (if guix-package-info-auto-download-source
- (guix-package-info-download-source package-id)
- (message "The source does not exist in the store."))))))
- (defun guix-package-info-download-source (package-id)
- "Download a source of the package PACKAGE-ID."
- (setq guix-package-info-download-buffer (current-buffer))
- (guix-package-source-build-derivation
- package-id
- "The source does not exist in the store. Download it?"))
- (defun guix-package-info-insert-source (source entry)
- "Insert SOURCE from package ENTRY at point.
- SOURCE is a list of URLs."
- (bui-insert-non-nil source
- (let* ((source-file (bui-entry-non-void-value entry 'source-file))
- (entry-id (bui-entry-id entry))
- (package-id (or (bui-entry-non-void-value entry 'package-id)
- entry-id)))
- (if (null source-file)
- (bui-insert-action-button
- "Show"
- (lambda (btn)
- (guix-package-info-show-source (button-get btn 'entry-id)
- (button-get btn 'package-id)))
- "Show the source store directory of the current package"
- 'entry-id entry-id
- 'package-id package-id)
- (unless (file-exists-p source-file)
- (bui-insert-action-button
- "Download"
- (lambda (btn)
- (guix-package-info-download-source
- (button-get btn 'package-id)))
- "Download the source into the store"
- 'package-id package-id))
- (bui-info-insert-value-indent source-file 'bui-file))
- (bui-info-insert-value-indent source 'guix-package-source))))
- (defun guix-package-info-redisplay-after-download ()
- "Redisplay an 'info' buffer after downloading the package source.
- This function is used to hide a \"Download\" button if needed."
- (when (buffer-live-p guix-package-info-download-buffer)
- (with-current-buffer guix-package-info-download-buffer
- (bui-redisplay-goto-button))
- (setq guix-package-info-download-buffer nil)))
- (add-hook 'guix-after-source-download-hook
- 'guix-package-info-redisplay-after-download)
- (defun guix-package-entry-ensure-known (entry)
- "Signal an error if package ENTRY is unknown."
- (unless (eq (bui-entry-non-void-value entry 'known-status)
- 'known)
- (error "This command is not available for obsolete or unknown packages")))
- (defun guix-package-info-edit (entry &optional directory)
- "Go to the location of the package ENTRY.
- See `guix-find-location' for the meaning of DIRECTORY."
- (interactive
- (let ((entry (guix-read-package-entry-by-name)))
- (guix-package-entry-ensure-known entry)
- (list entry
- (guix-read-directory))))
- (guix-find-location (bui-entry-non-void-value entry 'location)
- directory))
- (defun guix-package-info-graph (entry backend node-type)
- "Show BACKEND/NODE-TYPE graph for the package ENTRY."
- (interactive
- (list (guix-read-package-entry-by-name)
- (guix-read-graph-backend)
- (guix-read-graph-node-type)))
- (guix-package-graph (if (eq (bui-entry-non-void-value entry 'known-status)
- 'known)
- (bui-entry-id entry)
- (bui-entry-non-void-value entry 'name))
- backend node-type))
- (defun guix-package-info-size (entry &optional type)
- "Show size of the package ENTRY.
- See `guix-package-size' for the meaning of TYPE."
- (interactive
- (list (guix-read-package-entry-by-name)
- (guix-read-package-size-type)))
- (guix-package-size (if (eq (bui-entry-non-void-value entry 'known-status)
- 'known)
- (guix-package-entry->name-specification entry)
- ;; Take file name of the first output.
- ;; FIXME It is better to ask for an output, if
- ;; there are several outputs.
- (bui-entry-value
- (car (bui-entry-value entry 'installed))
- 'file-name))
- type))
- (defun guix-package-info-lint (entry &optional checkers)
- "Lint the package ENTRY.
- Interactively with prefix, prompt for CHECKERS.
- See `guix-package-lint' for details."
- (interactive
- (let ((entry (guix-read-package-entry-by-name)))
- (guix-package-entry-ensure-known entry)
- (list entry
- (and current-prefix-arg
- (guix-read-lint-checker-names)))))
- (guix-package-lint (list (bui-entry-id entry)) checkers))
- (defun guix-package-info-install (entry output)
- "Install package OUTPUT to the current profile.
- Interactively, prompt for package ENTRY and OUTPUT (if there are
- more than one)."
- (interactive (guix-read-package-entry-and-output))
- (if (member output (guix-package-entry-installed-outputs entry))
- (user-error "'%s' is already installed"
- (guix-package-entry->name-specification entry output))
- (guix-process-package-actions
- (guix-ui-current-profile)
- `((install (,(bui-entry-id entry) ,output)))
- (current-buffer))))
- (defun guix-package-info-delete (entry output)
- "Delete package OUTPUT from the current profile.
- Interactively, prompt for package ENTRY and OUTPUT (if there are
- more than one)."
- (interactive (guix-read-package-entry-and-output))
- (if (member output (guix-package-entry-installed-outputs entry))
- (guix-process-package-actions
- (guix-ui-current-profile)
- `((delete (,(bui-entry-id entry) ,output)))
- (current-buffer))
- (user-error "'%s' cannot be deleted as it is not installed"
- (guix-package-entry->name-specification entry output))))
- (defun guix-package-info-upgrade (entry output)
- "Upgrade package OUTPUT in the current profile.
- Interactively, prompt for package ENTRY and OUTPUT (if there are
- more than one)."
- (interactive (guix-read-package-entry-and-output))
- (if (member output (guix-package-entry-installed-outputs entry))
- (when (or (eq (bui-entry-non-void-value entry 'known-status)
- 'obsolete)
- (y-or-n-p
- (format "'%s' is not obsolete. Try to upgrade it anyway? "
- (guix-package-entry->name-specification
- entry output))))
- (guix-process-package-actions
- (guix-ui-current-profile)
- `((upgrade (,(bui-entry-id entry) ,output)))
- (current-buffer)))
- (when (y-or-n-p
- (format "'%s' is not installed. Install it? "
- (guix-package-entry->name-specification entry output)))
- (guix-process-package-actions
- (guix-ui-current-profile)
- `((install (,(bui-entry-id entry) ,output)))
- (current-buffer)))))
- ;;; Package 'list'
- (guix-ui-define-interface package list
- :mode-name "Package-List"
- :buffer-name "*Guix Packages*"
- :get-entries-function 'guix-package-list-get-entries
- :describe-function 'guix-ui-list-describe
- :format '((name guix-package-list-get-name 20 t)
- (version nil 10 nil)
- (outputs nil 13 t)
- (installed guix-package-list-get-installed-outputs 13 t)
- (synopsis bui-list-get-one-line 30 nil))
- :required '(id known-status superseded hidden)
- :hint 'guix-package-list-hint
- :sort-key '(name)
- :marks '((install . ?I)
- (upgrade . ?U)
- (delete . ?D)))
- (let ((map guix-package-list-mode-map))
- (define-key map (kbd "B") 'guix-package-list-latest-builds)
- (define-key map (kbd "G") 'guix-package-list-graph)
- (define-key map (kbd "z") 'guix-package-list-size)
- (define-key map (kbd "L") 'guix-package-list-lint)
- (define-key map (kbd "e") 'guix-package-list-edit)
- (define-key map (kbd "x") 'guix-package-list-execute)
- (define-key map (kbd "i") 'guix-package-list-mark-install)
- (define-key map (kbd "d") 'guix-package-list-mark-delete)
- (define-key map (kbd "U") 'guix-package-list-mark-upgrade)
- (define-key map (kbd "^") 'guix-package-list-mark-upgrades))
- (defface guix-package-list-installed
- '((t :inherit guix-package-info-installed-outputs))
- "Face used for installed packages."
- :group 'guix-package-list-faces)
- (defface guix-package-list-unknown
- '((t :inherit guix-package-info-unknown))
- "Face used for unknown packages.
- See `guix-package-info-unknown' face for details."
- :group 'guix-package-list-faces)
- (defface guix-package-list-obsolete
- '((t :inherit guix-package-info-obsolete))
- "Face used for obsolete packages.
- See `guix-package-info-obsolete' face for details."
- :group 'guix-package-list-faces)
- (defface guix-package-list-future
- '((t :inherit guix-package-info-future))
- "Face used for packages from the future.
- See `guix-package-info-future' face for details."
- :group 'guix-package-list-faces)
- (defface guix-package-list-superseded
- '((t :inherit guix-package-info-superseded))
- "Face used for superseded packages.
- See `guix-package-info-superseded' face for details."
- :group 'guix-package-list-faces)
- (defface guix-package-list-hidden
- '((t :inherit guix-package-info-hidden))
- "Face used for hidden packages.
- See `guix-package-info-hidden' face for details."
- :group 'guix-package-list-faces)
- (defcustom guix-package-list-generation-marking-enabled nil
- "If non-nil, allow putting marks in a list with 'generation packages'.
- By default this is disabled, because it may be confusing. For
- example, a package is installed in some generation, so a user can
- mark it for deletion in the list of packages from this
- generation, but the package may not be installed in the latest
- generation, so actually it cannot be deleted.
- If you managed to understand the explanation above or if you
- really know what you do or if you just don't care, you can set
- this variable to t. It should not do much harm anyway (most
- likely)."
- :type 'boolean
- :group 'guix-package-list)
- (defvar guix-package-list-default-hint
- '(("\\[guix-package-list-edit]") " edit (go to) the package definition;\n"
- ("\\[guix-package-list-graph]") " view package graph; "
- ("\\[guix-package-list-size]") " view package size; "
- ("\\[guix-package-list-lint]") " lint;\n"
- ("\\[guix-package-list-mark-install]") " mark for installation; "
- ("\\[guix-package-list-mark-delete]") " mark for deletion;\n"
- ("\\[guix-package-list-mark-upgrade]") " mark for upgrading; "
- ("\\[guix-package-list-mark-upgrades]") " mark all for upgrading;\n"
- ("\\[guix-package-list-execute]") " execute operation;\n"))
- (defun guix-package-list-hint ()
- (bui-format-hints
- guix-package-list-default-hint
- guix-ui-hint
- (bui-default-hint)))
- (defun guix-package-list-get-entries (profile search-type
- &rest search-values)
- "Return 'package' entries for displaying them in 'list' buffer."
- (guix-eval-read
- (guix-make-guile-expression
- 'package/output-sexps
- profile 'package search-type search-values
- (cl-union guix-package-list-required-params
- (bui-list-displayed-params 'guix-package)))))
- (defun guix-package-list-get-name (name entry)
- "Return NAME of the package ENTRY.
- Colorize it with an appropriate face if needed."
- (bui-get-string
- name
- (if (bui-entry-non-void-value entry 'hidden)
- 'guix-package-list-hidden
- (cl-case (bui-entry-non-void-value entry 'known-status)
- ((known nil)
- (cond
- ((bui-entry-non-void-value entry 'superseded)
- 'guix-package-list-superseded)
- ((bui-entry-non-void-value entry 'installed)
- 'guix-package-list-installed)))
- (obsolete 'guix-package-list-obsolete)
- (unknown 'guix-package-list-unknown)
- (future 'guix-package-list-future)))))
- (defun guix-package-list-get-installed-outputs (installed &optional _)
- "Return string with outputs from INSTALLED entries."
- (bui-get-string
- (--map (bui-entry-non-void-value it 'output)
- installed)))
- (defun guix-package-list-marking-check ()
- "Signal an error if marking is disabled for the current buffer."
- (when (and (not guix-package-list-generation-marking-enabled)
- (or (derived-mode-p 'guix-package-list-mode)
- (derived-mode-p 'guix-output-list-mode))
- (eq (guix-ui-current-search-type) 'generation))
- (error "Action marks are disabled for lists of 'generation packages'")))
- (defun guix-package-list-mark-outputs (mark default
- &optional prompt available)
- "Mark the current package with MARK and move to the next line.
- If PROMPT is non-nil, use it to ask a user for outputs from
- AVAILABLE list, otherwise mark all DEFAULT outputs."
- (let ((outputs (if prompt
- (guix-completing-read-multiple
- prompt available nil t)
- default)))
- (apply #'bui-list--mark mark t outputs)))
- (defun guix-package-list-mark-install (&optional arg)
- "Mark the current package for installation and move to the next line.
- With ARG, prompt for the outputs to install (several outputs may
- be separated with \",\")."
- (interactive "P")
- (guix-package-list-marking-check)
- (let* ((entry (bui-list-current-entry))
- (all (bui-entry-non-void-value entry 'outputs))
- (installed (guix-package-entry-installed-outputs entry))
- (available (cl-set-difference all installed :test #'string=)))
- (or available
- (user-error "This package is already installed"))
- (guix-package-list-mark-outputs
- 'install '("out")
- (and arg "Output(s) to install: ")
- available)))
- (defun guix-package-list-mark-delete (&optional arg)
- "Mark the current package for deletion and move to the next line.
- With ARG, prompt for the outputs to delete (several outputs may
- be separated with \",\")."
- (interactive "P")
- (guix-package-list-marking-check)
- (let* ((entry (bui-list-current-entry))
- (installed (guix-package-entry-installed-outputs entry)))
- (or installed
- (user-error "This package is not installed"))
- (guix-package-list-mark-outputs
- 'delete installed
- (and arg "Output(s) to delete: ")
- installed)))
- (defun guix-package-list-mark-upgrade (&optional arg)
- "Mark the current package for upgrading and move to the next line.
- With ARG, prompt for the outputs to upgrade (several outputs may
- be separated with \",\")."
- (interactive "P")
- (guix-package-list-marking-check)
- (let* ((entry (bui-list-current-entry))
- (installed (guix-package-entry-installed-outputs entry)))
- (or installed
- (user-error "This package is not installed"))
- (when (or (eq (bui-entry-non-void-value entry 'known-status)
- 'obsolete)
- (y-or-n-p "This package is not obsolete. Try to upgrade it anyway? "))
- (guix-package-list-mark-outputs
- 'upgrade installed
- (and arg "Output(s) to upgrade: ")
- installed))))
- (defun guix-package-mark-upgrades (fun &optional all)
- "Mark all obsolete packages for upgrading.
- Use FUN to perform marking of the current line. FUN should
- take an entry as argument.
- If ALL is non-nil, mark all installed (not only obsolete) packages."
- (guix-package-list-marking-check)
- (let ((entries (--filter (if all
- (bui-entry-non-void-value it 'installed)
- (eq (bui-entry-non-void-value it 'known-status)
- 'obsolete))
- (bui-current-entries))))
- (bui-list-for-each-line
- (lambda ()
- (let* ((id (bui-list-current-id))
- (entry (--find (equal id (bui-entry-id it))
- entries)))
- (when entry
- (funcall fun entry)))))))
- (defun guix-package-list-mark-upgrades (&optional arg)
- "Mark all obsolete packages for upgrading.
- With ARG, mark all installed (including non-obsolete) packages."
- (interactive "P")
- (guix-package-mark-upgrades
- (lambda (entry)
- (apply #'bui-list--mark
- 'upgrade nil
- (guix-package-entry-installed-outputs entry)))
- arg))
- (defun guix-package-assert-non-system-profile ()
- "Verify that the current profile is not a system one.
- The current profile is the one used by the current buffer."
- (--when-let (guix-ui-current-profile)
- (guix-assert-non-system-profile it)))
- (defun guix-package-execute-actions (fun)
- "Perform actions on the marked packages.
- Use FUN to define actions suitable for `guix-process-package-actions'.
- FUN should take action-type as argument."
- (guix-package-assert-non-system-profile)
- (let ((actions (-non-nil (mapcar fun '(install delete upgrade)))))
- (if actions
- (guix-process-package-actions (guix-ui-current-profile)
- actions (current-buffer))
- (user-error "No operations specified"))))
- (defun guix-package-list-execute ()
- "Perform actions on the marked packages."
- (interactive)
- (guix-package-execute-actions #'guix-package-list-make-action))
- (defun guix-package-list-make-action (action-type)
- "Return action specification for the packages marked with ACTION-TYPE.
- Return nil, if there are no packages marked with ACTION-TYPE.
- The specification is suitable for `guix-process-package-actions'."
- (let ((specs (bui-list-get-marked-args action-type)))
- (and specs (cons action-type specs))))
- (defun guix-package-list-edit (&optional directory)
- "Go to the location of the current package.
- See `guix-find-location' for the meaning of DIRECTORY."
- (interactive (list (guix-read-directory)))
- (guix-find-package-definition (bui-list-current-id) directory))
- (defun guix-package-list-graph (backend node-type)
- "Show BACKEND/NODE-TYPE graph for the current package."
- (interactive
- (list (guix-read-graph-backend)
- (guix-read-graph-node-type)))
- (let ((entry (bui-list-current-entry)))
- (guix-package-graph (if (eq (bui-entry-non-void-value entry 'known-status)
- 'known)
- (bui-list-current-id)
- (bui-entry-non-void-value entry 'name))
- backend node-type)))
- (defun guix-package-list-size (&optional type)
- "Show size of the current package.
- See `guix-package-size' for the meaning of TYPE."
- (interactive (list (guix-read-package-size-type)))
- (guix-package-size (guix-package-entry->name-specification
- (bui-list-current-entry))
- type))
- (defun guix-package-list-lint (&optional checkers)
- "Lint marked (or the current) packages.
- Interactively with prefix, prompt for CHECKERS.
- See `guix-package-lint' for details."
- (interactive
- (list (and current-prefix-arg
- (guix-read-lint-checker-names))))
- (guix-package-lint (bui-list-marked-or-current) checkers))
- (declare-function build-farm-build-latest-prompt-args "build-farm-build" t)
- (declare-function build-farm-latest-builds "build-farm-build" t)
- (declare-function build-farm-job-name-specification "build-farm" t)
- (defun guix-package-list-latest-builds (number &rest args)
- "Display latest NUMBER of `build-farm' builds of the current package.
- Interactively, with prefix argument, prompt for NUMBER and ARGS."
- (interactive
- (let ((entry (bui-list-current-entry)))
- (guix-assert-build-farm)
- (require 'build-farm-build)
- (build-farm-build-latest-prompt-args
- :job (build-farm-job-name-specification
- (bui-entry-non-void-value entry 'name)
- (bui-entry-non-void-value entry 'version)))))
- (guix-assert-build-farm)
- (apply #'build-farm-latest-builds number args))
- ;;; Output 'list'
- (guix-ui-define-interface output list
- :mode-name "Output-List"
- :buffer-name "*Guix Packages*"
- :get-entries-function 'guix-output-list-get-entries
- :describe-function 'guix-output-list-describe
- :format '((name guix-package-list-get-name 20 t)
- (version nil 10 nil)
- (output nil 9 t)
- (installed nil 12 t)
- (synopsis bui-list-get-one-line 30 nil))
- :required '(id package-id known-status superseded hidden)
- :hint 'guix-output-list-hint
- :sort-key '(name)
- :marks '((install . ?I)
- (upgrade . ?U)
- (delete . ?D)))
- (let ((map guix-output-list-mode-map))
- (define-key map (kbd "B") 'guix-package-list-latest-builds)
- (define-key map (kbd "G") 'guix-output-list-graph)
- (define-key map (kbd "z") 'guix-package-list-size)
- (define-key map (kbd "L") 'guix-output-list-lint)
- (define-key map (kbd "e") 'guix-output-list-edit)
- (define-key map (kbd "x") 'guix-output-list-execute)
- (define-key map (kbd "i") 'guix-output-list-mark-install)
- (define-key map (kbd "d") 'guix-output-list-mark-delete)
- (define-key map (kbd "U") 'guix-output-list-mark-upgrade)
- (define-key map (kbd "^") 'guix-output-list-mark-upgrades))
- (defvar guix-output-list-default-hint
- '(("\\[guix-output-list-edit]") " edit (go to) the package definition;\n"
- ("\\[guix-output-list-graph]") " view package graph; "
- ("\\[guix-package-list-size]") " view package size; "
- ("\\[guix-output-list-lint]") " lint;\n"
- ("\\[guix-output-list-mark-install]") " mark for installation; "
- ("\\[guix-output-list-mark-delete]") " mark for deletion;\n"
- ("\\[guix-output-list-mark-upgrade]") " mark for upgrading; "
- ("\\[guix-output-list-mark-upgrades]") " mark all for upgrading;\n"
- ("\\[guix-output-list-execute]") " execute operation;\n"))
- (defun guix-output-list-hint ()
- (bui-format-hints
- guix-output-list-default-hint
- guix-ui-hint
- (bui-default-hint)))
- (defun guix-output-list-get-entries (profile search-type
- &rest search-values)
- "Return 'output' entries for displaying them in 'list' buffer."
- (guix-eval-read
- (guix-make-guile-expression
- 'package/output-sexps
- profile 'output search-type search-values
- (cl-union guix-output-list-required-params
- (bui-list-displayed-params 'guix-output)))))
- (defun guix-output-list-mark-install ()
- "Mark the current output for installation and move to the next line."
- (interactive)
- (guix-package-list-marking-check)
- (let* ((entry (bui-list-current-entry))
- (installed (bui-entry-non-void-value entry 'installed)))
- (if installed
- (user-error "This output is already installed")
- (bui-list--mark 'install t))))
- (defun guix-output-list-mark-delete ()
- "Mark the current output for deletion and move to the next line."
- (interactive)
- (guix-package-list-marking-check)
- (let* ((entry (bui-list-current-entry))
- (installed (bui-entry-non-void-value entry 'installed)))
- (if installed
- (bui-list--mark 'delete t)
- (user-error "This output is not installed"))))
- (defun guix-output-list-mark-upgrade ()
- "Mark the current output for upgrading and move to the next line."
- (interactive)
- (guix-package-list-marking-check)
- (let* ((entry (bui-list-current-entry))
- (installed (bui-entry-non-void-value entry 'installed)))
- (or installed
- (user-error "This output is not installed"))
- (when (or (eq (bui-entry-non-void-value entry 'known-status)
- 'obsolete)
- (y-or-n-p "This output is not obsolete. Try to upgrade it anyway? "))
- (bui-list--mark 'upgrade t))))
- (defun guix-output-list-mark-upgrades (&optional arg)
- "Mark all obsolete package outputs for upgrading.
- With ARG, mark all installed (including non-obsolete) packages."
- (interactive "P")
- (guix-package-mark-upgrades
- (lambda (_) (bui-list--mark 'upgrade))
- arg))
- (defun guix-output-list-execute ()
- "Perform actions on the marked outputs."
- (interactive)
- (guix-package-execute-actions #'guix-output-list-make-action))
- (defun guix-output-list-make-action (action-type)
- "Return action specification for the outputs marked with ACTION-TYPE.
- Return nil, if there are no outputs marked with ACTION-TYPE.
- The specification is suitable for `guix-process-output-actions'."
- (let ((ids (bui-list-get-marked-id-list action-type)))
- (and ids (cons action-type
- (mapcar #'guix-package-id-and-output-by-output-id
- ids)))))
- (defun guix-output-list-describe (&rest ids)
- "Describe outputs with IDS (list of output identifiers)."
- (let ((pids (--map (car (guix-package-id-and-output-by-output-id it))
- ids)))
- (bui-get-display-entries
- 'guix-package 'info
- (cl-list* (guix-ui-current-profile)
- 'id (cl-remove-duplicates pids))
- 'add)))
- (defun guix-output-list-edit (&optional directory)
- "Go to the location of the current package.
- See `guix-find-location' for the meaning of DIRECTORY."
- (interactive (list (guix-read-directory)))
- (guix-find-package-definition
- (bui-entry-non-void-value (bui-list-current-entry)
- 'package-id)
- directory))
- (defun guix-output-list-graph (backend node-type)
- "Show BACKEND/NODE-TYPE graph for the current package."
- (interactive
- (list (guix-read-graph-backend)
- (guix-read-graph-node-type)))
- (let ((entry (bui-list-current-entry)))
- (guix-package-graph (if (eq (bui-entry-non-void-value entry 'known-status)
- 'known)
- (bui-entry-non-void-value entry 'package-id)
- (bui-entry-non-void-value entry 'name))
- backend node-type)))
- (defun guix-output-list-lint (&optional checkers)
- "Lint the current package.
- Interactively with prefix, prompt for CHECKERS.
- See `guix-package-lint' for details."
- (interactive
- (list (and current-prefix-arg
- (guix-read-lint-checker-names))))
- (guix-package-lint
- (list (bui-entry-non-void-value (bui-list-current-entry)
- 'package-id))
- checkers))
- ;;; Interactive commands
- (defvar guix-package-search-params '(name synopsis description)
- "Default list of package parameters for searching by regexp.")
- (defvar guix-package-search-history nil
- "A history of minibuffer prompts.")
- ;;;###autoload
- (defun guix-packages-by-name (name &optional profile)
- "Display Guix packages with NAME.
- NAME is a string with name specification. It may optionally contain
- a version number. Examples: \"guile\", \"guile@2.0.11\".
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive
- (let (default-pkg)
- (when guix-package-use-name-at-point
- (let ((at-point (thing-at-point 'symbol t)))
- (when (stringp at-point)
- (let ((at-point (car (split-string at-point "@"))))
- (setq default-pkg (and (member at-point (guix-package-names))
- at-point))))))
- (list (guix-read-package-name "Package: " default-pkg)
- (guix-ui-read-package-profile))))
- (guix-package-get-display profile 'name name))
- ;;;###autoload
- (defun guix-packages-by-regexp (regexp &optional params profile)
- "Search for Guix packages by REGEXP.
- PARAMS are package parameters that should be searched.
- If PARAMS are not specified, use `guix-package-search-params'.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive
- (list (read-regexp "Regexp: " nil 'guix-package-search-history)
- nil (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'regexp regexp
- (or params guix-package-search-params)))
- ;;;###autoload
- (define-obsolete-function-alias 'guix-search-by-regexp
- 'guix-packages-by-regexp "0.5.3")
- ;;;###autoload
- (defun guix-packages-by-name-regexp (regexp &optional profile)
- "Search for Guix packages matching REGEXP in a package name.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive
- (list (read-string "Package name by regexp: "
- nil 'guix-package-search-history)
- (guix-ui-read-package-profile)))
- (guix-packages-by-regexp regexp '(name) profile))
- ;;;###autoload
- (define-obsolete-function-alias 'guix-search-by-name
- 'guix-packages-by-name-regexp "0.5.3")
- ;;;###autoload
- (defun guix-packages-by-license (license &optional profile)
- "Display Guix packages with LICENSE.
- LICENSE is a license name string.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive
- (list (guix-read-license-name)
- (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'license license))
- ;;;###autoload
- (defun guix-packages-by-location (location &optional profile)
- "Display Guix packages placed in LOCATION file.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive
- (list (guix-read-package-location-file)
- (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'location location))
- ;;;###autoload
- (defun guix-package-from-file (file &optional profile)
- "Display Guix package that the code from FILE evaluates to.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively prompt for FILE (see also `guix-support-dired').
- With prefix argument, prompt for PROFILE as well."
- (interactive
- (list (guix-read-file-name-maybe "File with package: ")
- (guix-ui-read-package-profile)))
- (bui-get-display-entries
- 'guix-package 'info
- (list (or profile guix-current-profile) 'from-file file)
- 'add))
- ;;;###autoload
- (defun guix-packages-from-system-config-file (file &optional profile)
- "Display Guix packages from the operating system configuration FILE.
- Make sure FILE has a proper 'operating-system' declaration. You
- may check it, for example, by running the following shell command:
- guix system build --dry-run FILE
- See also Info node `(guix) System Configuration'.
- If PROFILE is nil, use system profile (it is used to show what
- packages from FILE are installed in PROFILE).
- Interactively, prompt for FILE (see also `guix-support-dired').
- With prefix argument, prompt for PROFILE as well.
- Note: This command displays only those packages that are placed
- in 'packages' field of the 'operating-system' declaration. An
- installed system also contains packages installed by
- services (like 'guix' or 'shepherd'). To see all the packages
- installed in a system profile, use
- '\\[guix-installed-system-packages]' command."
- (interactive
- (list (guix-read-os-file-name)
- (and current-prefix-arg
- (guix-read-package-profile guix-system-profile))))
- (guix-package-get-display (or profile guix-system-profile)
- 'from-os-file file))
- ;;;###autoload
- (defun guix-installed-packages (&optional profile)
- "Display information about installed Guix packages.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive (list (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'installed))
- ;;;###autoload
- (defun guix-installed-user-packages ()
- "Display information about Guix packages installed in a user profile."
- (interactive)
- (guix-installed-packages guix-user-profile))
- ;;;###autoload
- (defun guix-installed-system-packages ()
- "Display information about Guix packages installed in a system profile."
- (interactive)
- (guix-installed-packages guix-system-profile))
- ;;;###autoload
- (defun guix-obsolete-packages (&optional profile)
- "Display information about obsolete (or unknown) Guix packages.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive (list (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'unknown))
- ;;;###autoload
- (defun guix-superseded-packages (&optional profile)
- "Display information about superseded Guix packages.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive (list (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'superseded))
- (defun guix-read-package-dependent-type ()
- "Prompt a user for a type of dependent packages."
- (intern
- (completing-read "Dependency type (\"all\" or \"direct\"): "
- '("all" "direct")
- nil t nil nil "all")))
- ;;;###autoload
- (defun guix-dependent-packages (packages &optional type profile)
- "Display Guix packages that depend on PACKAGES.
- This is similar to 'guix refresh --list-dependent PACKAGES ...'.
- See Info node `(guix) Invoking guix refresh' for details.
- TYPE should be a symbol `all' or `direct'. Interactively, prompt
- for it.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive
- (list (guix-read-package-names)
- (guix-read-package-dependent-type)
- (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'dependent (or type 'all) packages))
- ;;;###autoload
- (defun guix-hidden-packages (&optional profile)
- "Display hidden Guix packages.
- If PROFILE is nil, use `guix-current-profile'."
- (interactive (list (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'hidden))
- ;;;###autoload
- (defun guix-all-packages (&optional profile)
- "Display all available Guix packages.
- If PROFILE is nil, use `guix-current-profile'.
- Interactively with prefix, prompt for PROFILE."
- (interactive (list (guix-ui-read-package-profile)))
- (guix-package-get-display profile 'all))
- ;;;###autoload
- (define-obsolete-function-alias 'guix-newest-packages
- 'guix-all-packages "0.5.2")
- ;;;###autoload
- (defun guix-number-of-packages ()
- "Display the number of available Guix packages.
- This number includes the packages from GUIX_PACKAGE_PATH (see
- Info node `(guix) Package Modules')."
- (interactive)
- (message "Total number of Guix packages: %d."
- (guix-eval-read "(number-of-packages)")))
- (provide 'guix-ui-package)
- ;;; guix-ui-package.el ends here
|