emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/transient ea851f3bde 4/4: Turn suffix specifications in


From: Jonas Bernoulli
Subject: [elpa] externals/transient ea851f3bde 4/4: Turn suffix specifications into code instead of data
Date: Thu, 20 Oct 2022 06:23:39 -0400 (EDT)

branch: externals/transient
commit ea851f3bde0b769b04ad03ab1a1341c013d0ddc6
Author: Jonas Bernoulli <jonas@bernoul.li>
Commit: Jonas Bernoulli <jonas@bernoul.li>

    Turn suffix specifications into code instead of data
    
    The macro `transient-define-prefix' manipulates its GROUP arguments,
    but until now it expanded to new vectors using vector syntax, i.e.,
    data.  An effort was made to evaluate lambda expressions, but that did
    not really work.
    
    Now the GROUP arguments are expanded into code, which evaluates to new
    vectors.  This way lambda expressions are automatically evaluated and
    byte-compiled, but we now have to take care to quote certain lists and
    symbols.
    
    Functions such as `transient-insert-suffix' take group and suffix
    specs of the same form as `transient-define-prefix' but because there
    is no macro expansion step, they have to `eval' the result of parsing
    these specifications.
    
    Add a new macro `transient-define-groups', which can be used to define
    suffix groups that are shared between multiple prefix commands, but
    don't use it for `transient-common-commands' to avoid having to make
    a bunch of functions available at compile time.
---
 docs/transient.org  |  54 +++++++++++++-----
 docs/transient.texi |  34 +++++++++---
 lisp/transient.el   | 157 ++++++++++++++++++++++++++++++++--------------------
 3 files changed, 163 insertions(+), 82 deletions(-)

diff --git a/docs/transient.org b/docs/transient.org
index b658341a2d..27401836f1 100644
--- a/docs/transient.org
+++ b/docs/transient.org
@@ -861,6 +861,18 @@ that is used to invoke that transient.
   For example, the scope of the ~magit-branch-configure~ transient is
   the branch whose variables are being configured.
 
+It is possible to define one or more groups independently of a prefix
+definition, which is useful when those groups are to be used by more
+than just one prefix command.
+
+- Macro: transient-define-groups name group... ::
+
+  This macro defines one or more groups of infix and suffix commands
+  and stores them in a property of the symbol {{{var(NAME)}}}.  
{{{var(GROUP)}}} has the
+  same form as for ~transient-define-prefix~.  Subsequently {{{var(NAME)}}} can
+  be used in a {{{var(GROUP)}}} of ~transient-define-prefix~, as described in 
the
+  next section.
+
 ** Binding Suffix and Infix Commands
 
 The macro ~transient-define-prefix~ is used to define a transient.
@@ -950,23 +962,35 @@ constructor of that class.
   contained in a group are right padded, effectively aligning the
   descriptions.
 
-The {{{var(ELEMENT)}}}s are either all subgroups (vectors), or all suffixes
-(lists) and strings.  (At least currently no group type exists that
-would allow mixing subgroups with commands at the same level, though
-in principle there is nothing that prevents that.)
+The {{{var(ELEMENT)}}}s are either all subgroups, or all suffixes and strings.
+(At least currently no group type exists that would allow mixing
+subgroups with commands at the same level, though in principle there
+is nothing that prevents that.)
 
 If the {{{var(ELEMENT)}}}s are not subgroups, then they can be a mixture of 
lists
-that specify commands and strings.  Strings are inserted verbatim.
-The empty string can be used to insert gaps between suffixes, which is
-particularly useful if the suffixes are outlined as a table.
-
-Variables are supported inside group specifications.  For example in
-place of a direct subgroup specification, a variable can be used whose
-value is a vector that qualifies as a group specification.  Likewise,
-a variable can be used where a suffix specification is expected.
-Lists of group or suffix specifications are also supported.  Indirect
-specifications are resolved when the transient prefix is being
-defined.
+that specify commands and strings.  Strings are inserted verbatim into
+the buffer.  The empty string can be used to insert gaps between
+suffixes, which is particularly useful if the suffixes are outlined as
+a table.
+
+Inside group specifications, including inside contained suffix
+specifications, nothing has to be quoted and quoting anyway is
+invalid.
+
+How symbols are treated, depends on context.  Inside suffix
+specifications they often name functions.  However if they appear in
+a place where a group is expected, then they are treated as indirect
+group specifications. Such a symbol must have an associated group
+specification, created using ~transient-define-groups~.
+
+Likewise a symbol can appear in a place where a suffix specification
+is expected.  The value of the ~transient--layout~ property of that
+symbol must be a single suffix specification or a list of such
+specifications.  Currently no macro exist that would create such a
+symbol, and this feature should usually not be used.
+
+The value following a keyword, can be explicitly unquoted using ~,~.
+This feature is experimental and should be avoided as well.
 
 The form of suffix specifications is documented in the next node.
 
