From 7735c503b5b8dfe7d8963207f4f2cf0b7dfd3894 Mon Sep 17 00:00:00 2001 From: Alex Kost Date: Fri, 20 Nov 2015 12:38:31 +0300 Subject: [PATCH] emacs: Factorize macros for defining interfaces. Make a root 'guix-buffer-define-interface' macro. It should generate a common code for any type of interface. Inherit 'guix-info-define-interface' and 'guix-list-define-interface' from it. They should provide a general 'info'/'list' interface for any data. Finally, make 'guix-ui-define-interface' for the common code for interfaces to Guix packages and generations, and inherit 'guix-ui-info-define-interface' and 'guix-ui-list-define-interface' from it. * emacs/guix-base.el (guix-define-buffer-type): Rename to... (guix-buffer-define-interface): ... this. Rename internal variables ('buf-' -> 'buffer-'). Move ':required' keyword to 'guix-ui-define-interface'. * emacs/guix-info.el (guix-info-define-interface): New macro. (guix-info-font-lock-keywords): New variable. * emacs/guix-list.el (guix-list-define-entry-type): Rename to... (guix-list-define-interface): ... this. (guix-list-font-lock-keywords): New variable. (guix-list-describe-ids): Move and rename to... * emacs/guix-ui.el: New file. (guix-ui-list-describe): ... this. (guix-ui-define-interface, guix-ui-info-define-interface) (guix-ui-list-define-interface): New macros. (guix-ui-font-lock-keywords): New variable. * emacs.am (ELFILES): Add "emacs/guix-ui.el" --- emacs.am | 1 + emacs/guix-base.el | 120 +++++++++++++++++++++------------------------ emacs/guix-info.el | 30 ++++++++++-- emacs/guix-list.el | 47 +++++++++--------- emacs/guix-ui.el | 109 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 92 deletions(-) create mode 100644 emacs/guix-ui.el diff --git a/emacs.am b/emacs.am index a205b0a359..ad7de78657 100644 --- a/emacs.am +++ b/emacs.am @@ -40,6 +40,7 @@ ELFILES = \ emacs/guix-prettify.el \ emacs/guix-profiles.el \ emacs/guix-read.el \ + emacs/guix-ui.el \ emacs/guix-utils.el \ emacs/guix.el diff --git a/emacs/guix-base.el b/emacs/guix-base.el index f55e1c67e0..67b83cfbe3 100644 --- a/emacs/guix-base.el +++ b/emacs/guix-base.el @@ -23,7 +23,7 @@ ;; package. ;; List and info buffers have many common patterns that are defined -;; using `guix-define-buffer-type' macro from this file. +;; using `guix-buffer-define-interface' macro from this file. ;;; Code: @@ -337,103 +337,93 @@ VAL is a value of this parameter.") (concat (symbol-name entry-type) "-")) (symbol-name buffer-type) "-" postfix))) -(defmacro guix-define-buffer-type (buf-type entry-type &rest args) - "Define common for BUF-TYPE buffers for displaying ENTRY-TYPE entries. - -In the text below TYPE means ENTRY-TYPE-BUF-TYPE. - -This macro defines `guix-TYPE-mode', a custom group and several -user variables. +(defmacro guix-buffer-define-interface (buffer-type entry-type &rest args) + "Define BUFFER-TYPE interface for displaying ENTRY-TYPE entries. +Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ... +In the following description TYPE means ENTRY-TYPE-BUFFER-TYPE. The following stuff should be defined outside this macro: - - `guix-BUF-TYPE-mode' - parent mode for the defined mode. + - `guix-BUFFER-TYPE-mode' - parent mode of the generated mode. - `guix-TYPE-mode-initialize' (optional) - function for additional mode settings; it is called without arguments. -Remaining argument (ARGS) should have a form [KEYWORD VALUE] ... The -following keywords are available: +Optional keywords: - - `:buffer-name' - default value for the defined + - `:buffer-name' - default value of the generated `guix-TYPE-buffer-name' variable. - - `:required' - default value for the defined - `guix-TYPE-required-params' variable. - - - `:history-size' - default value for the defined + - `:history-size' - default value of the generated `guix-TYPE-history-size' variable. - - `:revert' - default value for the defined - `guix-TYPE-revert-no-confirm' variable." - (let* ((entry-type-str (symbol-name entry-type)) - (buf-type-str (symbol-name buf-type)) - (Entry-type-str (capitalize entry-type-str)) - (Buf-type-str (capitalize buf-type-str)) - (entry-str (concat entry-type-str " entries")) - (buf-str (concat buf-type-str " buffer")) - (prefix (concat "guix-" entry-type-str "-" buf-type-str)) - (group (intern prefix)) - (faces-group (intern (concat prefix "-faces"))) - (mode-map-str (concat prefix "-mode-map")) - (parent-mode (intern (concat "guix-" buf-type-str "-mode"))) - (mode (intern (concat prefix "-mode"))) - (mode-init-fun (intern (concat prefix "-mode-initialize"))) - (buf-name-var (intern (concat prefix "-buffer-name"))) - (revert-var (intern (concat prefix "-revert-no-confirm"))) - (history-var (intern (concat prefix "-history-size"))) - (params-var (intern (concat prefix "-required-params")))) + - `:revert-confirm?' - default value of the generated + `guix-TYPE-revert-confirm' variable." + (declare (indent 2)) + (let* ((entry-type-str (symbol-name entry-type)) + (buffer-type-str (symbol-name buffer-type)) + (Entry-type-str (capitalize entry-type-str)) + (Buffer-type-str (capitalize buffer-type-str)) + (entry-str (concat entry-type-str " entries")) + (buffer-str (concat buffer-type-str " buffer")) + (prefix (concat "guix-" entry-type-str "-" + buffer-type-str)) + (group (intern prefix)) + (faces-group (intern (concat prefix "-faces"))) + (mode-map-str (concat prefix "-mode-map")) + (parent-mode (intern (concat "guix-" buffer-type-str "-mode"))) + (mode (intern (concat prefix "-mode"))) + (mode-init-fun (intern (concat prefix "-mode-initialize"))) + (buffer-name-var (intern (concat prefix "-buffer-name"))) + (history-size-var (intern (concat prefix "-history-size"))) + (revert-confirm-var (intern (concat prefix "-revert-confirm")))) (guix-keyword-args-let args - ((params-val :required '(id)) - (history-val :history-size 20) - (revert-val :revert) - (buf-name-val :buffer-name - (format "*Guix %s %s*" Entry-type-str Buf-type-str))) + ((buffer-name-val :buffer-name + (format "*Guix %s %s*" + Entry-type-str Buffer-type-str)) + (history-size-val :history-size 20) + (revert-confirm-val :revert-confirm? t)) `(progn (defgroup ,group nil - ,(concat Buf-type-str " buffer with " entry-str ".") + ,(format "Display '%s' entries in '%s' buffer." + entry-type-str buffer-type-str) :prefix ,(concat prefix "-") - :group ',(intern (concat "guix-" buf-type-str))) + :group ',(intern (concat "guix-" buffer-type-str))) (defgroup ,faces-group nil - ,(concat "Faces for " buf-type-str " buffer with " entry-str ".") - :group ',(intern (concat "guix-" buf-type-str "-faces"))) + ,(format "Faces for displaying '%s' entries in '%s' buffer." + entry-type-str buffer-type-str) + :group ',(intern (concat "guix-" buffer-type-str "-faces"))) - (defcustom ,buf-name-var ,buf-name-val - ,(concat "Default name of the " buf-str " for displaying " entry-str ".") + (defcustom ,buffer-name-var ,buffer-name-val + ,(format "\ +Default name of '%s' buffer for displaying '%s' entries." + buffer-type-str entry-type-str) :type 'string :group ',group) - (defcustom ,history-var ,history-val - ,(concat "Maximum number of items saved in the history of the " buf-str ".\n" - "If 0, the history is disabled.") + (defcustom ,history-size-var ,history-size-val + ,(format "\ +Maximum number of items saved in history of `%S' buffer. +If 0, the history is disabled." + buffer-name-var) :type 'integer :group ',group) - (defcustom ,revert-var ,revert-val - ,(concat "If non-nil, do not ask to confirm for reverting the " buf-str ".") + (defcustom ,revert-confirm-var ,revert-confirm-val + ,(format "\ +If non-nil, ask to confirm for reverting `%S' buffer." + buffer-name-var) :type 'boolean :group ',group) - (defvar ,params-var ',params-val - ,(concat "List of required " entry-type-str " parameters.\n\n" - "Displayed parameters and parameters from this list are received\n" - "for each " entry-type-str ".\n\n" - "May be a special value `all', in which case all supported\n" - "parameters are received (this may be very slow for a big number\n" - "of entries).\n\n" - "Do not remove `id' from this list as it is required for\n" - "identifying an entry.")) - - (define-derived-mode ,mode ,parent-mode ,(concat "Guix-" Buf-type-str) + (define-derived-mode ,mode ,parent-mode ,(concat "Guix-" Buffer-type-str) ,(concat "Major mode for displaying information about " entry-str ".\n\n" "\\{" mode-map-str "}") (setq-local revert-buffer-function 'guix-revert-buffer) - (setq-local guix-history-size ,history-var) + (setq-local guix-history-size ,history-size-var) (and (fboundp ',mode-init-fun) (,mode-init-fun))))))) -(put 'guix-define-buffer-type 'lisp-indent-function 'defun) - ;;; Getting and displaying info about packages and generations diff --git a/emacs/guix-info.el b/emacs/guix-info.el index 9c46810f60..d31e0377ff 100644 --- a/emacs/guix-info.el +++ b/emacs/guix-info.el @@ -28,6 +28,7 @@ (require 'guix-base) (require 'guix-entry) (require 'guix-utils) +(require 'guix-ui) (defgroup guix-info nil "General settings for info buffers." @@ -455,6 +456,8 @@ See `insert-text-button' for the meaning of PROPERTIES." properties)) +;;; Major mode and interface definer + (defvar guix-info-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent @@ -466,11 +469,21 @@ See `insert-text-button' for the meaning of PROPERTIES." (define-derived-mode guix-info-mode special-mode "Guix-Info" "Parent mode for displaying information in info buffers.") +(defmacro guix-info-define-interface (entry-type &rest args) + "Define 'info' interface for displaying ENTRY-TYPE entries. +Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ... + +The rest keyword arguments are passed to +`guix-buffer-define-interface' macro." + (declare (indent 1)) + `(guix-buffer-define-interface info ,entry-type + ,@args)) + ;;; Displaying packages -(guix-define-buffer-type info package - :required (id name version installed non-unique)) +(guix-ui-info-define-interface package + :required '(id name version installed non-unique)) (defface guix-package-info-heading '((t :inherit guix-info-heading)) @@ -758,7 +771,7 @@ This function is used to hide a \"Download\" button if needed." (guix-ui-info-define-interface output :buffer-name "*Guix Package Info*" - :required (id package-id installed non-unique)) + :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." @@ -786,7 +799,7 @@ This function is used to hide a \"Download\" button if needed." ;;; Displaying generations -(guix-define-buffer-type info generation) +(guix-ui-info-define-interface generation) (defface guix-generation-info-number '((t :inherit font-lock-keyword-face)) @@ -837,6 +850,15 @@ This function is used to hide a \"Download\" button if needed." "Switch to this generation (make it the current one)" 'number (guix-entry-value entry 'number)))) + +(defvar guix-info-font-lock-keywords + (eval-when-compile + `((,(rx "(" (group "guix-info-define-interface") + symbol-end) + . 1)))) + +(font-lock-add-keywords 'emacs-lisp-mode guix-info-font-lock-keywords) + (provide 'guix-info) ;;; guix-info.el ends here diff --git a/emacs/guix-list.el b/emacs/guix-list.el index e1fc199639..8943e8f589 100644 --- a/emacs/guix-list.el +++ b/emacs/guix-list.el @@ -30,6 +30,7 @@ (require 'guix-base) (require 'guix-entry) (require 'guix-utils) +(require 'guix-ui) (defgroup guix-list nil "General settings for list buffers." @@ -73,17 +74,12 @@ With prefix argument, describe entries marked with any mark." count))) (guix-list-describe-entries entry-type ids)))) -(defun guix-list-describe-ids (ids) - "Describe entries with IDS (list of identifiers)." - (apply #'guix-get-show-entries - guix-profile 'info guix-entry-type 'id ids)) - ;;; Wrappers for 'list' variables (defvar guix-list-data nil "Alist with 'list' data. -This alist is filled by `guix-list-define-entry-type' macro.") +This alist is filled by `guix-list-define-interface' macro.") (defun guix-list-value (entry-type symbol) "Return SYMBOL's value for ENTRY-TYPE from `guix-list-data'." @@ -416,8 +412,8 @@ Same as `tabulated-list-sort', but also restore marks after sorting." (setq-local guix-list-marks (guix-list-marks entry-type)) (tabulated-list-init-header)) -(defmacro guix-list-define-entry-type (entry-type &rest args) - "Define common stuff for displaying ENTRY-TYPE entries in list buffers. +(defmacro guix-list-define-interface (entry-type &rest args) + "Define 'list' interface for displaying ENTRY-TYPE entries. Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ... Required keywords: @@ -435,7 +431,10 @@ Optional keywords: - `:marks' - default value of the generated `guix-ENTRY-TYPE-list-marks' variable. -" + +The rest keyword arguments are passed to +`guix-buffer-define-interface' macro." + (declare (indent 1)) (let* ((entry-type-str (symbol-name entry-type)) (prefix (concat "guix-" entry-type-str "-list")) (group (intern prefix)) @@ -518,17 +517,15 @@ See also `guix-list-describe'." (format . ,format-var) (sort-key . ,sort-key-var) (marks . ,marks-var)) - 'guix-list-data ',entry-type))))) + 'guix-list-data ',entry-type) -(put 'guix-list-define-entry-type 'lisp-indent-function 'defun) + (guix-buffer-define-interface list ,entry-type + ,@%foreign-args))))) ;;; Displaying packages -(guix-define-buffer-type list package) - -(guix-list-define-entry-type package - :describe-function 'guix-list-describe-ids +(guix-ui-list-define-interface package :format '((name guix-package-list-get-name 20 t) (version nil 10 nil) (outputs nil 13 t) @@ -717,17 +714,15 @@ The specification is suitable for `guix-process-package-actions'." ;;; Displaying outputs -(guix-define-buffer-type list output +(guix-ui-list-define-interface output :buffer-name "*Guix Package List*" - :required (package-id)) - -(guix-list-define-entry-type output :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 guix-list-get-one-line 30 nil)) + :required '(package-id) :sort-key '(name) :marks '((install . ?I) (upgrade . ?U) @@ -816,10 +811,7 @@ See `guix-package-info-type'." ;;; Displaying generations -(guix-define-buffer-type list generation) - -(guix-list-define-entry-type generation - :describe-function 'guix-list-describe-ids +(guix-ui-list-define-interface generation :format '((number nil 5 guix-list-sort-numerically-0 :right-align t) (current guix-generation-list-get-current 10 t) (time guix-list-get-time 20 t) @@ -954,6 +946,15 @@ With ARG, mark all generations for deletion." (user-error "No generations marked for deletion")) (guix-delete-generations guix-profile marked (current-buffer)))) + +(defvar guix-list-font-lock-keywords + (eval-when-compile + `((,(rx "(" (group "guix-list-define-interface") + symbol-end) + . 1)))) + +(font-lock-add-keywords 'emacs-lisp-mode guix-list-font-lock-keywords) + (provide 'guix-list) ;;; guix-list.el ends here diff --git a/emacs/guix-ui.el b/emacs/guix-ui.el new file mode 100644 index 0000000000..4f4688250b --- /dev/null +++ b/emacs/guix-ui.el @@ -0,0 +1,109 @@ +;;; guix-ui.el --- Common code for Guix package management interface -*- lexical-binding: t -*- + +;; Copyright © 2014, 2015 Alex Kost + +;; This file is part of GNU Guix. + +;; GNU 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. + +;; GNU 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 this program. If not, see . + +;;; Commentary: + +;; This file provides some general code for 'list'/'info' interfaces for +;; packages and generations. + +;;; Code: + +(require 'cl-lib) +(require 'guix-utils) + +(defun guix-ui-list-describe (ids) + "Describe 'ui' entries with IDS (list of identifiers)." + (apply #'guix-get-show-entries + guix-profile 'info guix-entry-type 'id ids)) + + +;;; Interface definers + +(defmacro guix-ui-define-interface (buffer-type entry-type &rest args) + "Define BUFFER-TYPE interface for displaying ENTRY-TYPE entries. +Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ... +In the following description TYPE means ENTRY-TYPE-BUFFER-TYPE. + +Optional keywords: + + - `:required' - default value of the generated + `guix-TYPE-required-params' variable. + +The rest keyword arguments are passed to +`guix-BUFFER-TYPE-define-interface' macro." + (declare (indent 2)) + (let* ((entry-type-str (symbol-name entry-type)) + (buffer-type-str (symbol-name buffer-type)) + (prefix (concat "guix-" entry-type-str "-" + buffer-type-str)) + (required-var (intern (concat prefix "-required-params"))) + (definer (intern (format "guix-%s-define-interface" + buffer-type-str)))) + (guix-keyword-args-let args + ((required-val :required ''(id))) + `(progn + (defvar ,required-var ,required-val + ,(format "\ +List of the required '%s' parameters for '%s' buffer. +These parameters are received along with the displayed parameters." + entry-type-str buffer-type-str)) + + (,definer ,entry-type + ,@%foreign-args))))) + +(defmacro guix-ui-info-define-interface (entry-type &rest args) + "Define 'info' interface for displaying ENTRY-TYPE entries. +See `guix-ui-define-interface'." + (declare (indent 1)) + `(guix-ui-define-interface info ,entry-type + ,@args)) + +(defmacro guix-ui-list-define-interface (entry-type &rest args) + "Define 'list' interface for displaying ENTRY-TYPE entries. +Remaining arguments (ARGS) should have a form [KEYWORD VALUE] ... + +Optional keywords: + + - `:describe-function' - default value of the generated + `guix-ENTRY-TYPE-list-describe-function' variable (if not + specified, use `guix-ui-list-describe'). + +The rest keyword arguments are passed to +`guix-ui-define-interface' macro." + (declare (indent 1)) + (guix-keyword-args-let args + ((describe-val :describe-function)) + `(guix-ui-define-interface list ,entry-type + :describe-function ,(or describe-val ''guix-ui-list-describe) + ,@args))) + + +(defvar guix-ui-font-lock-keywords + (eval-when-compile + `((,(rx "(" (group (or "guix-ui-define-interface" + "guix-ui-info-define-interface" + "guix-ui-list-define-interface")) + symbol-end) + . 1)))) + +(font-lock-add-keywords 'emacs-lisp-mode guix-ui-font-lock-keywords) + +(provide 'guix-ui) + +;;; guix-ui.el ends here