>From d3fbccfd8a86d6b96f77f8993e88df8fe755e15f Mon Sep 17 00:00:00 2001 From: Alex Kost Date: Fri, 19 Sep 2014 09:57:36 +0400 Subject: [PATCH 3/3] emacs: Add support for displaying outputs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested by Taylan Ulrich Bayirli/Kammer and Ludovic Courtès. * emacs/guix-base.el (guix-param-titles): Add output titles. (guix-messages): Add output messages. (guix-get-package-id-and-output-by-output-id): New procedure. * emacs/guix-info.el: Add "output-info" buffer type. (guix-info-insert-methods): Add output methods. (guix-info-displayed-params): Add output params. (guix-output-info-insert-version, guix-output-info-insert-output): New procedures. * emacs/guix-list.el: Add "output-list" buffer type. (guix-list-column-format): Add output formats. (guix-list-column-value-methods): Add output methods. (guix-package-list-type): New variable. (guix-generation-list-show-packages): Use it. (guix-package-list-marking-check): Use 'guix-output-list-mode'. (guix-list-mark-package-upgrades): New procedure. (guix-package-list-mark-upgrades): Use it. (guix-list-execute-package-actions): New procedure. (guix-package-list-execute): Use it. (guix-output-list-mark-install, guix-output-list-mark-delete, guix-output-list-mark-upgrade, guix-output-list-mark-upgrades, guix-output-list-execute, guix-output-list-make-action): New procedures. * emacs/guix.el (guix-get-show-packages): Use 'guix-package-list-type'. * doc/emacs.texi (emacs Commands): Mention 'guix-package-list-type'. (emacs List buffer): Describe "output-list". (emacs Info buffer): Describe "output-info". (emacs Buffer Names): New node. (emacs Keymaps): Add keymaps for output buffers. --- doc/emacs.texi | 77 +++++++++++++++++++++++++++++++- emacs/guix-base.el | 61 +++++++++++++++++++++++++- emacs/guix-info.el | 53 ++++++++++++++++++++-- emacs/guix-list.el | 126 ++++++++++++++++++++++++++++++++++++++++++++++++----- emacs/guix.el | 12 ++--- 5 files changed, 305 insertions(+), 24 deletions(-) diff --git a/doc/emacs.texi b/doc/emacs.texi index 7616c8f..328b1f3 100644 --- a/doc/emacs.texi +++ b/doc/emacs.texi @@ -104,6 +104,14 @@ many last generations. @end table +By default commands for displaying packages display a list with a +package per line. If you prefer to see a list of outputs (i.e.@: a list +with each output on a separate line), use the following setting: + address@hidden +(setq guix-package-list-type 'output) address@hidden example + It is possible to change the currently used profile with @address@hidden This has the same effect as specifying @code{--profile} option for @command{guix package} @@ -191,6 +199,24 @@ Mark all obsolete packages for upgrading. Execute actions on marked packages. @end table +An ``output-list'' buffer additionally provides the following bindings: + address@hidden @kbd address@hidden @key{RET} +Describe marked outputs (display available information in a +``output-info'' buffer). address@hidden i +Mark the current output for installation. address@hidden d +Mark the current output for deletion. address@hidden U +Mark the current output for upgrading. address@hidden ^ +Mark all obsolete outputs for upgrading. address@hidden x +Execute actions on marked outputs. address@hidden table + A ``generation-list'' buffer additionally provides the following bindings: @@ -213,7 +239,7 @@ The interface of an ``info'' buffer is similar to the interface of emacs, The Emacs Editor}) which can be used to: @itemize @bullet address@hidden (in a ``package-info'' buffer) address@hidden (in a ``package-info'' or ``output-info'' buffer) @itemize @minus @item install/remove a package; @@ -244,6 +270,7 @@ all) and faces. @menu * Guile and Build Options: emacs Build Options. Specifying how packages are built. +* Buffer Names: emacs Buffer Names. Names of Guix buffers. * Keymaps: emacs Keymaps. Configuring key bindings. * Appearance: emacs Appearance. Settings for visual appearance. @end menu @@ -270,6 +297,48 @@ build}). @end table address@hidden emacs Buffer Names address@hidden Buffer Names + +Default names of ``guix.el'' buffers (address@hidden@dots{}*'') may be +changed by the following variables: + address@hidden @code address@hidden guix-package-list-buffer-name address@hidden guix-output-list-buffer-name address@hidden guix-generation-list-buffer-name address@hidden guix-package-info-buffer-name address@hidden guix-output-info-buffer-name address@hidden guix-generation-info-buffer-name address@hidden guix-repl-buffer-name address@hidden guix-internal-repl-buffer-name address@hidden guix-temp-buffer-name address@hidden table + +For example if you want outputs and packages to be displayed in the same +buffer, you may do it like this: + address@hidden +(eval-after-load "guix.el" + '(setq guix-output-info-buffer-name guix-package-info-buffer-name + guix-output-list-buffer-name guix-package-list-buffer-name)) address@hidden example + +It is even possible to display all types of results in a single buffer +(in such case you will probably use a history (@kbd{l}/@kbd{r}) +extensively): + address@hidden +(let ((name "Guix Universal buffer")) + (setq + guix-package-list-buffer-name name + guix-output-list-buffer-name name + guix-generation-list-buffer-name name + guix-package-info-buffer-name name + guix-output-info-buffer-name name + guix-generation-info-buffer-name name)) address@hidden example + @node emacs Keymaps @subsubsection Keymaps @@ -283,6 +352,9 @@ Parent keymap with general keys for ``list'' buffers. @item guix-package-list-mode-map Keymap with specific keys for ``package-list'' buffers. address@hidden guix-output-list-mode-map +Keymap with specific keys for ``output-list'' buffers. + @item guix-generation-list-mode-map Keymap with specific keys for ``generation-list'' buffers. @@ -292,6 +364,9 @@ Parent keymap with general keys for ``info'' buffers. @item guix-package-info-mode-map Keymap with specific keys for ``package-info'' buffers. address@hidden guix-output-info-mode-map +Keymap with specific keys for ``output-info'' buffers. + @item guix-generation-info-mode-map Keymap with specific keys for ``generation-info'' buffers. diff --git a/emacs/guix-base.el b/emacs/guix-base.el index 1959814..c393a4e 100644 --- a/emacs/guix-base.el +++ b/emacs/guix-base.el @@ -87,6 +87,22 @@ Interactively, prompt for PATH. With prefix, use (path . "Installed path") (dependencies . "Dependencies") (output . "Output")) + (output + (id . "ID") + (name . "Name") + (version . "Version") + (license . "License") + (synopsis . "Synopsis") + (description . "Description") + (home-url . "Home page") + (output . "Output") + (inputs . "Inputs") + (native-inputs . "Native inputs") + (propagated-inputs . "Propagated inputs") + (location . "Location") + (installed . "Installed") + (path . "Installed path") + (dependencies . "Dependencies")) (generation (id . "ID") (number . "Number") @@ -130,6 +146,14 @@ Each element of the list has a form: (equal id (guix-get-key-val entry 'id))) entries)) +(defun guix-get-package-id-and-output-by-output-id (oid) + "Return list (PACKAGE-ID OUTPUT) by output id OID." + (cl-multiple-value-bind (pid-str output) + (split-string oid ":") + (let ((pid (string-to-number pid-str))) + (list (if (= 0 pid) pid-str pid) + output)))) + ;;; Location of the packages @@ -470,8 +494,8 @@ This function will not update the information, use (many "%d newest available packages." count)) (installed (0 "No installed packages.") - (1 "A single installed package.") - (many "%d installed packages." count)) + (1 "A single package installed.") + (many "%d packages installed." count)) (obsolete (0 "No obsolete packages.") (1 "A single obsolete package.") @@ -480,6 +504,39 @@ This function will not update the information, use (0 "No packages installed in generation %d." val) (1 "A single package installed in generation %d." val) (many "%d packages installed in generation %d." count val))) + (output + (id + (0 "Package outputs not found.") + (1 "") + (many "%d package outputs." count)) + (name + (0 "The package output '%s' not found." val) + (1 "A single package output with name '%s'." val) + (many "%d package outputs with '%s' name." count val)) + (regexp + (0 "No package outputs matching '%s'." val) + (1 "A single package output matching '%s'." val) + (many "%d package outputs matching '%s'." count val)) + (all-available + (0 "No package outputs are available for some reason.") + (1 "A single available package output (that's strange).") + (many "%d available package outputs." count)) + (newest-available + (0 "No package outputs are available for some reason.") + (1 "A single newest available package output (that's strange).") + (many "%d newest available package outputs." count)) + (installed + (0 "No installed package outputs.") + (1 "A single package output installed.") + (many "%d package outputs installed." count)) + (obsolete + (0 "No obsolete package outputs.") + (1 "A single obsolete package output.") + (many "%d obsolete package outputs." count)) + (generation + (0 "No package outputs installed in generation %d." val) + (1 "A single package output installed in generation %d." val) + (many "%d package outputs installed in generation %d." count val))) (generation (id (0 "Generations not found.") diff --git a/emacs/guix-info.el b/emacs/guix-info.el index 05281e7..db8be40 100644 --- a/emacs/guix-info.el +++ b/emacs/guix-info.el @@ -117,6 +117,23 @@ number of characters, it will be split into several lines.") guix-info-insert-title-simple) (dependencies guix-package-info-insert-output-dependencies guix-info-insert-title-simple)) + (output + (name guix-package-info-name) + (version guix-output-info-insert-version) + (output guix-output-info-insert-output) + (path guix-package-info-insert-output-path + guix-info-insert-title-simple) + (dependencies guix-package-info-insert-output-dependencies + guix-info-insert-title-simple) + (license guix-package-info-license) + (synopsis guix-package-info-synopsis) + (description guix-package-info-insert-description + guix-info-insert-title-simple) + (home-url guix-info-insert-url) + (inputs guix-package-info-insert-inputs) + (native-inputs guix-package-info-insert-native-inputs) + (propagated-inputs guix-package-info-insert-propagated-inputs) + (location guix-package-info-insert-location)) (generation (number guix-generation-info-insert-number) (path guix-info-insert-file-path) @@ -141,6 +158,8 @@ argument.") (defvar guix-info-displayed-params '((package name version synopsis outputs location home-url license inputs native-inputs propagated-inputs description) + (output name version output synopsis path dependencies location home-url + license inputs native-inputs propagated-inputs description) (installed path dependencies) (generation number prev-number time path)) "List of displayed entry parameters. @@ -520,9 +539,37 @@ ENTRY is an alist with package info." "Insert PATH of the installed output." (guix-info-insert-val-simple path #'guix-info-insert-file-path)) -(defun guix-package-info-insert-output-dependencies (deps &optional _) - "Insert dependencies DEPS of the installed output." - (guix-info-insert-val-simple deps #'guix-info-insert-file-path)) +(defalias 'guix-package-info-insert-output-dependencies + 'guix-package-info-insert-output-path) + + +;;; Displaying outputs + +(guix-define-buffer-type info output + :required (id package-id installed non-unique)) + +(defun guix-output-info-insert-version (version entry) + "Insert output VERSION and obsolete text if needed at point." + (guix-info-insert-val-default version + 'guix-package-info-version) + (and (guix-get-key-val entry 'obsolete) + (guix-package-info-insert-obsolete-text))) + +(defun guix-output-info-insert-output (output entry) + "Insert OUTPUT and action buttons at point." + (let* ((installed (guix-get-key-val entry 'installed)) + (obsolete (guix-get-key-val entry 'obsolete)) + (action-type (if installed 'delete 'install))) + (guix-info-insert-val-default + output + (if installed + 'guix-package-info-installed-outputs + 'guix-package-info-uninstalled-outputs)) + (guix-info-insert-indent) + (guix-package-info-insert-action-button action-type entry output) + (when obsolete + (guix-info-insert-indent) + (guix-package-info-insert-action-button 'upgrade entry output)))) ;;; Displaying generations diff --git a/emacs/guix-list.el b/emacs/guix-list.el index 3732d9b..b9204da 100644 --- a/emacs/guix-list.el +++ b/emacs/guix-list.el @@ -55,6 +55,12 @@ entries, he will be prompted for confirmation." (outputs 13 t) (installed 13 t) (synopsis 30 nil)) + (output + (name 20 t) + (version 10 nil) + (output 9 t) + (installed 12 t) + (synopsis 30 nil)) (generation (number 5 ,(lambda (a b) (guix-list-sort-numerically 0 a b)) @@ -82,6 +88,10 @@ this list have a priority.") (synopsis . guix-list-get-one-line) (description . guix-list-get-one-line) (installed . guix-package-list-get-installed-outputs)) + (output + (name . guix-package-list-get-name) + (synopsis . guix-list-get-one-line) + (description . guix-list-get-one-line)) (generation (time . guix-list-get-time) (path . guix-list-get-file-path))) @@ -456,6 +466,14 @@ With prefix (if ARG is non-nil), describe entries marked with any mark." "Face used if a package is obsolete." :group 'guix-package-list) +(defcustom guix-package-list-type 'package + "Define how to display packages in a list buffer. +May be a symbol `package' or `output' (if `output', display each +output on a separate line)." + :type '(choice (const :tag "List of packages" package) + (const :tag "List of outputs" output)) + :group 'guix-package-list) + (defcustom guix-package-list-generation-marking-enabled nil "If non-nil, allow putting marks in a list with 'generation packages'. @@ -499,7 +517,8 @@ Colorize it with `guix-package-list-installed' or (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) - (derived-mode-p 'guix-package-list-mode) + (or (derived-mode-p 'guix-package-list-mode) + (derived-mode-p 'guix-output-list-mode)) (eq guix-search-type 'generation)) (error "Action marks are disabled for lists of 'generation packages'"))) @@ -563,9 +582,10 @@ be separated with \",\")." (and arg "Output(s) to upgrade: ") installed)))) -(defun guix-package-list-mark-upgrades () - "Mark all obsolete packages for upgrading." - (interactive) +(defun guix-list-mark-package-upgrades (fun) + "Mark all obsolete packages for upgrading. +Use FUN to perform marking of the current line. FUN should +accept an entry as argument." (guix-package-list-marking-check) (let ((obsolete (cl-remove-if-not (lambda (entry) @@ -579,20 +599,32 @@ be separated with \",\")." (equal id (guix-get-key-val entry 'id))) obsolete))) (when entry - (apply #'guix-list-mark - 'upgrade nil - (guix-get-installed-outputs entry)))))))) + (funcall fun entry))))))) -(defun guix-package-list-execute () - "Perform actions on the marked packages." +(defun guix-package-list-mark-upgrades () + "Mark all obsolete packages for upgrading." (interactive) + (guix-list-mark-package-upgrades + (lambda (entry) + (apply #'guix-list-mark + 'upgrade nil + (guix-get-installed-outputs entry))))) + +(defun guix-list-execute-package-actions (fun) + "Perform actions on the marked packages. +Use FUN to define actions suitable for `guix-process-package-actions'. +FUN should accept action-type as argument." (let ((actions (delq nil - (mapcar #'guix-package-list-make-action - '(install delete upgrade))))) + (mapcar fun '(install delete upgrade))))) (if actions (apply #'guix-process-package-actions actions) (user-error "No operations specified")))) +(defun guix-package-list-execute () + "Perform actions on the marked packages." + (interactive) + (guix-list-execute-package-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. @@ -601,6 +633,76 @@ The specification is suitable for `guix-process-package-actions'." (and specs (cons action-type specs)))) +;;; Displaying outputs + +(guix-define-buffer-type list output) + +(guix-list-define-entry-type output + :sort-key name + :marks ((install . ?I) + (upgrade . ?U) + (delete . ?D))) + +(let ((map guix-output-list-mode-map)) + (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)) + +(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 (guix-list-current-entry)) + (installed (guix-get-key-val entry 'installed))) + (if installed + (user-error "This output is already installed") + (guix-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 (guix-list-current-entry)) + (installed (guix-get-key-val entry 'installed))) + (if installed + (guix-list-mark 'delete t) + (user-error "This output is not installed")))) + +(defun guix-output-list-mark-upgrade () + "Mark the current output for deletion and move to the next line." + (interactive) + (guix-package-list-marking-check) + (let* ((entry (guix-list-current-entry)) + (installed (guix-get-key-val entry 'installed))) + (or installed + (user-error "This output is not installed")) + (when (or (guix-get-key-val entry 'obsolete) + (y-or-n-p "This output is not obsolete. Try to upgrade it anyway? ")) + (guix-list-mark 'upgrade t)))) + +(defun guix-output-list-mark-upgrades () + "Mark all obsolete package outputs for upgrading." + (interactive) + (guix-list-mark-package-upgrades + (lambda (_) (guix-list-mark 'upgrade)))) + +(defun guix-output-list-execute () + "Perform actions on the marked outputs." + (interactive) + (guix-list-execute-package-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 (guix-list-get-marked-id-list action-type))) + (and ids (cons action-type + (mapcar #'guix-get-package-id-and-output-by-output-id + ids))))) + + ;;; Displaying generations (guix-define-buffer-type list generation) @@ -618,7 +720,7 @@ The specification is suitable for `guix-process-package-actions'." (defun guix-generation-list-show-packages () "List installed packages for the generation at point." (interactive) - (guix-get-show-entries 'list 'package 'generation + (guix-get-show-entries 'list guix-package-list-type 'generation (guix-list-current-id))) (provide 'guix-list) diff --git a/emacs/guix.el b/emacs/guix.el index 621dd3b..f6e2023 100644 --- a/emacs/guix.el +++ b/emacs/guix.el @@ -58,24 +58,24 @@ SEARCH-VALS. Results are displayed in the list buffer, unless a single package is found and `guix-list-single-package' is nil." (let* ((list-params (guix-get-params-for-receiving - 'list 'package)) - (packages (guix-get-entries 'package + 'list guix-package-list-type)) + (packages (guix-get-entries guix-package-list-type search-type search-vals list-params))) (if (or guix-list-single-package (cdr packages)) - (guix-set-buffer packages 'list 'package + (guix-set-buffer packages 'list guix-package-list-type search-type search-vals) (let* ((info-params (guix-get-params-for-receiving - 'info 'package)) + 'info guix-package-list-type)) (packages (if (equal list-params info-params) packages ;; If we don't have required info, we should ;; receive it again - (guix-get-entries 'package + (guix-get-entries guix-package-list-type search-type search-vals info-params)))) - (guix-set-buffer packages 'info 'package + (guix-set-buffer packages 'info guix-package-list-type search-type search-vals))))) (defun guix-get-show-generations (search-type &rest search-vals) -- 2.1.0