aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonas Bernoulli2024-06-18 17:02:20 +0200
committerJonas Bernoulli2024-06-18 17:02:20 +0200
commitdceb28a1cfad276cdf070a9b2ca4d8f3ab3c1a85 (patch)
treed42badfbdf37aa45b314cdbaddf3b1520a34b8e0
parentdc308348a904d69916ca6ab1eb587aff03e8421c (diff)
downloademacs-dceb28a1cfad276cdf070a9b2ca4d8f3ab3c1a85.tar.gz
emacs-dceb28a1cfad276cdf070a9b2ca4d8f3ab3c1a85.zip
Update to Transient v0.7.0-1-g482bc777
-rw-r--r--doc/misc/transient.texi37
-rw-r--r--lisp/transient.el361
2 files changed, 231 insertions, 167 deletions
diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi
index 0aa520237f7..7e8ffcf91bf 100644
--- a/doc/misc/transient.texi
+++ b/doc/misc/transient.texi
@@ -31,7 +31,7 @@ General Public License for more details.
31@finalout 31@finalout
32@titlepage 32@titlepage
33@title Transient User and Developer Manual 33@title Transient User and Developer Manual
34@subtitle for version 0.6.0 34@subtitle for version 0.7.0
35@author Jonas Bernoulli 35@author Jonas Bernoulli
36@page 36@page
37@vskip 0pt plus 1filll 37@vskip 0pt plus 1filll
@@ -53,7 +53,7 @@ resource to get over that hurdle is Psionic K's interactive tutorial,
53available at @uref{https://github.com/positron-solutions/transient-showcase}. 53available at @uref{https://github.com/positron-solutions/transient-showcase}.
54 54
55@noindent 55@noindent
56This manual is for Transient version 0.6.0. 56This manual is for Transient version 0.7.0.
57 57
58@insertcopying 58@insertcopying
59@end ifnottex 59@end ifnottex
@@ -1112,7 +1112,8 @@ Transients}) and adds the transient's infix and suffix bindings, as
1112described below. 1112described below.
1113 1113
1114Users and third-party packages can add additional bindings using 1114Users and third-party packages can add additional bindings using
1115functions such as @code{transient-insert-suffix} (@pxref{Modifying Existing Transients}). These functions take a ``suffix specification'' as one of 1115functions such as @code{transient-insert-suffix} (@pxref{Modifying Existing Transients}).
1116These functions take a ``suffix specification'' as one of
1116their arguments, which has the same form as the specifications used in 1117their arguments, which has the same form as the specifications used in
1117@code{transient-define-prefix}. 1118@code{transient-define-prefix}.
1118 1119
@@ -1380,16 +1381,12 @@ This macro defines @var{NAME} as a transient infix command.
1380reserved for future use. @var{DOCSTRING} is the documentation string and 1381reserved for future use. @var{DOCSTRING} is the documentation string and
1381is optional. 1382is optional.
1382 1383
1383The keyword-value pairs are mandatory. All transient infix commands 1384At least one key-value pair is required. All transient infix
1384are @code{equal} to each other (but not @code{eq}), so it is meaningless to define 1385commands are @code{equal} to each other (but not @code{eq}). It is meaningless
1385an infix command without also setting at least @code{:class} and one other 1386to define an infix command, without providing at least one keyword
1386keyword (which it is depends on the used class, usually @code{:argument} or 1387argument (usually @code{:argument} or @code{:variable}, depending on the class).
1387@code{:variable}). 1388The suffix class defaults to @code{transient-switch} and can be set using
1388 1389the @code{:class} keyword.
1389Each keyword has to be a keyword symbol, either @code{:class} or a keyword
1390argument supported by the constructor of that class. The
1391@code{transient-switch} class is used if the class is not specified
1392explicitly.
1393 1390
1394The function definition is always: 1391The function definition is always:
1395 1392
@@ -2372,6 +2369,20 @@ the transient popup, you will be able to yank it in another buffer.
2372 #'transient--do-stay) 2369 #'transient--do-stay)
2373@end lisp 2370@end lisp
2374 2371
2372@anchor{How can I autoload prefix and suffix commands?}
2373@appendixsec How can I autoload prefix and suffix commands?
2374
2375If your package only supports Emacs 30, just prefix the definition
2376with @code{;;;###autoload}. If your package supports released versions of
2377Emacs, you unfortunately have to use a long form autoload comment
2378as described in @ref{Autoload,,,elisp,}.
2379
2380@lisp
2381;;;###autoload (autoload 'magit-dispatch "magit" nil t)
2382(transient-define-prefix magit-dispatch ()
2383 ...)
2384@end lisp
2385
2375@anchor{How does Transient compare to prefix keys and universal arguments?} 2386@anchor{How does Transient compare to prefix keys and universal arguments?}
2376@appendixsec How does Transient compare to prefix keys and universal arguments? 2387@appendixsec How does Transient compare to prefix keys and universal arguments?
2377 2388
diff --git a/lisp/transient.el b/lisp/transient.el
index c9b6e457d00..34458bec688 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -5,7 +5,7 @@
5;; Author: Jonas Bernoulli <jonas@bernoul.li> 5;; Author: Jonas Bernoulli <jonas@bernoul.li>
6;; URL: https://github.com/magit/transient 6;; URL: https://github.com/magit/transient
7;; Keywords: extensions 7;; Keywords: extensions
8;; Version: 0.6.0 8;; Version: 0.7.0
9 9
10;; SPDX-License-Identifier: GPL-3.0-or-later 10;; SPDX-License-Identifier: GPL-3.0-or-later
11 11
@@ -38,7 +38,7 @@
38(require 'format-spec) 38(require 'format-spec)
39 39
40(eval-and-compile 40(eval-and-compile
41 (when (and (featurep' seq) 41 (when (and (featurep 'seq)
42 (not (fboundp 'seq-keep))) 42 (not (fboundp 'seq-keep)))
43 (unload-feature 'seq 'force))) 43 (unload-feature 'seq 'force)))
44(require 'seq) 44(require 'seq)
@@ -721,24 +721,12 @@ the prototype is stored in the clone's `prototype' slot.")
721 (if-not-derived 721 (if-not-derived
722 :initarg :if-not-derived 722 :initarg :if-not-derived
723 :initform nil 723 :initform nil
724 :documentation "Enable if major-mode does not derive from value.")) 724 :documentation "Enable if major-mode does not derive from value.")
725 "Abstract superclass for group and suffix classes. 725 (inapt
726 726 :initform nil)
727It is undefined what happens if more than one `if*' predicate 727 (inapt-face
728slot is non-nil." 728 :initarg :inapt-face
729 :abstract t) 729 :initform 'transient-inapt-suffix)
730
731(defclass transient-suffix (transient-child)
732 ((definition :allocation :class :initform nil)
733 (key :initarg :key)
734 (command :initarg :command)
735 (transient :initarg :transient)
736 (format :initarg :format :initform " %k %d")
737 (description :initarg :description :initform nil)
738 (face :initarg :face :initform nil)
739 (show-help :initarg :show-help :initform nil)
740 (inapt-face :initarg :inapt-face :initform 'transient-inapt-suffix)
741 (inapt :initform nil)
742 (inapt-if 730 (inapt-if
743 :initarg :inapt-if 731 :initarg :inapt-if
744 :initform nil 732 :initform nil
@@ -771,13 +759,33 @@ slot is non-nil."
771 :initarg :inapt-if-not-derived 759 :initarg :inapt-if-not-derived
772 :initform nil 760 :initform nil
773 :documentation "Inapt if major-mode does not derive from value.")) 761 :documentation "Inapt if major-mode does not derive from value."))
762 "Abstract superclass for group and suffix classes.
763
764It is undefined what happens if more than one `if*' predicate
765slot is non-nil."
766 :abstract t)
767
768(defclass transient-suffix (transient-child)
769 ((definition :allocation :class :initform nil)
770 (key :initarg :key)
771 (command :initarg :command)
772 (transient :initarg :transient)
773 (format :initarg :format :initform " %k %d")
774 (description :initarg :description :initform nil)
775 (face :initarg :face :initform nil)
776 (show-help :initarg :show-help :initform nil))
774 "Superclass for suffix command.") 777 "Superclass for suffix command.")
775 778
776(defclass transient-information (transient-suffix) 779(defclass transient-information (transient-suffix)
777 ((format :initform " %k %d") 780 ((format :initform " %k %d")
778 (key :initform " ")) 781 (key :initform " "))
779 "Display-only information. 782 "Display-only information, aligned with suffix keys.
780A suffix object with no associated command.") 783Technically a suffix object with no associated command.")
784
785(defclass transient-information* (transient-information)
786 ((format :initform " %d"))
787 "Display-only information, aligned with suffix descriptions.
788Technically a suffix object with no associated command.")
781 789
782(defclass transient-infix (transient-suffix) 790(defclass transient-infix (transient-suffix)
783 ((transient :initform t) 791 ((transient :initform t)
@@ -834,6 +842,7 @@ They become the value of this argument.")
834 (hide :initarg :hide :initform nil) 842 (hide :initarg :hide :initform nil)
835 (description :initarg :description :initform nil) 843 (description :initarg :description :initform nil)
836 (pad-keys :initarg :pad-keys :initform nil) 844 (pad-keys :initarg :pad-keys :initform nil)
845 (info-format :initarg :info-format :initform nil)
837 (setup-children :initarg :setup-children)) 846 (setup-children :initarg :setup-children))
838 "Abstract superclass of all group classes." 847 "Abstract superclass of all group classes."
839 :abstract t) 848 :abstract t)
@@ -907,8 +916,9 @@ to the setup function:
907 [&optional ("interactive" interactive) def-body])) 916 [&optional ("interactive" interactive) def-body]))
908 (indent defun) 917 (indent defun)
909 (doc-string 3)) 918 (doc-string 3))
910 (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body) 919 (pcase-let
911 (transient--expand-define-args args arglist))) 920 ((`(,class ,slots ,suffixes ,docstr ,body ,interactive-only)
921 (transient--expand-define-args args arglist 'transient-define-prefix)))
912 `(progn 922 `(progn
913 (defalias ',name 923 (defalias ',name
914 ,(if body 924 ,(if body
@@ -916,7 +926,7 @@ to the setup function:
916 `(lambda () 926 `(lambda ()
917 (interactive) 927 (interactive)
918 (transient-setup ',name)))) 928 (transient-setup ',name))))
919 (put ',name 'interactive-only t) 929 (put ',name 'interactive-only ,interactive-only)
920 (put ',name 'function-documentation ,docstr) 930 (put ',name 'function-documentation ,docstr)
921 (put ',name 'transient--prefix 931 (put ',name 'transient--prefix
922 (,(or class 'transient-prefix) :command ',name ,@slots)) 932 (,(or class 'transient-prefix) :command ',name ,@slots))
@@ -940,42 +950,50 @@ The BODY must begin with an `interactive' form that matches
940ARGLIST. The infix arguments are usually accessed by using 950ARGLIST. The infix arguments are usually accessed by using
941`transient-args' inside `interactive'. 951`transient-args' inside `interactive'.
942 952
943\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... BODY...)" 953\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... [BODY...])"
944 (declare (debug ( &define name lambda-list 954 (declare (debug ( &define name lambda-list
945 [&optional lambda-doc] 955 [&optional lambda-doc]
946 [&rest keywordp sexp] 956 [&rest keywordp sexp]
947 ("interactive" interactive) 957 [&optional ("interactive" interactive) def-body]))
948 def-body))
949 (indent defun) 958 (indent defun)
950 (doc-string 3)) 959 (doc-string 3))
951 (pcase-let ((`(,class ,slots ,_ ,docstr ,body) 960 (pcase-let
952 (transient--expand-define-args args arglist))) 961 ((`(,class ,slots ,_ ,docstr ,body ,interactive-only)
962 (transient--expand-define-args args arglist 'transient-define-suffix)))
953 `(progn 963 `(progn
954 (defalias ',name 964 (defalias ',name
955 ,(if (and (not body) class (oref-default class definition)) 965 ,(if (and (not body) class (oref-default class definition))
956 `(oref-default ',class definition) 966 `(oref-default ',class definition)
957 `(lambda ,arglist ,@body))) 967 `(lambda ,arglist ,@body)))
958 (put ',name 'interactive-only t) 968 (put ',name 'interactive-only ,interactive-only)
959 (put ',name 'function-documentation ,docstr) 969 (put ',name 'function-documentation ,docstr)
960 (put ',name 'transient--suffix 970 (put ',name 'transient--suffix
961 (,(or class 'transient-suffix) :command ',name ,@slots))))) 971 (,(or class 'transient-suffix) :command ',name ,@slots)))))
962 972
973(defmacro transient-augment-suffix (name &rest args)
974 "Augment existing command NAME with a new transient suffix object.
975Similar to `transient-define-suffix' but define a suffix object only.
976\n\(fn NAME [KEYWORD VALUE]...)"
977 (declare (debug (&define name [&rest keywordp sexp]))
978 (indent defun))
979 (pcase-let
980 ((`(,class ,slots)
981 (transient--expand-define-args args nil 'transient-augment-suffix t)))
982 `(put ',name 'transient--suffix
983 (,(or class 'transient-suffix) :command ',name ,@slots))))
984
963(defmacro transient-define-infix (name arglist &rest args) 985(defmacro transient-define-infix (name arglist &rest args)
964 "Define NAME as a transient infix command. 986 "Define NAME as a transient infix command.
965 987
966ARGLIST is always ignored and reserved for future use. 988ARGLIST is always ignored and reserved for future use.
967DOCSTRING is the documentation string and is optional. 989DOCSTRING is the documentation string and is optional.
968 990
969The key-value pairs are mandatory. All transient infix commands 991At least one key-value pair is required. All transient infix
970are equal to each other (but not eq), so it is meaningless to 992commands are equal to each other (but not eq). It is meaning-
971define an infix command without also setting at least `:class' 993less to define an infix command, without providing at least one
972and one other keyword (which it is depends on the used class, 994keyword argument (usually `:argument' or `:variable', depending
973usually `:argument' or `:variable'). 995on the class). The suffix class defaults to `transient-switch'
974 996and can be set using the `:class' keyword.
975Each key has to be a keyword symbol, either `:class' or a keyword
976argument supported by the constructor of that class. The
977`transient-switch' class is used if the class is not specified
978explicitly.
979 997
980The function definitions is always: 998The function definitions is always:
981 999
@@ -994,17 +1012,19 @@ that case you have to use `transient-define-suffix' to define
994the infix command and use t as the value of the `:transient' 1012the infix command and use t as the value of the `:transient'
995keyword. 1013keyword.
996 1014
997\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)" 1015\(fn NAME ARGLIST [DOCSTRING] KEYWORD VALUE [KEYWORD VALUE]...)"
998 (declare (debug ( &define name lambda-list 1016 (declare (debug ( &define name lambda-list
999 [&optional lambda-doc] 1017 [&optional lambda-doc]
1018 keywordp sexp
1000 [&rest keywordp sexp])) 1019 [&rest keywordp sexp]))
1001 (indent defun) 1020 (indent defun)
1002 (doc-string 3)) 1021 (doc-string 3))
1003 (pcase-let ((`(,class ,slots ,_ ,docstr ,_) 1022 (pcase-let
1004 (transient--expand-define-args args arglist))) 1023 ((`(,class ,slots ,_ ,docstr ,_ ,interactive-only)
1024 (transient--expand-define-args args arglist 'transient-define-infix t)))
1005 `(progn 1025 `(progn
1006 (defalias ',name #'transient--default-infix-command) 1026 (defalias ',name #'transient--default-infix-command)
1007 (put ',name 'interactive-only t) 1027 (put ',name 'interactive-only ,interactive-only)
1008 (put ',name 'completion-predicate #'transient--suffix-only) 1028 (put ',name 'completion-predicate #'transient--suffix-only)
1009 (put ',name 'function-documentation ,docstr) 1029 (put ',name 'function-documentation ,docstr)
1010 (put ',name 'transient--suffix 1030 (put ',name 'transient--suffix
@@ -1044,7 +1064,8 @@ falling back to that of the same aliased command."
1044(put 'transient--default-infix-command 'completion-predicate 1064(put 'transient--default-infix-command 'completion-predicate
1045 #'transient--suffix-only) 1065 #'transient--suffix-only)
1046 1066
1047(defun transient--find-function-advised-original (fn func) 1067(define-advice find-function-advised-original
1068 (:around (fn func) transient-default-infix)
1048 "Return nil instead of `transient--default-infix-command'. 1069 "Return nil instead of `transient--default-infix-command'.
1049When using `find-function' to jump to the definition of a transient 1070When using `find-function' to jump to the definition of a transient
1050infix command/argument, then we want to actually jump to that, not to 1071infix command/argument, then we want to actually jump to that, not to
@@ -1052,14 +1073,12 @@ the definition of `transient--default-infix-command', which all infix
1052commands are aliases for." 1073commands are aliases for."
1053 (let ((val (funcall fn func))) 1074 (let ((val (funcall fn func)))
1054 (and val (not (eq val 'transient--default-infix-command)) val))) 1075 (and val (not (eq val 'transient--default-infix-command)) val)))
1055(advice-add 'find-function-advised-original :around
1056 #'transient--find-function-advised-original)
1057 1076
1058(eval-and-compile 1077(eval-and-compile ;transient--expand-define-args
1059 (defun transient--expand-define-args (args &optional arglist) 1078 (defun transient--expand-define-args (args arglist form &optional nobody)
1060 (unless (listp arglist) 1079 (unless (listp arglist)
1061 (error "Mandatory ARGLIST is missing")) 1080 (error "Mandatory ARGLIST is missing"))
1062 (let (class keys suffixes docstr) 1081 (let (class keys suffixes docstr declare (interactive-only t))
1063 (when (stringp (car args)) 1082 (when (stringp (car args))
1064 (setq docstr (pop args))) 1083 (setq docstr (pop args)))
1065 (while (keywordp (car args)) 1084 (while (keywordp (car args))
@@ -1073,13 +1092,28 @@ commands are aliases for."
1073 (or (vectorp arg) 1092 (or (vectorp arg)
1074 (and arg (symbolp arg)))) 1093 (and arg (symbolp arg))))
1075 (push (pop args) suffixes)) 1094 (push (pop args) suffixes))
1095 (when (eq (car-safe (car args)) 'declare)
1096 (setq declare (car args))
1097 (setq args (cdr args))
1098 (when-let ((int (assq 'interactive-only declare)))
1099 (setq interactive-only (cadr int))
1100 (delq int declare))
1101 (unless (cdr declare)
1102 (setq declare nil)))
1103 (cond
1104 ((not args))
1105 (nobody
1106 (error "%s: No function body allowed" form))
1107 ((not (eq (car-safe (nth (if declare 1 0) args)) 'interactive))
1108 (error "%s: Interactive form missing" form)))
1076 (list (if (eq (car-safe class) 'quote) 1109 (list (if (eq (car-safe class) 'quote)
1077 (cadr class) 1110 (cadr class)
1078 class) 1111 class)
1079 (nreverse keys) 1112 (nreverse keys)
1080 (nreverse suffixes) 1113 (nreverse suffixes)
1081 docstr 1114 docstr
1082 args)))) 1115 (if declare (cons declare args) args)
1116 interactive-only))))
1083 1117
1084(defun transient--parse-child (prefix spec) 1118(defun transient--parse-child (prefix spec)
1085 (cl-typecase spec 1119 (cl-typecase spec
@@ -1150,9 +1184,9 @@ commands are aliases for."
1150 (commandp (cadr spec))) 1184 (commandp (cadr spec)))
1151 (setq args (plist-put args :description (macroexp-quote pop))))) 1185 (setq args (plist-put args :description (macroexp-quote pop)))))
1152 (cond 1186 (cond
1153 ((eq car :info)) 1187 ((memq car '(:info :info*)))
1154 ((keywordp car) 1188 ((keywordp car)
1155 (error "Need command or `:info', got `%s'" car)) 1189 (error "Need command, `:info' or `:info*', got `%s'" car))
1156 ((symbolp car) 1190 ((symbolp car)
1157 (setq args (plist-put args :command (macroexp-quote pop)))) 1191 (setq args (plist-put args :command (macroexp-quote pop))))
1158 ((and (commandp car) 1192 ((and (commandp car)
@@ -1212,6 +1246,9 @@ commands are aliases for."
1212 ((eq key :info) 1246 ((eq key :info)
1213 (setq class 'transient-information) 1247 (setq class 'transient-information)
1214 (setq args (plist-put args :description val))) 1248 (setq args (plist-put args :description val)))
1249 ((eq key :info*)
1250 (setq class 'transient-information*)
1251 (setq args (plist-put args :description val)))
1215 ((eq (car-safe val) '\,) 1252 ((eq (car-safe val) '\,)
1216 (setq args (plist-put args key (cadr val)))) 1253 (setq args (plist-put args key (cadr val))))
1217 ((or (symbolp val) 1254 ((or (symbolp val)
@@ -1479,6 +1516,10 @@ variable instead.")
1479(defvar transient-exit-hook nil 1516(defvar transient-exit-hook nil
1480 "Hook run after exiting a transient.") 1517 "Hook run after exiting a transient.")
1481 1518
1519(defvar transient-setup-buffer-hook nil
1520 "Hook run when setting up the transient buffer.
1521That buffer is current and empty when this hook runs.")
1522
1482(defvar transient--prefix nil) 1523(defvar transient--prefix nil)
1483(defvar transient--layout nil) 1524(defvar transient--layout nil)
1484(defvar transient--suffixes nil) 1525(defvar transient--suffixes nil)
@@ -1506,6 +1547,9 @@ variable instead.")
1506(defvar transient--buffer-name " *transient*" 1547(defvar transient--buffer-name " *transient*"
1507 "Name of the transient buffer.") 1548 "Name of the transient buffer.")
1508 1549
1550(defvar transient--buffer nil
1551 "The transient menu buffer.")
1552
1509(defvar transient--window nil 1553(defvar transient--window nil
1510 "The window used to display the transient popup buffer.") 1554 "The window used to display the transient popup buffer.")
1511 1555
@@ -1859,15 +1903,20 @@ of the corresponding object."
1859 (setq key (save-match-data 1903 (setq key (save-match-data
1860 (funcall transient-substitute-key-function obj))) 1904 (funcall transient-substitute-key-function obj)))
1861 (oset obj key key)) 1905 (oset obj key key))
1862 (let ((kbd (kbd key)) 1906 (let* ((kbd (kbd key))
1863 (cmd (oref obj command))) 1907 (cmd (oref obj command))
1864 (when-let ((conflict (and transient-detect-key-conflicts 1908 (alt (transient--lookup-key map kbd)))
1865 (transient--lookup-key map kbd)))) 1909 (cond ((not alt)
1866 (unless (eq cmd conflict) 1910 (define-key map kbd cmd))
1867 (error "Cannot bind %S to %s and also %s" 1911 ((eq alt cmd))
1868 (string-trim key) 1912 ((transient--inapt-suffix-p obj))
1869 cmd conflict))) 1913 ((and-let* ((obj (transient-suffix-object alt)))
1870 (define-key map kbd cmd)))) 1914 (transient--inapt-suffix-p obj))
1915 (define-key map kbd cmd))
1916 (transient-detect-key-conflicts
1917 (error "Cannot bind %S to %s and also %s"
1918 (string-trim key) cmd alt))
1919 ((define-key map kbd cmd))))))
1871 (when-let ((b (keymap-lookup map "-"))) (keymap-set map "<kp-subtract>" b)) 1920 (when-let ((b (keymap-lookup map "-"))) (keymap-set map "<kp-subtract>" b))
1872 (when-let ((b (keymap-lookup map "="))) (keymap-set map "<kp-equal>" b)) 1921 (when-let ((b (keymap-lookup map "="))) (keymap-set map "<kp-equal>" b))
1873 (when-let ((b (keymap-lookup map "+"))) (keymap-set map "<kp-add>" b)) 1922 (when-let ((b (keymap-lookup map "+"))) (keymap-set map "<kp-add>" b))
@@ -2039,7 +2088,7 @@ value. Otherwise return CHILDREN as is."
2039 2088
2040(defun transient--init-suffixes (name) 2089(defun transient--init-suffixes (name)
2041 (let ((levels (alist-get name transient-levels))) 2090 (let ((levels (alist-get name transient-levels)))
2042 (cl-mapcan (lambda (c) (transient--init-child levels c)) 2091 (cl-mapcan (lambda (c) (transient--init-child levels c nil))
2043 (append (get name 'transient--layout) 2092 (append (get name 'transient--layout)
2044 (and (not transient--editp) 2093 (and (not transient--editp)
2045 (get 'transient-common-commands 2094 (get 'transient-common-commands
@@ -2057,24 +2106,29 @@ value. Otherwise return CHILDREN as is."
2057 (list def))))) 2106 (list def)))))
2058 (cl-mapcan #'s layout))) 2107 (cl-mapcan #'s layout)))
2059 2108
2060(defun transient--init-child (levels spec) 2109(defun transient--init-child (levels spec parent)
2061 (cl-etypecase spec 2110 (cl-etypecase spec
2062 (vector (transient--init-group levels spec)) 2111 (vector (transient--init-group levels spec parent))
2063 (list (transient--init-suffix levels spec)) 2112 (list (transient--init-suffix levels spec parent))
2064 (string (list spec)))) 2113 (string (list spec))))
2065 2114
2066(defun transient--init-group (levels spec) 2115(defun transient--init-group (levels spec parent)
2067 (pcase-let ((`(,level ,class ,args ,children) (append spec nil))) 2116 (pcase-let ((`(,level ,class ,args ,children) (append spec nil)))
2068 (and-let* ((- (transient--use-level-p level)) 2117 (and-let* (((transient--use-level-p level))
2069 (obj (apply class :level level args)) 2118 (obj (apply class :level level args))
2070 (- (transient--use-suffix-p obj)) 2119 ((transient--use-suffix-p obj))
2071 (suffixes (cl-mapcan (lambda (c) (transient--init-child levels c)) 2120 ((prog1 t
2072 (transient-setup-children obj children)))) 2121 (when (or (and parent (oref parent inapt))
2122 (transient--inapt-suffix-p obj))
2123 (oset obj inapt t))))
2124 (suffixes (cl-mapcan
2125 (lambda (c) (transient--init-child levels c obj))
2126 (transient-setup-children obj children))))
2073 (progn ; work around debbugs#31840 2127 (progn ; work around debbugs#31840
2074 (oset obj suffixes suffixes) 2128 (oset obj suffixes suffixes)
2075 (list obj))))) 2129 (list obj)))))
2076 2130
2077(defun transient--init-suffix (levels spec) 2131(defun transient--init-suffix (levels spec parent)
2078 (pcase-let* ((`(,level ,class ,args) spec) 2132 (pcase-let* ((`(,level ,class ,args) spec)
2079 (cmd (plist-get args :command)) 2133 (cmd (plist-get args :command))
2080 (key (transient--kbd (plist-get args :key))) 2134 (key (transient--kbd (plist-get args :key)))
@@ -2107,7 +2161,8 @@ value. Otherwise return CHILDREN as is."
2107 (unless (cl-typep obj 'transient-information) 2161 (unless (cl-typep obj 'transient-information)
2108 (transient--init-suffix-key obj)) 2162 (transient--init-suffix-key obj))
2109 (when (transient--use-suffix-p obj) 2163 (when (transient--use-suffix-p obj)
2110 (if (transient--inapt-suffix-p obj) 2164 (if (or (and parent (oref parent inapt))
2165 (transient--inapt-suffix-p obj))
2111 (oset obj inapt t) 2166 (oset obj inapt t)
2112 (transient-init-scope obj) 2167 (transient-init-scope obj)
2113 (transient-init-value obj)) 2168 (transient-init-value obj))
@@ -2296,8 +2351,9 @@ value. Otherwise return CHILDREN as is."
2296 'other) 2351 'other)
2297 (with-demoted-errors "Error while exiting transient: %S" 2352 (with-demoted-errors "Error while exiting transient: %S"
2298 (delete-window transient--window))) 2353 (delete-window transient--window)))
2299 (when-let ((buffer (get-buffer transient--buffer-name))) 2354 (when (buffer-live-p transient--buffer)
2300 (kill-buffer buffer)) 2355 (kill-buffer transient--buffer))
2356 (setq transient--buffer nil)
2301 (when remain-in-minibuffer-window 2357 (when remain-in-minibuffer-window
2302 (select-window remain-in-minibuffer-window))))) 2358 (select-window remain-in-minibuffer-window)))))
2303 2359
@@ -2468,7 +2524,7 @@ value. Otherwise return CHILDREN as is."
2468 ;; We cannot use `current-prefix-arg' because it is set 2524 ;; We cannot use `current-prefix-arg' because it is set
2469 ;; too late (in `command-execute'), and if it were set 2525 ;; too late (in `command-execute'), and if it were set
2470 ;; earlier, then we likely still would not be able to 2526 ;; earlier, then we likely still would not be able to
2471 ;; rely on it and `prefix-command-preserve-state-hook' 2527 ;; rely on it, and `prefix-command-preserve-state-hook'
2472 ;; would have to be used to record that a universal 2528 ;; would have to be used to record that a universal
2473 ;; argument is in effect. 2529 ;; argument is in effect.
2474 (not prefix-arg))) 2530 (not prefix-arg)))
@@ -2546,8 +2602,7 @@ value. Otherwise return CHILDREN as is."
2546 mouse-set-region)) 2602 mouse-set-region))
2547 (equal (key-description (this-command-keys-vector)) 2603 (equal (key-description (this-command-keys-vector))
2548 "<mouse-movement>")) 2604 "<mouse-movement>"))
2549 (and (eq (current-buffer) 2605 (and (eq (current-buffer) transient--buffer))))
2550 (get-buffer transient--buffer-name)))))
2551 (transient--show)) 2606 (transient--show))
2552 (when (and (numberp transient-show-popup) 2607 (when (and (numberp transient-show-popup)
2553 (not (zerop transient-show-popup)) 2608 (not (zerop transient-show-popup))
@@ -2575,11 +2630,12 @@ value. Otherwise return CHILDREN as is."
2575 (if (symbolp arg) 2630 (if (symbolp arg)
2576 (message "-- %-22s (cmd: %s, event: %S, exit: %s%s)" 2631 (message "-- %-22s (cmd: %s, event: %S, exit: %s%s)"
2577 arg 2632 arg
2578 (if (fboundp 'help-fns-function-name) 2633 (cond ((and (symbolp this-command) this-command))
2579 (help-fns-function-name this-command) 2634 ((fboundp 'help-fns-function-name)
2580 (if (byte-code-function-p this-command) 2635 (help-fns-function-name this-command))
2581 "#[...]" 2636 ((byte-code-function-p this-command)
2582 this-command)) 2637 "#[...]")
2638 (this-command))
2583 (key-description (this-command-keys-vector)) 2639 (key-description (this-command-keys-vector))
2584 transient--exitp 2640 transient--exitp
2585 (cond ((keywordp (car args)) 2641 (cond ((keywordp (car args))
@@ -3357,7 +3413,7 @@ prompt."
3357 3413
3358(cl-defmethod transient-infix-set :after ((obj transient-argument) value) 3414(cl-defmethod transient-infix-set :after ((obj transient-argument) value)
3359 "Unset incompatible infix arguments." 3415 "Unset incompatible infix arguments."
3360 (when-let* ((--- value) 3416 (when-let* ((value)
3361 (val (transient-infix-value obj)) 3417 (val (transient-infix-value obj))
3362 (arg (if (slot-boundp obj 'argument) 3418 (arg (if (slot-boundp obj 'argument)
3363 (oref obj argument) 3419 (oref obj argument)
@@ -3371,15 +3427,15 @@ prompt."
3371 (and (not (equal val arg)) 3427 (and (not (equal val arg))
3372 (cl-mapcan (apply-partially filter val) spec))))) 3428 (cl-mapcan (apply-partially filter val) spec)))))
3373 (dolist (obj transient--suffixes) 3429 (dolist (obj transient--suffixes)
3374 (when-let* ((--- (cl-typep obj 'transient-argument)) 3430 (when-let* (((cl-typep obj 'transient-argument))
3375 (val (transient-infix-value obj)) 3431 (val (transient-infix-value obj))
3376 (arg (if (slot-boundp obj 'argument) 3432 (arg (if (slot-boundp obj 'argument)
3377 (oref obj argument) 3433 (oref obj argument)
3378 (oref obj argument-format))) 3434 (oref obj argument-format)))
3379 (--- (if (equal val arg) 3435 ((if (equal val arg)
3380 (member arg incomp) 3436 (member arg incomp)
3381 (or (member val incomp) 3437 (or (member val incomp)
3382 (member arg incomp))))) 3438 (member arg incomp)))))
3383 (transient-infix-set obj nil))))) 3439 (transient-infix-set obj nil)))))
3384 3440
3385(cl-defgeneric transient-set-value (obj) 3441(cl-defgeneric transient-set-value (obj)
@@ -3515,6 +3571,10 @@ the option does not appear in ARGS."
3515 (or (match-string 1 match) ""))) 3571 (or (match-string 1 match) "")))
3516 (and (member arg args) t))) 3572 (and (member arg args) t)))
3517 3573
3574(defun transient-scope ()
3575 "Return the value of the `scope' slot of the current prefix."
3576 (oref (transient-prefix-object) scope))
3577
3518;;; History 3578;;; History
3519 3579
3520(cl-defgeneric transient--history-key (obj) 3580(cl-defgeneric transient--history-key (obj)
@@ -3580,15 +3640,18 @@ have a history of their own.")
3580 (transient--timer-cancel) 3640 (transient--timer-cancel)
3581 (setq transient--showp t) 3641 (setq transient--showp t)
3582 (let ((transient--shadowed-buffer (current-buffer)) 3642 (let ((transient--shadowed-buffer (current-buffer))
3583 (buf (get-buffer-create transient--buffer-name))
3584 (focus nil)) 3643 (focus nil))
3585 (with-current-buffer buf 3644 (setq transient--buffer (get-buffer-create transient--buffer-name))
3645 (with-current-buffer transient--buffer
3586 (when transient-enable-popup-navigation 3646 (when transient-enable-popup-navigation
3587 (setq focus (or (button-get (point) 'command) 3647 (setq focus (or (button-get (point) 'command)
3588 (and (not (bobp)) 3648 (and (not (bobp))
3589 (button-get (1- (point)) 'command)) 3649 (button-get (1- (point)) 'command))
3590 (transient--heading-at-point)))) 3650 (transient--heading-at-point))))
3591 (erase-buffer) 3651 (erase-buffer)
3652 (run-hooks 'transient-setup-buffer-hook)
3653 (when transient-force-fixed-pitch
3654 (transient--force-fixed-pitch))
3592 (setq window-size-fixed t) 3655 (setq window-size-fixed t)
3593 (when (bound-and-true-p tab-line-format) 3656 (when (bound-and-true-p tab-line-format)
3594 (setq tab-line-format nil)) 3657 (setq tab-line-format nil))
@@ -3609,12 +3672,11 @@ have a history of their own.")
3609 (when (or transient--helpp transient--editp) 3672 (when (or transient--helpp transient--editp)
3610 (transient--insert-help)) 3673 (transient--insert-help))
3611 (when-let ((line (transient--separator-line))) 3674 (when-let ((line (transient--separator-line)))
3612 (insert line)) 3675 (insert line)))
3613 (when transient-force-fixed-pitch
3614 (transient--force-fixed-pitch)))
3615 (unless (window-live-p transient--window) 3676 (unless (window-live-p transient--window)
3616 (setq transient--window 3677 (setq transient--window
3617 (display-buffer buf transient-display-buffer-action))) 3678 (display-buffer transient--buffer
3679 transient-display-buffer-action)))
3618 (when (window-live-p transient--window) 3680 (when (window-live-p transient--window)
3619 (with-selected-window transient--window 3681 (with-selected-window transient--window
3620 (goto-char (point-min)) 3682 (goto-char (point-min))
@@ -3657,9 +3719,8 @@ have a history of their own.")
3657 (transient-with-shadowed-buffer 3719 (transient-with-shadowed-buffer
3658 (funcall hide)))) 3720 (funcall hide))))
3659 (list group)))) 3721 (list group))))
3660 transient--layout)) 3722 transient--layout)))
3661 group) 3723 (while-let ((group (pop groups)))
3662 (while (setq group (pop groups))
3663 (transient--insert-group group) 3724 (transient--insert-group group)
3664 (when groups 3725 (when groups
3665 (insert ?\n))))) 3726 (insert ?\n)))))
@@ -3702,9 +3763,9 @@ have a history of their own.")
3702 (transient-with-shadowed-buffer 3763 (transient-with-shadowed-buffer
3703 (let* ((transient--pending-group column) 3764 (let* ((transient--pending-group column)
3704 (rows (mapcar #'transient-format (oref column suffixes)))) 3765 (rows (mapcar #'transient-format (oref column suffixes))))
3705 (when-let ((desc (transient-format-description column))) 3766 (if-let ((desc (transient-format-description column)))
3706 (push desc rows)) 3767 (cons desc rows)
3707 (flatten-tree rows)))) 3768 rows))))
3708 (oref group suffixes))) 3769 (oref group suffixes)))
3709 (vp (or (oref transient--prefix variable-pitch) 3770 (vp (or (oref transient--prefix variable-pitch)
3710 transient-align-variable-pitch)) 3771 transient-align-variable-pitch))
@@ -3721,7 +3782,7 @@ have a history of their own.")
3721 col)))) 3782 col))))
3722 columns)) 3783 columns))
3723 (cc (transient--seq-reductions-from 3784 (cc (transient--seq-reductions-from
3724 (apply-partially #'+ (* 3 (if vp (transient--pixel-width " ") 1))) 3785 (apply-partially #'+ (* 2 (if vp (transient--pixel-width " ") 1)))
3725 cw 0))) 3786 cw 0)))
3726 (if transient-force-single-column 3787 (if transient-force-single-column
3727 (dotimes (c cs) 3788 (dotimes (c cs)
@@ -3750,14 +3811,12 @@ have a history of their own.")
3750 (insert ?\n)))))))) 3811 (insert ?\n))))))))
3751 3812
3752(cl-defmethod transient--insert-group ((group transient-subgroups)) 3813(cl-defmethod transient--insert-group ((group transient-subgroups))
3753 (let* ((subgroups (oref group suffixes)) 3814 (let ((subgroups (oref group suffixes)))
3754 (n (length subgroups))) 3815 (while-let ((subgroup (pop subgroups)))
3755 (dotimes (s n) 3816 (transient--maybe-pad-keys subgroup group)
3756 (let ((subgroup (nth s subgroups))) 3817 (transient--insert-group subgroup)
3757 (transient--maybe-pad-keys subgroup group) 3818 (when subgroups
3758 (transient--insert-group subgroup) 3819 (insert ?\n)))))
3759 (when (< s (1- n))
3760 (insert ?\n))))))
3761 3820
3762(cl-defgeneric transient-format (obj) 3821(cl-defgeneric transient-format (obj)
3763 "Format and return OBJ for display. 3822 "Format and return OBJ for display.
@@ -3889,28 +3948,22 @@ as a button."
3889(cl-defgeneric transient-format-description (obj) 3948(cl-defgeneric transient-format-description (obj)
3890 "Format OBJ's `description' for display and return the result.") 3949 "Format OBJ's `description' for display and return the result.")
3891 3950
3892(cl-defmethod transient-format-description ((obj transient-child)) 3951(cl-defmethod transient-format-description ((obj transient-suffix))
3893 "The `description' slot may be a function, in which case that is 3952 "The `description' slot may be a function, in which case that is
3894called inside the correct buffer (see `transient--insert-group') 3953called inside the correct buffer (see `transient--insert-group')
3895and its value is returned to the caller." 3954and its value is returned to the caller."
3896 (and-let* ((desc (oref obj description)) 3955 (transient--get-description obj))
3897 (desc (if (functionp desc)
3898 (if (= (car (func-arity desc)) 1)
3899 (funcall desc obj)
3900 (funcall desc))
3901 desc)))
3902 (if-let* ((face (transient--get-face obj 'face)))
3903 (transient--add-face desc face t)
3904 desc)))
3905 3956
3906(cl-defmethod transient-format-description ((obj transient-group)) 3957(cl-defmethod transient-format-description ((obj transient-group))
3907 "Format the description by calling the next method. If the result 3958 "Format the description by calling the next method. If the result
3908doesn't use the `face' property at all, then apply the face 3959doesn't use the `face' property at all, then apply the face
3909`transient-heading' to the complete string." 3960`transient-heading' to the complete string."
3910 (and-let* ((desc (cl-call-next-method obj))) 3961 (and-let* ((desc (transient--get-description obj)))
3911 (if (text-property-not-all 0 (length desc) 'face nil desc) 3962 (cond ((oref obj inapt)
3912 desc 3963 (propertize desc 'face 'transient-inapt-suffix))
3913 (propertize desc 'face 'transient-heading)))) 3964 ((text-property-not-all 0 (length desc) 'face nil desc)
3965 desc)
3966 ((propertize desc 'face 'transient-heading)))))
3914 3967
3915(cl-defmethod transient-format-description :around ((obj transient-suffix)) 3968(cl-defmethod transient-format-description :around ((obj transient-suffix))
3916 "Format the description by calling the next method. If the result 3969 "Format the description by calling the next method. If the result
@@ -3920,8 +3973,11 @@ If the OBJ's `key' is currently unreachable, then apply the face
3920 (let ((desc (or (cl-call-next-method obj) 3973 (let ((desc (or (cl-call-next-method obj)
3921 (and (slot-boundp transient--prefix 'suffix-description) 3974 (and (slot-boundp transient--prefix 'suffix-description)
3922 (funcall (oref transient--prefix suffix-description) 3975 (funcall (oref transient--prefix suffix-description)
3923 obj)) 3976 obj)))))
3924 (propertize "(BUG: no description)" 'face 'error)))) 3977 (if desc
3978 (when-let ((face (transient--get-face obj 'face)))
3979 (setq desc (transient--add-face desc face t)))
3980 (setq desc (propertize "(BUG: no description)" 'face 'error)))
3925 (when (if transient--all-levels-p 3981 (when (if transient--all-levels-p
3926 (> (oref obj level) transient--default-prefix-level) 3982 (> (oref obj level) transient--default-prefix-level)
3927 (and transient-highlight-higher-levels 3983 (and transient-highlight-higher-levels
@@ -3983,23 +4039,30 @@ If the OBJ's `key' is currently unreachable, then apply the face
3983 choices 4039 choices
3984 (propertize "|" 'face 'transient-delimiter)))))) 4040 (propertize "|" 'face 'transient-delimiter))))))
3985 4041
3986(defun transient--add-face (string face &optional append beg end) 4042(cl-defmethod transient--get-description ((obj transient-child))
3987 (let ((str (copy-sequence string))) 4043 (and-let* ((desc (oref obj description)))
3988 (add-face-text-property (or beg 0) (or end (length str)) face append str) 4044 (if (functionp desc)
3989 str)) 4045 (if (= (car (transient--func-arity desc)) 1)
4046 (funcall desc obj)
4047 (funcall desc))
4048 desc)))
3990 4049
3991(defun transient--get-face (obj slot) 4050(cl-defmethod transient--get-face ((obj transient-suffix) slot)
3992 (and-let* ((! (slot-exists-p obj slot)) 4051 (and-let* (((slot-boundp obj slot))
3993 (! (slot-boundp obj slot))
3994 (face (slot-value obj slot))) 4052 (face (slot-value obj slot)))
3995 (if (and (not (facep face)) 4053 (if (and (not (facep face))
3996 (functionp face)) 4054 (functionp face))
3997 (let ((transient--pending-suffix obj)) 4055 (let ((transient--pending-suffix obj))
3998 (if (= (car (func-arity face)) 1) 4056 (if (= (car (transient--func-arity face)) 1)
3999 (funcall face obj) 4057 (funcall face obj)
4000 (funcall face))) 4058 (funcall face)))
4001 face))) 4059 face)))
4002 4060
4061(defun transient--add-face (string face &optional append beg end)
4062 (let ((str (copy-sequence string)))
4063 (add-face-text-property (or beg 0) (or end (length str)) face append str)
4064 str))
4065
4003(defun transient--key-face (&optional cmd enforce-type) 4066(defun transient--key-face (&optional cmd enforce-type)
4004 (or (and transient-semantic-coloring 4067 (or (and transient-semantic-coloring
4005 (not transient--helpp) 4068 (not transient--helpp)
@@ -4025,12 +4088,13 @@ If the OBJ's `key' is currently unreachable, then apply the face
4025 (when-let ((pad (or (oref group pad-keys) 4088 (when-let ((pad (or (oref group pad-keys)
4026 (and parent (oref parent pad-keys))))) 4089 (and parent (oref parent pad-keys)))))
4027 (oset group pad-keys 4090 (oset group pad-keys
4028 (apply #'max (cons (if (integerp pad) pad 0) 4091 (apply #'max
4029 (seq-keep (lambda (suffix) 4092 (if (integerp pad) pad 0)
4030 (and (eieio-object-p suffix) 4093 (seq-keep (lambda (suffix)
4031 (slot-boundp suffix 'key) 4094 (and (eieio-object-p suffix)
4032 (length (oref suffix key)))) 4095 (slot-boundp suffix 'key)
4033 (oref group suffixes))))))) 4096 (length (oref suffix key))))
4097 (oref group suffixes))))))
4034 4098
4035(defun transient--pixel-width (string) 4099(defun transient--pixel-width (string)
4036 (save-window-excursion 4100 (save-window-excursion
@@ -4386,7 +4450,8 @@ we stop there."
4386 (face-remap-reset-base 'default) 4450 (face-remap-reset-base 'default)
4387 (face-remap-add-relative 'default 'fixed-pitch)) 4451 (face-remap-add-relative 'default 'fixed-pitch))
4388 4452
4389;;;; Missing from Emacs 4453(defun transient--func-arity (fn)
4454 (func-arity (advice--cd*r (if (symbolp fn) (symbol-function fn) fn))))
4390 4455
4391(defun transient--seq-reductions-from (function sequence initial-value) 4456(defun transient--seq-reductions-from (function sequence initial-value)
4392 (let ((acc (list initial-value))) 4457 (let ((acc (list initial-value)))
@@ -4394,18 +4459,6 @@ we stop there."
4394 (push (funcall function (car acc) elt) acc)) 4459 (push (funcall function (car acc) elt) acc))
4395 (nreverse acc))) 4460 (nreverse acc)))
4396 4461
4397(defun transient-plist-to-alist (plist)
4398 (let (alist)
4399 (while plist
4400 (push (cons (let* ((symbol (pop plist))
4401 (name (symbol-name symbol)))
4402 (if (eq (aref name 0) ?:)
4403 (intern (substring name 1))
4404 symbol))
4405 (pop plist))
4406 alist))
4407 (nreverse alist)))
4408
4409;;; Font-Lock 4462;;; Font-Lock
4410 4463
4411(defconst transient-font-lock-keywords 4464(defconst transient-font-lock-keywords