diff --git a/docs/transient.texi b/docs/transient.texi
index bcc29f418f..78b44f5943 100644
--- a/docs/transient.texi
+++ b/docs/transient.texi
@@ -1041,6 +1041,18 @@ For example, the scope of the 
@code{magit-branch-configure} transient is
 the branch whose variables are being configured.
 @end defmac
 
+It is possible to define one or more groups independently of a prefix
+definition, which is useful when those groups are to be used by more
+than just one prefix command.
+
+@defmac transient-define-groups name group...
+This macro defines one or more groups of infix and suffix commands
+and stores them in a property of the symbol @var{NAME}.  @var{GROUP} has the
+same form as for @code{transient-define-prefix}.  Subsequently @var{NAME} can
+be used in a @var{GROUP} of @code{transient-define-prefix}, as described in the
+next section.
+@end defmac
+
 @node Binding Suffix and Infix Commands
 @section Binding Suffix and Infix Commands
 
@@ -1153,13 +1165,21 @@ that specify commands and strings.  Strings are 
inserted verbatim.
 The empty string can be used to insert gaps between suffixes, which is
 particularly useful if the suffixes are outlined as a table.
 
-Variables are supported inside group specifications.  For example in
-place of a direct subgroup specification, a variable can be used whose
-value is a vector that qualifies as a group specification.  Likewise,
-a variable can be used where a suffix specification is expected.
-Lists of group or suffix specifications are also supported.  Indirect
-specifications are resolved when the transient prefix is being
-defined.
+Inside group specifications, including contained suffix specifications,
+nothing has to be quoted.  How symbols are treated, depends on context.
+In most places they are expected to name functions.  However if they
+appear in a place where a group vector is expected, then they are
+treated as indirect group specifications. Such a symbol must have an
+associated group specification, created using @code{transient-define-groups}.
+
+Likewise a symbol can appear in a place where a suffix specification
+is expected.  The value of the @code{transient--layout} property of that
+symbol must be a single suffix specification or a list of such
+specifications.  Currently no macro exist that would create such a
+symbol, and this feature should usually not be used.
+
+The value following a keyword, can be explicitly unquoted using @code{,}.
+This feature is experimental and should be avoided as well.
 
 The form of suffix specifications is documented in the next node.
 
