From 2c7ed388cf403120c17b5e222ac5dda093f5ad94 Mon Sep 17 00:00:00 2001 From: Alex Kost Date: Fri, 20 Nov 2015 00:13:19 +0300 Subject: [PATCH] emacs: info: Generalize inserting and formatting. * emacs/guix-info.el: Use a more flexible format for inserting any data. (guix-info-ignore-empty-vals): Rename to... (guix-info-ignore-empty-values): ... this. (guix-info-insert-methods): Merge this and... (guix-info-displayed-params): ... this into... (guix-info-format): ... this. Change format specifications. (guix-info-title-aliases, guix-info-value-aliases): New variables. (guix-info-displayed-params): Adjust for the new format. (guix-info-insert-entry): Likewise. (guix-package-info-fill-heading): Replace with... (guix-info-fill): ... this. (guix-info-insert-param): Replace with... (guix-info-insert-entry-unit): ... this. (guix-info-insert-title-default): Replace with... (guix-info-insert-title-format): ... this. (guix-info-insert-val-default): Replace with... (guix-info-insert-value-format): ... this. (guix-info-insert-val-simple): Replace with... (guix-info-insert-value-indent): ... this. (guix-package-info-insert-source): Adjust accordingly. (guix-package-info-insert-heading): Insert only name and version. (guix-package-info-define-insert-inputs): Do not generate 'guix-package-info-insert-ENTRY-TYPE-inputs' procedures. (guix-info-fill-column, guix-info-insert-entry-default) (guix-info-method-funcall, guix-info-insert-file-path) (guix-info-insert-url, guix-info-insert-package-function) (guix-info-insert-installed-function) (guix-info-insert-output-function) (guix-info-insert-generation-function) (guix-package-info-heading-params) (guix-package-info-insert-with-heading) (guix-package-info-insert-description) (guix-package-info-insert-location) (guix-package-info-insert-full-names) (guix-package-info-insert-source-url): Remove. (guix-info-fill-column, guix-info-param-title) (guix-info-title-function, guix-info-value-function) (guix-info-title-method->function) (guix-info-value-method->function) (guix-info-insert-value-simple): New procedures. * emacs/guix-utils.el (guix-buttonize, guix-button-type?): New procedures. (guix-split-string): Split multi-line strings and ignore empty lines. * doc/emacs.texi (Emacs Appearance): Adjust accordingly. --- doc/emacs.texi | 7 +- emacs/guix-info.el | 536 ++++++++++++++++++++------------------------ emacs/guix-utils.el | 35 ++- 3 files changed, 277 insertions(+), 301 deletions(-) diff --git a/doc/emacs.texi b/doc/emacs.texi index 08c973e767..7f5a9bb023 100644 --- a/doc/emacs.texi +++ b/doc/emacs.texi @@ -484,13 +484,12 @@ the following variables (@dfn{ENTRY-TYPE} means @code{package}, Specify the columns, their names, what and how is displayed in ``list'' buffers. -@item guix-info-displayed-params -@itemx guix-info-insert-methods -@itemx guix-info-ignore-empty-vals +@item guix-info-format +@itemx guix-info-ignore-empty-values @itemx guix-info-param-title-format @itemx guix-info-multiline-prefix @itemx guix-info-indent -@itemx guix-info-fill-column +@itemx guix-info-fill @itemx guix-info-delimiter Various settings for ``info'' buffers. diff --git a/emacs/guix-info.el b/emacs/guix-info.el index 05a8143202..9c46810f60 100644 --- a/emacs/guix-info.el +++ b/emacs/guix-info.el @@ -81,122 +81,175 @@ "Mouse face used for action buttons." :group 'guix-info-faces) -(defcustom guix-info-ignore-empty-vals nil +(defcustom guix-info-ignore-empty-values nil "If non-nil, do not display parameters with nil values." :type 'boolean :group 'guix-info) +(defcustom guix-info-fill t + "If non-nil, fill string parameters to fit the window. +If nil, insert text parameters (like synopsis or description) in +a raw form." + :type 'boolean + :group 'guix-info) + (defvar guix-info-param-title-format "%-18s: " "String used to format a title of a parameter. It should be a '%s'-sequence. After inserting a title formatted with this string, a value of the parameter is inserted. -This string is used by `guix-info-insert-title-default'.") +This string is used by `guix-info-insert-title-format'.") -(defvar guix-info-multiline-prefix (make-string 20 ?\s) +(defvar guix-info-multiline-prefix + (make-string (length (format guix-info-param-title-format " ")) + ?\s) "String used to format multi-line parameter values. If a value occupies more than one line, this string is inserted in the beginning of each line after the first one. -This string is used by `guix-info-insert-val-default'.") +This string is used by `guix-info-insert-value-format'.") (defvar guix-info-indent 2 "Number of spaces used to indent various parts of inserted text.") -(defvar guix-info-fill-column 60 - "Column used for filling (word wrapping) parameters with long lines. -If a value is not multi-line and it occupies more than this -number of characters, it will be split into several lines.") - (defvar guix-info-delimiter "\n\f\n" "String used to separate entries.") -(defvar guix-info-insert-methods +(defvar guix-info-format '((package - (name guix-package-info-name) - (version guix-package-info-version) - (license guix-package-info-license) - (synopsis guix-package-info-synopsis) - (description guix-package-info-insert-description - guix-info-insert-title-simple) - (outputs guix-package-info-insert-outputs - guix-info-insert-title-simple) - (source guix-package-info-insert-source - 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)) + guix-package-info-insert-heading + ignore + (synopsis ignore (simple guix-package-info-synopsis)) + ignore + (description ignore (simple guix-package-info-description)) + ignore + (outputs simple guix-package-info-insert-outputs) + (source simple guix-package-info-insert-source) + (location format (format guix-package-location)) + (home-url format (format guix-url)) + (license format (format guix-package-info-license)) + (inputs format (format guix-package-input)) + (native-inputs format (format guix-package-native-input)) + (propagated-inputs format (format guix-package-propagated-input))) (installed - (path guix-package-info-insert-output-path - guix-info-insert-title-simple) - (dependencies guix-package-info-insert-output-dependencies - guix-info-insert-title-simple)) + (path simple (indent guix-file)) + (dependencies simple (indent guix-file))) (output - (name guix-package-info-name) - (version guix-output-info-insert-version) - (output guix-output-info-insert-output) - (source guix-package-info-insert-source - guix-info-insert-title-simple) - (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)) + (name format (format guix-package-info-name)) + (version format guix-output-info-insert-version) + (output format guix-output-info-insert-output) + (synopsis simple (indent guix-package-info-synopsis)) + (source simple guix-package-info-insert-source) + (path simple (indent guix-file)) + (dependencies simple (indent guix-file)) + (location format (format guix-package-location)) + (home-url format (format guix-url)) + (license format (format guix-package-info-license)) + (inputs format (format guix-package-input)) + (native-inputs format (format guix-package-native-input)) + (propagated-inputs format (format guix-package-propagated-input)) + (description simple (indent guix-package-info-description))) (generation - (number guix-generation-info-insert-number) - (current guix-generation-info-insert-current) - (path guix-info-insert-file-path) - (time guix-info-insert-time))) + (number format guix-generation-info-insert-number) + (prev-number format (format)) + (current format guix-generation-info-insert-current) + (path simple (indent guix-file)) + (time format (time)))) "Methods for inserting parameter values. Each element of the list should have a form: - (ENTRY-TYPE . ((PARAM INSERT-VALUE [INSERT-TITLE]) ...)) + (ENTRY-TYPE . (METHOD ...)) -INSERT-VALUE may be either nil, a face name or a function. If it -is nil or a face, `guix-info-insert-val-default' function is -called with parameter value and INSERT-VALUE as arguments. If it -is a function, this function is called with parameter value and -entry info (alist of parameters and their values) as arguments. +Each METHOD should be either a function or should have the +following form: -INSERT-TITLE may be either nil, a face name or a function. If it -is nil or a face, `guix-info-insert-title-default' function is -called with parameter title and INSERT-TITLE as arguments. If it -is a function, this function is called with parameter title as -argument.") + (PARAM INSERT-TITLE INSERT-VALUE) -(defvar guix-info-displayed-params - '((package name version synopsis outputs source location home-url - license inputs native-inputs propagated-inputs description) - (output name version output synopsis source path dependencies location - home-url license inputs native-inputs propagated-inputs - description) - (installed path dependencies) - (generation number prev-number current time path)) - "List of displayed entry parameters. -Each element of the list should have a form: +If METHOD is a function, it is called with an entry as argument. - (ENTRY-TYPE . (PARAM ...)) +PARAM is a name of entry parameter. -The order of displayed parameters is the same as in this list.") +INSERT-TITLE may be either a symbol or a list. If it is a +symbol, it should be a function or an alias from +`guix-info-title-aliases', in which case it is called with title +as argument. If it is a list, it should have a +form (FUN-OR-ALIAS [ARGS ...]), in which case FUN-OR-ALIAS is +called with title and ARGS as arguments. -(defun guix-info-insert-methods (entry-type param) - "Return list of insert methods for parameter PARAM of ENTRY-TYPE. -See `guix-info-insert-methods' for details." - (guix-assq-value guix-info-insert-methods - entry-type param)) +INSERT-VALUE may be either a symbol or a list. If it is a +symbol, it should be a function or an alias from +`guix-info-value-aliases', in which case it is called with value +and entry as arguments. If it is a list, it should have a +form (FUN-OR-ALIAS [ARGS ...]), in which case FUN-OR-ALIAS is +called with value and ARGS as arguments. + +Parameters are inserted in the same order as defined by this list. +After calling each METHOD, a new line is inserted.") + +(defun guix-info-param-title (entry-type param) + "Return a title of an ENTRY-TYPE parameter PARAM." + (guix-get-param-title entry-type param)) + +(defun guix-info-format (entry-type) + "Return 'info' format for ENTRY-TYPE." + (guix-assq-value guix-info-format entry-type)) (defun guix-info-displayed-params (entry-type) - "Return parameters of ENTRY-TYPE that should be displayed." - (guix-assq-value guix-info-displayed-params - entry-type)) + "Return a list of ENTRY-TYPE parameters that should be displayed." + (delq nil + (mapcar (lambda (spec) + (pcase spec + (`(,param . ,_) param))) + (guix-info-format entry-type)))) + + +;;; Inserting entries + +(defvar guix-info-title-aliases + '((format . guix-info-insert-title-format) + (simple . guix-info-insert-title-simple)) + "Alist of aliases and functions to insert titles.") + +(defvar guix-info-value-aliases + '((format . guix-info-insert-value-format) + (indent . guix-info-insert-value-indent) + (simple . guix-info-insert-value-simple) + (time . guix-info-insert-time)) + "Alist of aliases and functions to insert values.") + +(defun guix-info-title-function (fun-or-alias) + "Convert FUN-OR-ALIAS into a function to insert a title." + (or (guix-assq-value guix-info-title-aliases fun-or-alias) + fun-or-alias)) + +(defun guix-info-value-function (fun-or-alias) + "Convert FUN-OR-ALIAS into a function to insert a value." + (or (guix-assq-value guix-info-value-aliases fun-or-alias) + fun-or-alias)) + +(defun guix-info-title-method->function (method) + "Convert title METHOD into a function to insert a title." + (pcase method + ((pred null) #'ignore) + ((pred symbolp) (guix-info-title-function method)) + (`(,fun-or-alias . ,rest-args) + (lambda (title) + (apply (guix-info-title-function fun-or-alias) + title rest-args))) + (_ (error "Unknown title method '%S'" method)))) + +(defun guix-info-value-method->function (method) + "Convert value METHOD into a function to insert a value." + (pcase method + ((pred null) #'ignore) + ((pred functionp) method) + (`(,fun-or-alias . ,rest-args) + (lambda (value _) + (apply (guix-info-value-function fun-or-alias) + value rest-args))) + (_ (error "Unknown value method '%S'" method)))) + +(defun guix-info-fill-column () + "Return fill column for the current window." + (min (window-width) fill-column)) (defun guix-info-get-indent (&optional level) "Return `guix-info-indent' \"multiplied\" by LEVEL spaces. @@ -215,115 +268,122 @@ ENTRIES should have a form of `guix-entries'." entries guix-info-delimiter)) -(defun guix-info-insert-entry-default (entry entry-type - &optional indent-level) - "Insert ENTRY of ENTRY-TYPE into the current info buffer. -If INDENT-LEVEL is non-nil, indent displayed information by this -number of `guix-info-indent' spaces." - (guix-with-indent (* (or indent-level 0) - guix-info-indent) - (mapc (lambda (param) - (guix-info-insert-param param entry entry-type)) - (guix-info-displayed-params entry-type)))) - (defun guix-info-insert-entry (entry entry-type &optional indent-level) "Insert ENTRY of ENTRY-TYPE into the current info buffer. -Use `guix-info-insert-ENTRY-TYPE-function' or -`guix-info-insert-entry-default' if it is nil." - (let* ((var (intern (concat "guix-info-insert-" - (symbol-name entry-type) - "-function"))) - (fun (symbol-value var))) - (if (functionp fun) - (funcall fun entry) - (guix-info-insert-entry-default entry entry-type indent-level)))) +If INDENT-LEVEL is non-nil, indent displayed data by this number +of `guix-info-indent' spaces." + (guix-with-indent (* (or indent-level 0) + guix-info-indent) + (dolist (spec (guix-info-format entry-type)) + (guix-info-insert-entry-unit spec entry entry-type)))) -(defun guix-info-insert-param (param entry entry-type) +(defun guix-info-insert-entry-unit (format-spec entry entry-type) "Insert title and value of a PARAM at point. ENTRY is alist with parameters and their values. ENTRY-TYPE is a type of ENTRY." - (let ((val (guix-entry-value entry param))) - (unless (and guix-info-ignore-empty-vals (null val)) - (let* ((title (guix-get-param-title entry-type param)) - (insert-methods (guix-info-insert-methods entry-type param)) - (val-method (car insert-methods)) - (title-method (cadr insert-methods))) - (guix-info-method-funcall title title-method - #'guix-info-insert-title-default) - (guix-info-method-funcall val val-method - #'guix-info-insert-val-default - entry) - (insert "\n"))))) - -(defun guix-info-method-funcall (val method default-fun &rest args) - "Call METHOD or DEFAULT-FUN. - -If METHOD is a function and VAL is non-nil, call this -function by applying it to VAL and ARGS. - -If METHOD is a face, propertize inserted VAL with this face." - (cond ((or (null method) - (facep method)) - (funcall default-fun val method)) - ((functionp method) - (apply method val args)) - (t (error "Unknown method '%S'" method)))) - -(defun guix-info-insert-title-default (title &optional face format) - "Insert TITLE formatted with `guix-info-param-title-format' at point." - (guix-format-insert title - (or face 'guix-info-param-title) - (or format guix-info-param-title-format))) + (pcase format-spec + ((pred functionp) + (funcall format-spec entry) + (insert "\n")) + (`(,param ,title-method ,value-method) + (let ((value (guix-entry-value entry param))) + (unless (and guix-info-ignore-empty-values (null value)) + (let ((title (guix-info-param-title entry-type param)) + (insert-title (guix-info-title-method->function title-method)) + (insert-value (guix-info-value-method->function value-method))) + (funcall insert-title title) + (funcall insert-value value entry) + (insert "\n"))))) + (_ (error "Unknown format specification '%S'" format-spec)))) (defun guix-info-insert-title-simple (title &optional face) - "Insert TITLE at point." - (guix-info-insert-title-default title face "%s:")) + "Insert \"TITLE: \" string at point. +If FACE is nil, use `guix-info-param-title'." + (guix-format-insert title + (or face 'guix-info-param-title) + "%s: ")) -(defun guix-info-insert-val-default (val &optional face) - "Format and insert parameter value VAL at point. +(defun guix-info-insert-title-format (title &optional face) + "Insert TITLE using `guix-info-param-title-format' at point. +If FACE is nil, use `guix-info-param-title'." + (guix-format-insert title + (or face 'guix-info-param-title) + guix-info-param-title-format)) -This function is intended to be called after -`guix-info-insert-title-default'. +(defun guix-info-insert-value-simple (value &optional button-or-face indent) + "Format and insert parameter VALUE at point. -If VAL is a one-line string longer than `guix-info-fill-column', -split it into several short lines. See also -`guix-info-multiline-prefix'. +VALUE may be split into several short lines to fit the current +window, depending on `guix-info-fill', and each line is indented +with INDENT number of spaces. -If FACE is non-nil, propertize inserted line(s) with this FACE." - (guix-split-insert val face - guix-info-fill-column - (concat "\n" guix-info-multiline-prefix))) +If BUTTON-OR-FACE is a button type symbol, transform VALUE into +this (these) button(s) and insert each one on a new line. If it +is a face symbol, propertize inserted line(s) with this face." + (or indent (setq indent 0)) + (guix-with-indent indent + (let* ((button? (guix-button-type? button-or-face)) + (face (unless button? button-or-face)) + (fill-col (unless (or button? + (and (stringp value) + (not guix-info-fill))) + (- (guix-info-fill-column) indent))) + (value (if (and value button?) + (guix-buttonize value button-or-face "\n") + value))) + (guix-split-insert value face fill-col "\n")))) -(defun guix-info-insert-val-simple (val &optional face-or-fun) - "Format and insert parameter value VAL at point. +(defun guix-info-insert-value-indent (value &optional button-or-face) + "Format and insert parameter VALUE at point. -This function is intended to be called after -`guix-info-insert-title-simple'. +This function is intended to be called after inserting a title +with `guix-info-insert-title-simple'. -If VAL is a one-line string longer than `guix-info-fill-column', -split it into several short lines and indent each line with -`guix-info-indent' spaces. +VALUE may be split into several short lines to fit the current +window, depending on `guix-info-fill', and each line is indented +with `guix-info-indent'. -If FACE-OR-FUN is a face, propertize inserted line(s) with this FACE. +For the meaning of BUTTON-OR-FACE, see `guix-info-insert-value-simple'." + (when value (insert "\n")) + (guix-info-insert-value-simple value button-or-face guix-info-indent)) -If FACE-OR-FUN is a function, call it with VAL as argument. If -VAL is a list, call the function on each element of this list." - (if (null val) - (progn (guix-info-insert-indent) - (guix-format-insert nil)) - (let ((prefix (concat "\n" (guix-info-get-indent)))) - (insert prefix) - (if (functionp face-or-fun) - (guix-mapinsert face-or-fun - (if (listp val) val (list val)) - prefix) - (guix-split-insert val face-or-fun - guix-info-fill-column prefix))))) +(defun guix-info-insert-value-format (value &optional button-or-face + &rest button-properties) + "Format and insert parameter VALUE at point. -(defun guix-info-insert-time (seconds &optional _) +This function is intended to be called after inserting a title +with `guix-info-insert-title-format'. + +VALUE may be split into several short lines to fit the current +window, depending on `guix-info-fill' and +`guix-info-multiline-prefix'. If VALUE is a list, its elements +will be separated with `guix-list-separator'. + +If BUTTON-OR-FACE is a button type symbol, transform VALUE into +this (these) button(s). If it is a face symbol, propertize +inserted line(s) with this face. + +BUTTON-PROPERTIES are passed to `guix-buttonize' (only if +BUTTON-OR-FACE is a button type)." + (let* ((button? (guix-button-type? button-or-face)) + (face (unless button? button-or-face)) + (fill-col (when (or button? + guix-info-fill + (not (stringp value))) + (- (guix-info-fill-column) + (length guix-info-multiline-prefix)))) + (value (if (and value button?) + (apply #'guix-buttonize + value button-or-face guix-list-separator + button-properties) + value))) + (guix-split-insert value face fill-col + (concat "\n" guix-info-multiline-prefix)))) + +(defun guix-info-insert-time (seconds &optional face) "Insert formatted time string using SECONDS at point." - (guix-info-insert-val-default (guix-get-time-string seconds) - 'guix-info-time)) + (guix-format-insert (guix-get-time-string seconds) + (or face 'guix-info-time))) ;;; Buttons @@ -394,14 +454,6 @@ See `insert-text-button' for the meaning of PROPERTIES." 'help-echo message properties)) -(defun guix-info-insert-file-path (path &optional _) - "Make button from file PATH and insert it at point." - (guix-insert-button path 'guix-file)) - -(defun guix-info-insert-url (url &optional _) - "Make button from URL and insert it at point." - (guix-insert-button url 'guix-url)) - (defvar guix-info-mode-map (let ((map (make-sparse-keymap))) @@ -418,7 +470,7 @@ See `insert-text-button' for the meaning of PROPERTIES." ;;; Displaying packages (guix-define-buffer-type info package - :required (id installed non-unique)) + :required (id name version installed non-unique)) (defface guix-package-info-heading '((t :inherit guix-info-heading)) @@ -483,57 +535,11 @@ See `insert-text-button' for the meaning of PROPERTIES." "Face used if a package is obsolete." :group 'guix-package-info-faces) -(defvar guix-info-insert-package-function - #'guix-package-info-insert-with-heading - "Function used to insert a package information. -It is called with a single argument - alist of package parameters. -If nil, insert package in a default way.") - -(defvar guix-package-info-heading-params '(synopsis description) - "List of parameters displayed in a heading along with name and version.") - -(defcustom guix-package-info-fill-heading t - "If nil, insert heading parameters in a raw form, without -filling them to fit the window." - :type 'boolean - :group 'guix-package-info) - (defun guix-package-info-insert-heading (entry) - "Insert the heading for package ENTRY. -Show package name, version, and `guix-package-info-heading-params'." + "Insert package ENTRY heading (name specification) at point." (guix-format-insert (concat (guix-entry-value entry 'name) " " (guix-entry-value entry 'version)) - 'guix-package-info-heading) - (insert "\n\n") - (mapc (lambda (param) - (let ((val (guix-entry-value entry param)) - (face (guix-get-symbol (symbol-name param) - 'info 'package))) - (when val - (let* ((col (min (window-width) fill-column)) - (val (if guix-package-info-fill-heading - (guix-get-filled-string val col) - val))) - (guix-format-insert val (and (facep face) face)) - (insert "\n\n"))))) - guix-package-info-heading-params)) - -(defun guix-package-info-insert-with-heading (entry) - "Insert package ENTRY with its heading at point." - (guix-package-info-insert-heading entry) - (mapc (lambda (param) - (unless (or (memq param '(name version)) - (memq param guix-package-info-heading-params)) - (guix-info-insert-param param entry 'package))) - (guix-info-displayed-params 'package))) - -(defun guix-package-info-insert-description (desc &optional _) - "Insert description DESC at point." - (guix-info-insert-val-simple desc 'guix-package-info-description)) - -(defun guix-package-info-insert-location (location &optional _) - "Make button from file LOCATION and insert it at point." - (guix-insert-button location 'guix-package-location)) + 'guix-package-info-heading)) (defmacro guix-package-info-define-insert-inputs (&optional type) "Define a face and a function for inserting package inputs. @@ -544,8 +550,7 @@ Face name is `guix-package-info-TYPE-inputs'." (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"))) - (fun (intern (concat "guix-package-info-insert-" type-name "inputs")))) + (btn (intern (concat "guix-package-" type-name "input")))) `(progn (defface ,face '((t :inherit guix-package-info-name-button)) @@ -554,29 +559,12 @@ Face name is `guix-package-info-TYPE-inputs'." (define-button-type ',btn :supertype 'guix-package-name - 'face ',face) - - (defun ,fun (inputs &optional _) - ,(concat "Make buttons from " type-desc "INPUTS and insert them at point.") - (guix-package-info-insert-full-names inputs ',btn))))) + '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-full-names (names button-type) - "Make BUTTON-TYPE buttons from package NAMES and insert them at point. -NAMES is a list of strings." - (if names - (guix-info-insert-val-default - (with-temp-buffer - (guix-mapinsert (lambda (name) - (guix-insert-button name button-type)) - names - guix-list-separator) - (buffer-substring (point-min) (point-max)))) - (guix-format-insert nil))) - ;;; Inserting outputs and installed parameters @@ -588,12 +576,6 @@ formatted with this string, an action button is inserted.") (defvar guix-package-info-obsolete-string "(This package is obsolete)" "String used if a package is obsolete.") -(defvar guix-info-insert-installed-function nil - "Function used to insert an installed information. -It is called with a single argument - alist of installed -parameters (`output', `path', `dependencies'). -If nil, insert installed info in a default way.") - (defun guix-package-info-insert-outputs (outputs entry) "Insert OUTPUTS from package ENTRY at point." (and (guix-entry-value entry 'obsolete) @@ -668,13 +650,6 @@ ENTRY is an alist with package info." (guix-entry-id entry)) 'output output))) -(defun guix-package-info-insert-output-path (path &optional _) - "Insert PATH of the installed output." - (guix-info-insert-val-simple path #'guix-info-insert-file-path)) - -(defalias 'guix-package-info-insert-output-dependencies - 'guix-package-info-insert-output-path) - ;;; Inserting a source @@ -711,10 +686,6 @@ prompt depending on `guix-operation-confirm' variable)." ;; no action is bound to a source button. (message "Yes, this is the source URL. What did you expect?"))) -(defun guix-package-info-insert-source-url (url &optional _) - "Make button from source URL and insert it at point." - (guix-insert-button url 'guix-package-source)) - (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'). @@ -746,7 +717,6 @@ PACKAGE-ID is an ID of the package which source to show." (defun guix-package-info-insert-source (source entry) "Insert SOURCE from package ENTRY at point. SOURCE is a list of URLs." - (guix-info-insert-indent) (if (null source) (guix-format-insert nil) (let* ((source-file (guix-entry-value entry 'source-file)) @@ -759,7 +729,7 @@ SOURCE is a list of URLs." (lambda (btn) (guix-package-info-show-source (button-get btn 'entry-id) (button-get btn 'package-id))) - "Show the source store path of the current package" + "Show the source store directory of the current package" 'entry-id entry-id 'package-id package-id) (unless (file-exists-p source-file) @@ -770,10 +740,8 @@ SOURCE is a list of URLs." (button-get btn 'package-id))) "Download the source into the store" 'package-id package-id)) - (guix-info-insert-val-simple source-file - #'guix-info-insert-file-path)) - (guix-info-insert-val-simple source - #'guix-package-info-insert-source-url)))) + (guix-info-insert-value-indent source-file 'guix-file)) + (guix-info-insert-value-indent source 'guix-package-source)))) (defun guix-package-info-redisplay-after-download () "Redisplay an 'info' buffer after downloading the package source. @@ -788,19 +756,14 @@ This function is used to hide a \"Download\" button if needed." ;;; Displaying outputs -(guix-define-buffer-type info output +(guix-ui-info-define-interface output :buffer-name "*Guix Package Info*" :required (id package-id installed non-unique)) -(defvar guix-info-insert-output-function nil - "Function used to insert an output information. -It is called with a single argument - alist of output parameters. -If nil, insert output in a default way.") - (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) + (guix-info-insert-value-format version + 'guix-package-info-version) (and (guix-entry-value entry 'obsolete) (guix-package-info-insert-obsolete-text))) @@ -809,7 +772,7 @@ If nil, insert output in a default way.") (let* ((installed (guix-entry-value entry 'installed)) (obsolete (guix-entry-value entry 'obsolete)) (action-type (if installed 'delete 'install))) - (guix-info-insert-val-default + (guix-info-insert-value-format output (if installed 'guix-package-info-installed-outputs @@ -840,14 +803,9 @@ If nil, insert output in a default way.") "Face used if a generation is not the current one." :group 'guix-generation-info-faces) -(defvar guix-info-insert-generation-function nil - "Function used to insert a generation information. -It is called with a single argument - alist of generation parameters. -If nil, insert generation in a default way.") - (defun guix-generation-info-insert-number (number &optional _) "Insert generation NUMBER and action buttons." - (guix-info-insert-val-default number 'guix-generation-info-number) + (guix-info-insert-value-format number 'guix-generation-info-number) (guix-info-insert-indent) (guix-info-insert-action-button "Packages" @@ -868,8 +826,8 @@ If nil, insert generation in a default way.") (defun guix-generation-info-insert-current (val entry) "Insert boolean value VAL showing whether this generation is current." (if val - (guix-info-insert-val-default "Yes" 'guix-generation-info-current) - (guix-info-insert-val-default "No" 'guix-generation-info-not-current) + (guix-info-insert-value-format "Yes" 'guix-generation-info-current) + (guix-info-insert-value-format "No" 'guix-generation-info-not-current) (guix-info-insert-indent) (guix-info-insert-action-button "Switch" diff --git a/emacs/guix-utils.el b/emacs/guix-utils.el index fbe0a613da..3c75417a08 100644 --- a/emacs/guix-utils.el +++ b/emacs/guix-utils.el @@ -104,6 +104,28 @@ See `insert-text-button' for the meaning of PROPERTIES." :type (or type 'button) properties))) +(defun guix-buttonize (value button-type separator &rest properties) + "Make BUTTON-TYPE button(s) from VALUE. +Return a string with button(s). + +VALUE should be a string or a list of strings. If it is a list +of strings, buttons are separated with SEPARATOR string. + +PROPERTIES are passed to `guix-insert-button'." + (with-temp-buffer + (let ((labels (if (listp value) value (list value)))) + (guix-mapinsert (lambda (label) + (apply #'guix-insert-button + label button-type properties)) + labels + separator)) + (buffer-substring (point-min) (point-max)))) + +(defun guix-button-type? (symbol) + "Return non-nil, if SYMBOL is a button type." + (and symbol + (get symbol 'button-category-symbol))) + (defun guix-split-insert (val &optional face col separator) "Convert VAL into a string, split it and insert at point. @@ -122,14 +144,11 @@ Separate inserted lines with SEPARATOR." (defun guix-split-string (str &optional col) "Split string STR by lines and return list of result strings. -If COL is non-nil and STR is a one-line string longer than COL, -split it into several short lines." - (let ((strings (split-string str "\n *"))) - (if (and col - (null (cdr strings)) ; if not multi-line - (> (length str) col)) - (split-string (guix-get-filled-string str col) "\n") - strings))) +If COL is non-nil, fill STR to this column." + (let ((str (if col + (guix-get-filled-string str col) + str))) + (split-string str "\n *" t))) (defun guix-get-filled-string (str col) "Return string by filling STR to column COL."