branch: externals/transient
commit 81727bacfce91a119c840d08ac6da92cb6af91c4
Author: Jonas Bernoulli <jo...@bernoul.li>
Commit: Jonas Bernoulli <jo...@bernoul.li>

    transient-cons-option: New suffix class for non-cmdline options
    
    The existing `transient-option' class is suitable for options passed to
    commands.  A suffix that uses that class may represent an argument such
    as "--option".  In the list returned by `transient-args', that argument
    and its value, "value", is represented as one element "--option=value".
    
    For "options" that are not passed to a command that is not appropriate.
    A caller that wants to extract the value of such an option would have
    to parse that string.  `transient-arg-value' can help with that, but it
    always returns a string (or nil).
    
    The new `transient-cons-option' class provides a more direct way to deal
    with "non-command-line options", by representing the key value pair
    using a cons-cell (ARGUMENT . VALUE), not just internally, but in
    particular when handing it of to a caller.  VALUE can have any printable
    type and ARGUMENT can have any printable type, expect string.
    
    We cannot use the term "key" because that is already used to the key
    binding for the command.  Also avoid using "property" because that
    implies a property list, while the value returned by `transient-args'
    is an alist when all infixes use the `transient-cons-options' class.
    Sticking to "argument" also has the advantage, that we don't have to
    implement or generalize a dozen methods.
---
 CHANGELOG           |  4 ++++
 docs/transient.org  | 19 +++++++++++++++++++
 docs/transient.texi | 20 ++++++++++++++++++++
 lisp/transient.el   | 29 +++++++++++++++++++++++++++--
 4 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 492fdf836a..e50027c061 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,10 @@
 - Added new macro ~transient-inline-group~, which inlines an included
   group into a specific menu.
 
+- Added new, experimental suffix class ~transient-cons-option~, which is
+  intended for situations where ~transient-args~ should return an alist,
+  instead of a list of strings (arguments).
+
 * v0.8.8    2025-05-01
 
 - Added option ~transient-common-command-prefix~ to allow using a key
diff --git a/docs/transient.org b/docs/transient.org
index ed92eea0a1..58b0593a10 100644
--- a/docs/transient.org
+++ b/docs/transient.org
@@ -2083,6 +2083,25 @@ object should not affect later invocations.
   it is somewhat likely that future improvements won't be fully
   backward compatible.
 
+- The ~transient-cons-option~ class is intended for situations where
+  ~transient-args~ should return an alist, instead of a list of strings
+  (arguments).  Such suffixes can be specified in prefix definitions
+  like so:
+
+  #+begin_src emacs-lisp
+    (:cons OPTION :key KEY [KEYWORD VALUE]...)
+  #+end_src
+
+  OPTION may be something other than a string, likely a keyword or
+  some other symbol, it is used as the ~car~ of the cons-cell.  When
+  using such an inline definition ~:key~ has to be specified.  In most
+  cases ~:reader~ should also be specified.  When defining such a suffix
+  separately, the "alist key" has to be specified using the ~:variable~
+  keyword argument.
+
+  This class is still experimental it is somewhat likely that future
+  improvements won't be fully backward compatible.
+
 - The ~transient-describe-target~ class is used by the command
   ~transient-describe~.
 
diff --git a/docs/transient.texi b/docs/transient.texi
index 4576cc6b16..0f1a83770d 100644
--- a/docs/transient.texi
+++ b/docs/transient.texi
@@ -2332,6 +2332,26 @@ value of lisp variables.  This class is not fully 
featured yet and
 it is somewhat likely that future improvements won't be fully
 backward compatible.
 
+@item
+The @code{transient-cons-option} class is intended for situations where
+@code{transient-args} should return an alist, instead of a list of strings
+(arguments).  Such suffixes can be specified in prefix definitions
+like so:
+
+@lisp
+(:cons OPTION :key KEY [KEYWORD VALUE]...)
+@end lisp
+
+OPTION may be something other than a string, likely a keyword or
+some other symbol, it is used as the @code{car} of the cons-cell.  When
+using such an inline definition @code{:key} has to be specified.  In most
+cases @code{:reader} should also be specified.  When defining such a suffix
+separately, the "alist key" has to be specified using the @code{:variable}
+keyword argument.
+
+This class is still experimental it is somewhat likely that future
+improvements won't be fully backward compatible.
+
 @item
 The @code{transient-describe-target} class is used by the command
 @code{transient-describe}.
diff --git a/lisp/transient.el b/lisp/transient.el
index b7359c5f0f..4852a34f7f 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -2540,7 +2540,9 @@ value.  Otherwise return CHILDREN as is.")
   (if (transient-switches--eieio-childp obj)
       (cl-call-next-method obj)
     (when-let* (((not (slot-boundp obj 'shortarg)))
-                (shortarg (transient--derive-shortarg (oref obj argument))))
+                (argument (oref obj argument))
+                ((stringp argument))
+                (shortarg (transient--derive-shortarg argument)))
       (oset obj shortarg shortarg))
     (unless (slot-boundp obj 'key)
       (if (slot-boundp obj 'shortarg)
@@ -4682,7 +4684,7 @@ apply the face `transient-unreachable' to the complete 
string."
                       'transient-inactive-argument)))
 
 (cl-defmethod transient-format-value ((obj transient-option))
-  (let ((argument (oref obj argument)))
+  (let ((argument (prin1-to-string (oref obj argument) t)))
     (if-let ((value (oref obj value)))
         (pcase-exhaustive (oref obj multi-value)
           ('nil
@@ -5312,6 +5314,29 @@ as stand-in for elements of exhausted lists."
 (defun transient-lisp-variable--reader (prompt initial-input _history)
   (read--expression prompt initial-input))
 
+;;;; `transient-cons-option'
+
+(defclass transient-cons-option (transient-option)
+  ((format :initform " %k %d: %v"))
+  "[Experimental] Class used for unencoded key-value pairs.")
+
+(cl-defmethod transient-infix-value ((obj transient-cons-option))
+  "Return ARGUMENT and VALUE as a cons-cell or nil if the latter is nil."
+  (and-let* ((value (oref obj value)))
+    (cons (oref obj argument) value)))
+
+(cl-defmethod transient-format-description ((obj transient-cons-option))
+  (or (oref obj description)
+      (let ((description (prin1-to-string (oref obj argument) t)))
+        (if (string-prefix-p ":" description)
+            (substring description 1)
+          description))))
+
+(cl-defmethod transient-format-value ((obj transient-cons-option))
+  (let ((value (oref obj value)))
+    (propertize (prin1-to-string value t) 'face
+                (if value 'transient-value 'transient-inactive-value))))
+
 ;;; _
 (provide 'transient)
 ;; Local Variables:

Reply via email to