diff --git a/lisp/transient.el b/lisp/transient.el
index 535effed56..b8371d9619 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -893,8 +893,20 @@ to the setup function:
        (put ',name 'transient--prefix
             (,(or class 'transient-prefix) :command ',name ,@slots))
        (put ',name 'transient--layout
-            ',(cl-mapcan (lambda (s) (transient--parse-child name s))
-                         suffixes)))))
+            (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s))
+                               suffixes))))))
+
+(defmacro transient-define-groups (name &rest groups)
+  "Define one or more GROUPS and store them in symbol NAME.
+GROUPS, defined using this macro, can be used inside the
+definition of transient prefix commands, by using the symbol
+NAME where a group vector is expected.  GROUPS has the same
+form as for `transient-define-prefix'."
+  (declare (debug (&define name [&rest vectorp]))
+           (indent defun))
+  `(put ',name 'transient--layout
+        (list ,@(cl-mapcan (lambda (group) (transient--parse-child name group))
+                           groups))))
 
 (defmacro transient-define-suffix (name arglist &rest args)
   "Define NAME as a transient suffix command.
@@ -1000,9 +1012,8 @@ example, sets a variable use `transient-define-infix' 
instead.
           (push k keys)
           (push v keys))))
     (while (let ((arg (car args)))
-             (if (vectorp arg)
-                 (setcar args (eval (cdr (backquote-process arg))))
-               (and arg (symbolp arg))))
+             (or (vectorp arg)
+                 (and arg (symbolp arg))))
       (push (pop args) suffixes))
     (list (if (eq (car-safe class) 'quote)
               (cadr class)
@@ -1035,17 +1046,24 @@ example, sets a variable use `transient-define-infix' 
instead.
       (when (stringp car)
         (setq args (plist-put args :description pop)))
       (while (keywordp car)
-        (let ((k pop))
-          (if (eq k :class)
-              (setq class pop)
-            (setq args (plist-put args k pop)))))
-      (vector (or level transient--default-child-level)
-              (or class
-                  (if (vectorp car)
-                      'transient-columns
-                    'transient-column))
-              args
-              (cl-mapcan (lambda (s) (transient--parse-child prefix s)) 
spec)))))
+        (let ((key pop)
+              (val pop))
+          (cond ((eq key :class)
+                 (setq class (list 'quote val)))
+                ((or (symbolp val)
+                     (and (listp val) (not (eq (car val) 'lambda))))
+                 (setq args (plist-put args key (list 'quote val))))
+                ((setq args (plist-put args key val))))))
+      (list 'vector
+            (or level transient--default-child-level)
+            (or class
+                (if (vectorp car)
+                    (quote 'transient-columns)
+                  (quote 'transient-column)))
+            (and args (cons 'list args))
+            (cons 'list
+                  (cl-mapcan (lambda (s) (transient--parse-child prefix s))
+                             spec))))))
 
 (defun transient--parse-suffix (prefix spec)
   (let (level class args)
@@ -1057,17 +1075,19 @@ example, sets a variable use `transient-define-infix' 
instead.
       (when (or (stringp car)
                 (vectorp car))
         (setq args (plist-put args :key pop)))
-      (when (or (stringp car)
-                (eq (car-safe car) 'lambda)
-                (and (symbolp car)
-                     (not (commandp car))
-                     (commandp (cadr spec))))
+      (cond
+       ((or (stringp car)
+            (eq (car-safe car) 'lambda))
         (setq args (plist-put args :description pop)))
+       ((and (symbolp car)
+             (not (commandp car))
+             (commandp (cadr spec)))
+        (setq args (plist-put args :description (list 'quote pop)))))
       (cond
        ((keywordp car)
         (error "Need command, got %S" car))
        ((symbolp car)
-        (setq args (plist-put args :command pop)))
+        (setq args (plist-put args :command (list 'quote pop))))
        ((and (commandp car)
              (not (stringp car)))
         (let ((cmd pop)
@@ -1076,7 +1096,7 @@ example, sets a variable use `transient-define-infix' 
instead.
                                    (or (plist-get args :description)
                                        (plist-get args :key))))))
           (defalias sym cmd)
-          (setq args (plist-put args :command sym))))
+          (setq args (plist-put args :command (list 'quote sym)))))
        ((or (stringp car)
             (and car (listp car)))
         (let ((arg pop))
@@ -1090,11 +1110,14 @@ example, sets a variable use `transient-define-infix' 
instead.
                (setq args (plist-put args :shortarg shortarg)))
              (setq args (plist-put args :argument arg))))
           (setq args (plist-put args :command
-                                (intern (format "transient:%s:%s"
-                                                prefix arg))))
+                                (list 'quote (intern (format "transient:%s:%s"
+                                                             prefix arg)))))
           (cond ((and car (not (keywordp car)))
                  (setq class 'transient-option)
-                 (setq args (plist-put args :reader pop)))
+                 (setq args (plist-put args :reader
+                                       (if (symbolp car)
+                                           (list 'quote pop)
+                                         pop))))
                 ((not (string-suffix-p "=" arg))
                  (setq class 'transient-switch))
                 (t
@@ -1102,17 +1125,23 @@ example, sets a variable use `transient-define-infix' 
instead.
        (t
         (error "Needed command or argument, got %S" car)))
       (while (keywordp car)
-        (let ((k pop))
-          (cl-case k
-            (:class (setq class pop))
-            (:level (setq level pop))
-            (t (setq args (plist-put args k pop)))))))
+        (let ((key pop)
+              (val pop))
+          (cond ((eq key :class) (setq class (list 'quote val)))
+                ((eq key :level) (setq level val))
+                ((eq (car-safe val) '\,)
+                 (setq args (plist-put args key (cadr val))))
+                ((or (symbolp val)
+                     (and (listp val) (not (eq (car val) 'lambda))))
+                 (setq args (plist-put args key (list 'quote val))))
+                ((setq args (plist-put args key val)))))))
     (unless (plist-get args :key)
       (when-let ((shortarg (plist-get args :shortarg)))
         (setq args (plist-put args :key shortarg))))
-    (list (or level transient--default-child-level)
-          (or class 'transient-suffix)
-          args)))
+    (list 'list
+          (or level transient--default-child-level)
+          (list 'quote (or class 'transient-suffix))
+          (cons 'list args))))
 
 (defun transient--default-infix-command ()
   (cons 'lambda
@@ -1148,6 +1177,7 @@ example, sets a variable use `transient-define-infix' 
instead.
                 (string suffix)))
          (mem (transient--layout-member loc prefix))
          (elt (car mem)))
+    (setq suf (eval suf))
     (cond
      ((not mem)
       (message "Cannot insert %S into %s; %s not found"
@@ -1558,32 +1588,39 @@ to `transient-predicate-map'.  Also see 
`transient-base-map'.")
 
 (put 'transient-common-commands
      'transient--layout
-     (cl-mapcan
-      (lambda (s) (transient--parse-child 'transient-common-commands s))
-      `([:hide ,(lambda ()
-                  (and (not (memq (car (bound-and-true-p
-                                        transient--redisplay-key))
-                                  transient--common-command-prefixes))
-                       (not transient-show-common-commands)))
-         ["Value commands"
-          ("C-x s  " "Set"            transient-set)
-          ("C-x C-s" "Save"           transient-save)
-          ("C-x C-k" "Reset"          transient-reset)
-          ("C-x p  " "Previous value" transient-history-prev)
-          ("C-x n  " "Next value"     transient-history-next)]
-         ["Sticky commands"
-          ;; Like `transient-sticky-map' except that
-          ;; "C-g" has to be bound to a different command.
-          ("C-g" "Quit prefix or transient" transient-quit-one)
-          ("C-q" "Quit transient stack"     transient-quit-all)
-          ("C-z" "Suspend transient stack"  transient-suspend)]
-         ["Customize"
-          ("C-x t" transient-toggle-common
-           :description ,(lambda ()
-                           (if transient-show-common-commands
-                               "Hide common commands"
-                             "Show common permanently")))
-          ("C-x l" "Show/hide suffixes" transient-set-level)]])))
+     (list
+      (eval
+       (car (transient--parse-child
+             'transient-common-commands
+             (vector
+              :hide
+              (lambda ()
+                (and (not (memq
+                           (car (bound-and-true-p transient--redisplay-key))
+                           transient--common-command-prefixes))
+                     (not transient-show-common-commands)))
+              (vector
+               "Value commands"
+               (list "C-x s  " "Set"            #'transient-set)
+               (list "C-x C-s" "Save"           #'transient-save)
+               (list "C-x C-k" "Reset"          #'transient-reset)
+               (list "C-x p  " "Previous value" #'transient-history-prev)
+               (list "C-x n  " "Next value"     #'transient-history-next))
+              (vector
+               "Sticky commands"
+               ;; Like `transient-sticky-map' except that
+               ;; "C-g" has to be bound to a different command.
+               (list "C-g" "Quit prefix or transient" #'transient-quit-one)
+               (list "C-q" "Quit transient stack"     #'transient-quit-all)
+               (list "C-z" "Suspend transient stack"  #'transient-suspend))
+              (vector
+               "Customize"
+               (list "C-x t" 'transient-toggle-common :description
+                     (lambda ()
+                       (if transient-show-common-commands
+                           "Hide common commands"
+                         "Show common permanently")))
+               (list "C-x l" "Show/hide suffixes" #'transient-set-level))))))))
 
 (defvar transient-popup-navigation-map
   (let ((map (make-sparse-keymap)))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]