Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3615 lines
138 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. ;;; transient.el --- Transient commands -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2018-2021 Free Software Foundation, Inc.
  3. ;; Author: Jonas Bernoulli <jonas@bernoul.li>
  4. ;; Homepage: https://github.com/magit/transient
  5. ;; Package-Requires: ((emacs "25.1"))
  6. ;; Package-Version: 0.3.2
  7. ;; Keywords: bindings
  8. ;; This file is part of GNU Emacs.
  9. ;; GNU Emacs is free software: you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published
  11. ;; by the Free Software Foundation, either version 3 of the License,
  12. ;; or (at your option) any later version.
  13. ;; GNU Emacs is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;; Taking inspiration from prefix keys and prefix arguments, Transient
  21. ;; implements a similar abstraction involving a prefix command, infix
  22. ;; arguments and suffix commands. We could call this abstraction a
  23. ;; "transient command", but because it always involves at least two
  24. ;; commands (a prefix and a suffix) we prefer to call it just a
  25. ;; "transient".
  26. ;; When the user calls a transient prefix command, then a transient
  27. ;; (temporary) keymap is activated, which binds the transient's infix
  28. ;; and suffix commands, and functions that control the transient state
  29. ;; are added to `pre-command-hook' and `post-command-hook'. The
  30. ;; available suffix and infix commands and their state are shown in
  31. ;; the echo area until the transient is exited by invoking a suffix
  32. ;; command.
  33. ;; Calling an infix command causes its value to be changed, possibly
  34. ;; by reading a new value in the minibuffer.
  35. ;; Calling a suffix command usually causes the transient to be exited
  36. ;; but suffix commands can also be configured to not exit the
  37. ;; transient state.
  38. ;;; Code:
  39. (require 'cl-lib)
  40. (require 'eieio)
  41. (require 'format-spec)
  42. (require 'seq)
  43. (eval-when-compile
  44. (require 'subr-x))
  45. (declare-function info 'info)
  46. (declare-function Man-find-section 'man)
  47. (declare-function Man-next-section 'man)
  48. (declare-function Man-getpage-in-background 'man)
  49. (defvar Man-notify-method)
  50. (define-obsolete-function-alias 'define-transient-command
  51. 'transient-define-prefix "Transient 0.3.0")
  52. (define-obsolete-function-alias 'define-suffix-command
  53. 'transient-define-suffix "Transient 0.3.0")
  54. (define-obsolete-function-alias 'define-infix-command
  55. 'transient-define-infix "Transient 0.3.0")
  56. (define-obsolete-function-alias 'define-infix-argument
  57. 'transient-define-argument "Transient 0.3.0")
  58. (define-obsolete-variable-alias 'current-transient-prefix
  59. 'transient-current-prefix "Transient 0.3.0")
  60. (define-obsolete-variable-alias 'current-transient-command
  61. 'transient-current-command "Transient 0.3.0")
  62. (define-obsolete-variable-alias 'current-transient-suffixes
  63. 'transient-current-suffixes "Transient 0.3.0")
  64. (define-obsolete-variable-alias 'post-transient-hook
  65. 'transient-exit-hook "Transient 0.3.0")
  66. (defmacro transient--with-emergency-exit (&rest body)
  67. (declare (indent defun))
  68. `(condition-case err
  69. (let ((debugger #'transient--exit-and-debug))
  70. ,(macroexp-progn body))
  71. ((debug error)
  72. (transient--emergency-exit)
  73. (signal (car err) (cdr err)))))
  74. (defun transient--exit-and-debug (&rest args)
  75. (transient--emergency-exit)
  76. (apply #'debug args))
  77. ;;; Options
  78. (defgroup transient nil
  79. "Transient commands."
  80. :group 'extensions)
  81. (defcustom transient-show-popup t
  82. "Whether to show the current transient in a popup buffer.
  83. - If t, then show the popup as soon as a transient prefix command
  84. is invoked.
  85. - If nil, then do not show the popup unless the user explicitly
  86. requests it, by pressing an incomplete prefix key sequence.
  87. - If a number, then delay displaying the popup and instead show
  88. a brief one-line summary. If zero or negative, then suppress
  89. even showing that summary and display the pressed key only.
  90. Show the popup when the user explicitly requests it by pressing
  91. an incomplete prefix key sequence. Unless zero, then also show
  92. the popup after that many seconds of inactivity (using the
  93. absolute value)."
  94. :package-version '(transient . "0.1.0")
  95. :group 'transient
  96. :type '(choice (const :tag "instantly" t)
  97. (const :tag "on demand" nil)
  98. (const :tag "on demand (no summary)" 0)
  99. (number :tag "after delay" 1)))
  100. (defcustom transient-enable-popup-navigation nil
  101. "Whether navigation commands are enabled in the transient popup.
  102. While a transient is active the transient popup buffer is not the
  103. current buffer, making it necessary to use dedicated commands to
  104. act on that buffer itself. If this non-nil, then the following
  105. features are available:
  106. - \"<up>\" moves the cursor to the previous suffix.
  107. \"<down>\" moves the cursor to the next suffix.
  108. \"RET\" invokes the suffix the cursor is on.
  109. - \"<mouse-1>\" invokes the clicked on suffix.
  110. - \"C-s\" and \"C-r\" start isearch in the popup buffer."
  111. :package-version '(transient . "0.2.0")
  112. :group 'transient
  113. :type 'boolean)
  114. (defcustom transient-display-buffer-action
  115. '(display-buffer-in-side-window
  116. (side . bottom)
  117. (inhibit-same-window . t))
  118. "The action used to display the transient popup buffer.
  119. The transient popup buffer is displayed in a window using
  120. \(display-buffer buf transient-display-buffer-action)
  121. The value of this option has the form (FUNCTION . ALIST),
  122. where FUNCTION is a function or a list of functions. Each such
  123. function should accept two arguments: a buffer to display and
  124. an alist of the same form as ALIST. See `display-buffer' for
  125. details.
  126. The default is (display-buffer-in-side-window (side . bottom)).
  127. This displays the window at the bottom of the selected frame.
  128. Another useful value is (display-buffer-below-selected). This
  129. is what `magit-popup' used by default. For more alternatives
  130. see info node `(elisp)Display Action Functions'.
  131. It may be possible to display the window in another frame, but
  132. whether that works in practice depends on the window-manager.
  133. If the window manager selects the new window (Emacs frame),
  134. then it doesn't work.
  135. If you change the value of this option, then you might also
  136. want to change the value of `transient-mode-line-format'."
  137. :package-version '(transient . "0.3.0")
  138. :group 'transient
  139. :type '(cons (choice function (repeat :tag "Functions" function))
  140. alist))
  141. (defcustom transient-mode-line-format 'line
  142. "The mode-line format for the transient popup buffer.
  143. If nil, then the buffer has no mode-line. If the buffer is not
  144. displayed right above the echo area, then this probably is not
  145. a good value.
  146. If `line' (the default), then the buffer also has no mode-line,
  147. but a thin line is drawn instead, using the background color of
  148. the face `transient-separator'. Termcap frames cannot display
  149. thin lines and therefore fallback to treating `line' like nil.
  150. Otherwise this can be any mode-line format.
  151. See `mode-line-format' for details."
  152. :package-version '(transient . "0.2.0")
  153. :group 'transient
  154. :type '(choice (const :tag "hide mode-line" nil)
  155. (const :tag "substitute thin line" line)
  156. (const :tag "name of prefix command"
  157. ("%e" mode-line-front-space
  158. mode-line-buffer-identification))
  159. (sexp :tag "custom mode-line format")))
  160. (defcustom transient-show-common-commands nil
  161. "Whether to show common transient suffixes in the popup buffer.
  162. These commands are always shown after typing the prefix key
  163. \"C-x\" when a transient command is active. To toggle the value
  164. of this variable use \"C-x t\" when a transient is active."
  165. :package-version '(transient . "0.1.0")
  166. :group 'transient
  167. :type 'boolean)
  168. (defcustom transient-read-with-initial-input nil
  169. "Whether to use the last history element as initial minibuffer input."
  170. :package-version '(transient . "0.2.0")
  171. :group 'transient
  172. :type 'boolean)
  173. (defcustom transient-highlight-mismatched-keys nil
  174. "Whether to highlight keys that do not match their argument.
  175. This only affects infix arguments that represent command-line
  176. arguments. When this option is non-nil, then the key binding
  177. for infix argument are highlighted when only a long argument
  178. \(e.g. \"--verbose\") is specified but no shor-thand (e.g \"-v\").
  179. In the rare case that a short-hand is specified but does not
  180. match the key binding, then it is highlighed differently.
  181. The highlighting is done using using `transient-mismatched-key'
  182. and `transient-nonstandard-key'."
  183. :package-version '(transient . "0.1.0")
  184. :group 'transient
  185. :type 'boolean)
  186. (defcustom transient-substitute-key-function nil
  187. "Function used to modify key bindings.
  188. This function is called with one argument, the prefix object,
  189. and must return a key binding description, either the existing
  190. key description it finds in the `key' slot, or a substitution.
  191. This is intended to let users replace certain prefix keys. It
  192. could also be used to make other substitutions, but that is
  193. discouraged.
  194. For example, \"=\" is hard to reach using my custom keyboard
  195. layout, so I substitute \"(\" for that, which is easy to reach
  196. using a layout optimized for lisp.
  197. (setq transient-substitute-key-function
  198. (lambda (obj)
  199. (let ((key (oref obj key)))
  200. (if (string-match \"\\\\`\\\\(=\\\\)[a-zA-Z]\" key)
  201. (replace-match \"(\" t t key 1)
  202. key)))))"
  203. :package-version '(transient . "0.1.0")
  204. :group 'transient
  205. :type '(choice (const :tag "Transform no keys (nil)" nil) function))
  206. (defcustom transient-semantic-coloring nil
  207. "Whether to color prefixes and suffixes in Hydra-like fashion.
  208. This feature is experimental.
  209. If non-nil, then the key binding of each suffix is colorized to
  210. indicate whether it exits the transient state or not. The color
  211. of the prefix is indicated using the line that is drawn when the
  212. value of `transient-mode-line-format' is `line'.
  213. For more information about how Hydra uses colors see
  214. https://github.com/abo-abo/hydra#color and
  215. https://oremacs.com/2015/02/19/hydra-colors-reloaded."
  216. :package-version '(transient . "0.3.0")
  217. :group 'transient
  218. :type 'boolean)
  219. (defcustom transient-detect-key-conflicts nil
  220. "Whether to detect key binding conflicts.
  221. Conflicts are detected when a transient prefix command is invoked
  222. and results in an error, which prevents the transient from being
  223. used."
  224. :package-version '(transient . "0.1.0")
  225. :group 'transient
  226. :type 'boolean)
  227. (defcustom transient-force-fixed-pitch nil
  228. "Whether to force use of monospaced font in the popup buffer.
  229. Even if you use a proportional font for the `default' face,
  230. you might still want to use a monospaced font in transient's
  231. popup buffer. Setting this option to t causes `default' to
  232. be remapped to `fixed-pitch' in that buffer."
  233. :package-version '(transient . "0.2.0")
  234. :group 'transient
  235. :type 'boolean)
  236. (defcustom transient-default-level 4
  237. "Control what suffix levels are made available by default.
  238. Each suffix command is placed on a level and each prefix command
  239. has a level, which controls which suffix commands are available.
  240. Integers between 1 and 7 (inclusive) are valid levels.
  241. The levels of individual transients and/or their individual
  242. suffixes can be changed individually, by invoking the prefix and
  243. then pressing \"C-x l\".
  244. The default level for both transients and their suffixes is 4.
  245. This option only controls the default for transients. The default
  246. suffix level is always 4. The author of a transient should place
  247. certain suffixes on a higher level if they expect that it won't be
  248. of use to most users, and they should place very important suffixes
  249. on a lower level so that they remain available even if the user
  250. lowers the transient level.
  251. \(Magit currently places nearly all suffixes on level 4 and lower
  252. levels are not used at all yet. So for the time being you should
  253. not set a lower level here and using a higher level might not
  254. give you as many additional suffixes as you hoped.)"
  255. :package-version '(transient . "0.1.0")
  256. :group 'transient
  257. :type '(choice (const :tag "1 - fewest suffixes" 1)
  258. (const 2)
  259. (const 3)
  260. (const :tag "4 - default" 4)
  261. (const 5)
  262. (const 6)
  263. (const :tag "7 - most suffixes" 7)))
  264. (defcustom transient-levels-file
  265. (locate-user-emacs-file (convert-standard-filename "transient/levels.el"))
  266. "File used to save levels of transients and their suffixes."
  267. :package-version '(transient . "0.1.0")
  268. :group 'transient
  269. :type 'file)
  270. (defcustom transient-values-file
  271. (locate-user-emacs-file (convert-standard-filename "transient/values.el"))
  272. "File used to save values of transients."
  273. :package-version '(transient . "0.1.0")
  274. :group 'transient
  275. :type 'file)
  276. (defcustom transient-history-file
  277. (locate-user-emacs-file (convert-standard-filename "transient/history.el"))
  278. "File used to save history of transients and their infixes."
  279. :package-version '(transient . "0.1.0")
  280. :group 'transient
  281. :type 'file)
  282. (defcustom transient-history-limit 10
  283. "Number of history elements to keep when saving to file."
  284. :package-version '(transient . "0.1.0")
  285. :group 'transient
  286. :type 'integer)
  287. (defcustom transient-save-history t
  288. "Whether to save history of transient commands when exiting Emacs."
  289. :package-version '(transient . "0.1.0")
  290. :group 'transient
  291. :type 'boolean)
  292. ;;; Faces
  293. (defgroup transient-faces nil
  294. "Faces used by Transient."
  295. :group 'transient)
  296. (defface transient-heading '((t :inherit font-lock-keyword-face))
  297. "Face used for headings."
  298. :group 'transient-faces)
  299. (defface transient-key '((t :inherit font-lock-builtin-face))
  300. "Face used for keys."
  301. :group 'transient-faces)
  302. (defface transient-argument '((t :inherit font-lock-warning-face))
  303. "Face used for enabled arguments."
  304. :group 'transient-faces)
  305. (defface transient-value '((t :inherit font-lock-string-face))
  306. "Face used for values."
  307. :group 'transient-faces)
  308. (defface transient-inactive-argument '((t :inherit shadow))
  309. "Face used for inactive arguments."
  310. :group 'transient-faces)
  311. (defface transient-inactive-value '((t :inherit shadow))
  312. "Face used for inactive values."
  313. :group 'transient-faces)
  314. (defface transient-unreachable '((t :inherit shadow))
  315. "Face used for suffixes unreachable from the current prefix sequence."
  316. :group 'transient-faces)
  317. (defface transient-active-infix '((t :inherit secondary-selection))
  318. "Face used for the infix for which the value is being read."
  319. :group 'transient-faces)
  320. (defface transient-unreachable-key '((t :inherit shadow))
  321. "Face used for keys unreachable from the current prefix sequence."
  322. :group 'transient-faces)
  323. (defface transient-nonstandard-key '((t :underline t))
  324. "Face optionally used to highlight keys conflicting with short-argument.
  325. Also see option `transient-highlight-mismatched-keys'."
  326. :group 'transient-faces)
  327. (defface transient-mismatched-key '((t :underline t))
  328. "Face optionally used to highlight keys without a short-argument.
  329. Also see option `transient-highlight-mismatched-keys'."
  330. :group 'transient-faces)
  331. (defface transient-inapt-suffix '((t :inherit shadow :italic t))
  332. "Face used for suffixes that are inapt at this time."
  333. :group 'transient-faces)
  334. (defface transient-enabled-suffix
  335. '((t :background "green" :foreground "black" :weight bold))
  336. "Face used for enabled levels while editing suffix levels.
  337. See info node `(transient)Enabling and Disabling Suffixes'."
  338. :group 'transient-faces)
  339. (defface transient-disabled-suffix
  340. '((t :background "red" :foreground "black" :weight bold))
  341. "Face used for disabled levels while editing suffix levels.
  342. See info node `(transient)Enabling and Disabling Suffixes'."
  343. :group 'transient-faces)
  344. (defface transient-separator
  345. `((((class color) (background light))
  346. ,@(and (>= emacs-major-version 27) '(:extend t))
  347. :background "grey80")
  348. (((class color) (background dark))
  349. ,@(and (>= emacs-major-version 27) '(:extend t))
  350. :background "grey30"))
  351. "Face used to draw line below transient popup window.
  352. This is only used if `transient-mode-line-format' is `line'.
  353. Only the background color is significant."
  354. :group 'transient-faces)
  355. (defgroup transient-color-faces
  356. '((transient-semantic-coloring custom-variable))
  357. "Faces used by Transient for Hydra-like command coloring.
  358. These faces are only used if `transient-semantic-coloring'
  359. \(which see) is non-nil."
  360. :group 'transient-faces)
  361. (defface transient-red
  362. '((t :inherit transient-key :foreground "red"))
  363. "Face used for red prefixes and suffixes."
  364. :group 'transient-color-faces)
  365. (defface transient-blue
  366. '((t :inherit transient-key :foreground "blue"))
  367. "Face used for blue prefixes and suffixes."
  368. :group 'transient-color-faces)
  369. (defface transient-amaranth
  370. '((t :inherit transient-key :foreground "#E52B50"))
  371. "Face used for amaranth prefixes."
  372. :group 'transient-color-faces)
  373. (defface transient-pink
  374. '((t :inherit transient-key :foreground "#FF6EB4"))
  375. "Face used for pink prefixes."
  376. :group 'transient-color-faces)
  377. (defface transient-teal
  378. '((t :inherit transient-key :foreground "#367588"))
  379. "Face used for teal prefixes."
  380. :group 'transient-color-faces)
  381. ;;; Persistence
  382. (defun transient--read-file-contents (file)
  383. (with-demoted-errors "Transient error: %S"
  384. (and (file-exists-p file)
  385. (with-temp-buffer
  386. (insert-file-contents file)
  387. (read (current-buffer))))))
  388. (defun transient--pp-to-file (list file)
  389. (make-directory (file-name-directory file) t)
  390. (setq list (cl-sort (copy-sequence list) #'string< :key #'car))
  391. (with-temp-file file
  392. (let ((print-level nil)
  393. (print-length nil))
  394. (pp list (current-buffer)))))
  395. (defvar transient-values
  396. (transient--read-file-contents transient-values-file)
  397. "Values of transient commands.
  398. The value of this variable persists between Emacs sessions
  399. and you usually should not change it manually.")
  400. (defun transient-save-values ()
  401. (transient--pp-to-file transient-values transient-values-file))
  402. (defvar transient-levels
  403. (transient--read-file-contents transient-levels-file)
  404. "Levels of transient commands.
  405. The value of this variable persists between Emacs sessions
  406. and you usually should not change it manually.")
  407. (defun transient-save-levels ()
  408. (transient--pp-to-file transient-levels transient-levels-file))
  409. (defvar transient-history
  410. (transient--read-file-contents transient-history-file)
  411. "History of transient commands and infix arguments.
  412. The value of this variable persists between Emacs sessions
  413. \(unless `transient-save-history' is nil) and you usually
  414. should not change it manually.")
  415. (defun transient-save-history ()
  416. (setq transient-history
  417. (cl-sort (mapcar (pcase-lambda (`(,key . ,val))
  418. (cons key (seq-take (delete-dups val)
  419. transient-history-limit)))
  420. transient-history)
  421. #'string< :key #'car))
  422. (transient--pp-to-file transient-history transient-history-file))
  423. (defun transient-maybe-save-history ()
  424. "Save the value of `transient-history'.
  425. If `transient-save-history' is nil, then do nothing."
  426. (when transient-save-history
  427. (transient-save-history)))
  428. (unless noninteractive
  429. (add-hook 'kill-emacs-hook 'transient-maybe-save-history))
  430. ;;; Classes
  431. ;;;; Prefix
  432. (defclass transient-prefix ()
  433. ((prototype :initarg :prototype)
  434. (command :initarg :command)
  435. (level :initarg :level)
  436. (variable :initarg :variable :initform nil)
  437. (init-value :initarg :init-value)
  438. (value) (default-value :initarg :value)
  439. (scope :initarg :scope :initform nil)
  440. (history :initarg :history :initform nil)
  441. (history-pos :initarg :history-pos :initform 0)
  442. (history-key :initarg :history-key :initform nil)
  443. (man-page :initarg :man-page :initform nil)
  444. (info-manual :initarg :info-manual :initform nil)
  445. (transient-suffix :initarg :transient-suffix :initform nil)
  446. (transient-non-suffix :initarg :transient-non-suffix :initform nil)
  447. (incompatible :initarg :incompatible :initform nil)
  448. (suffix-description :initarg :suffix-description))
  449. "Transient prefix command.
  450. Each transient prefix command consists of a command, which is
  451. stored in a symbol's function slot and an object, which is
  452. stored in the `transient--prefix' property of the same symbol.
  453. When a transient prefix command is invoked, then a clone of that
  454. object is stored in the global variable `transient--prefix' and
  455. the prototype is stored in the clone's `prototype' slot.")
  456. ;;;; Suffix
  457. (defclass transient-child ()
  458. ((level
  459. :initarg :level
  460. :initform 1
  461. :documentation "Enable if level of prefix is equal or greater.")
  462. (if
  463. :initarg :if
  464. :initform nil
  465. :documentation "Enable if predicate returns non-nil.")
  466. (if-not
  467. :initarg :if-not
  468. :initform nil
  469. :documentation "Enable if predicate returns nil.")
  470. (if-non-nil
  471. :initarg :if-non-nil
  472. :initform nil
  473. :documentation "Enable if variable's value is non-nil.")
  474. (if-nil
  475. :initarg :if-nil
  476. :initform nil
  477. :documentation "Enable if variable's value is nil.")
  478. (if-mode
  479. :initarg :if-mode
  480. :initform nil
  481. :documentation "Enable if major-mode matches value.")
  482. (if-not-mode
  483. :initarg :if-not-mode
  484. :initform nil
  485. :documentation "Enable if major-mode does not match value.")
  486. (if-derived
  487. :initarg :if-derived
  488. :initform nil
  489. :documentation "Enable if major-mode derives from value.")
  490. (if-not-derived
  491. :initarg :if-not-derived
  492. :initform nil
  493. :documentation "Enable if major-mode does not derive from value."))
  494. "Abstract superclass for group and and suffix classes.
  495. It is undefined what happens if more than one `if*' predicate
  496. slot is non-nil."
  497. :abstract t)
  498. (defclass transient-suffix (transient-child)
  499. ((key :initarg :key)
  500. (command :initarg :command)
  501. (transient :initarg :transient)
  502. (format :initarg :format :initform " %k %d")
  503. (description :initarg :description :initform nil)
  504. (inapt :initform nil)
  505. (inapt-if
  506. :initarg :inapt-if
  507. :initform nil
  508. :documentation "Inapt if predicate returns non-nil.")
  509. (inapt-if-not
  510. :initarg :inapt-if-not
  511. :initform nil
  512. :documentation "Inapt if predicate returns nil.")
  513. (inapt-if-non-nil
  514. :initarg :inapt-if-non-nil
  515. :initform nil
  516. :documentation "Inapt if variable's value is non-nil.")
  517. (inapt-if-nil
  518. :initarg :inapt-if-nil
  519. :initform nil
  520. :documentation "Inapt if variable's value is nil.")
  521. (inapt-if-mode
  522. :initarg :inapt-if-mode
  523. :initform nil
  524. :documentation "Inapt if major-mode matches value.")
  525. (inapt-if-not-mode
  526. :initarg :inapt-if-not-mode
  527. :initform nil
  528. :documentation "Inapt if major-mode does not match value.")
  529. (inapt-if-derived
  530. :initarg :inapt-if-derived
  531. :initform nil
  532. :documentation "Inapt if major-mode derives from value.")
  533. (inapt-if-not-derived
  534. :initarg :inapt-if-not-derived
  535. :initform nil
  536. :documentation "Inapt if major-mode does not derive from value."))
  537. "Superclass for suffix command.")
  538. (defclass transient-infix (transient-suffix)
  539. ((transient :initform t)
  540. (argument :initarg :argument)
  541. (shortarg :initarg :shortarg)
  542. (value :initform nil)
  543. (init-value :initarg :init-value)
  544. (unsavable :initarg :unsavable :initform nil)
  545. (multi-value :initarg :multi-value :initform nil)
  546. (always-read :initarg :always-read :initform nil)
  547. (allow-empty :initarg :allow-empty :initform nil)
  548. (history-key :initarg :history-key :initform nil)
  549. (reader :initarg :reader :initform nil)
  550. (prompt :initarg :prompt :initform nil)
  551. (choices :initarg :choices :initform nil)
  552. (format :initform " %k %d (%v)"))
  553. "Transient infix command."
  554. :abstract t)
  555. (defclass transient-argument (transient-infix) ()
  556. "Abstract superclass for infix arguments."
  557. :abstract t)
  558. (defclass transient-switch (transient-argument) ()
  559. "Class used for command-line argument that can be turned on and off.")
  560. (defclass transient-option (transient-argument) ()
  561. "Class used for command-line argument that can take a value.")
  562. (defclass transient-variable (transient-infix)
  563. ((variable :initarg :variable)
  564. (format :initform " %k %d %v"))
  565. "Abstract superclass for infix commands that set a variable."
  566. :abstract t)
  567. (defclass transient-switches (transient-argument)
  568. ((argument-format :initarg :argument-format)
  569. (argument-regexp :initarg :argument-regexp))
  570. "Class used for sets of mutually exclusive command-line switches.")
  571. (defclass transient-files (transient-infix) ()
  572. "Class used for the \"--\" argument.
  573. All remaining arguments are treated as files.
  574. They become the value of this this argument.")
  575. ;;;; Group
  576. (defclass transient-group (transient-child)
  577. ((suffixes :initarg :suffixes :initform nil)
  578. (hide :initarg :hide :initform nil)
  579. (description :initarg :description :initform nil)
  580. (setup-children :initarg :setup-children)
  581. (pad-keys :initarg :pad-keys))
  582. "Abstract superclass of all group classes."
  583. :abstract t)
  584. (defclass transient-column (transient-group) ()
  585. "Group class that displays each element on a separate line.")
  586. (defclass transient-row (transient-group) ()
  587. "Group class that displays all elements on a single line.")
  588. (defclass transient-columns (transient-group) ()
  589. "Group class that displays elements organized in columns.
  590. Direct elements have to be groups whose elements have to be
  591. commands or string. Each subgroup represents a column. This
  592. class takes care of inserting the subgroups' elements.")
  593. (defclass transient-subgroups (transient-group) ()
  594. "Group class that wraps other groups.
  595. Direct elements have to be groups whose elements have to be
  596. commands or strings. This group inserts an empty line between
  597. subgroups. The subgroups are responsible for displaying their
  598. elements themselves.")
  599. ;;; Define
  600. (defmacro transient-define-prefix (name arglist &rest args)
  601. "Define NAME as a transient prefix command.
  602. ARGLIST are the arguments that command takes.
  603. DOCSTRING is the documentation string and is optional.
  604. These arguments can optionally be followed by key-value pairs.
  605. Each key has to be a keyword symbol, either `:class' or a keyword
  606. argument supported by the constructor of that class. The
  607. `transient-prefix' class is used if the class is not specified
  608. explicitly.
  609. GROUPs add key bindings for infix and suffix commands and specify
  610. how these bindings are presented in the popup buffer. At least
  611. one GROUP has to be specified. See info node `(transient)Binding
  612. Suffix and Infix Commands'.
  613. The BODY is optional. If it is omitted, then ARGLIST is also
  614. ignored and the function definition becomes:
  615. (lambda ()
  616. (interactive)
  617. (transient-setup \\='NAME))
  618. If BODY is specified, then it must begin with an `interactive'
  619. form that matches ARGLIST, and it must call `transient-setup'.
  620. It may however call that function only when some condition is
  621. satisfied; that is one of the reason why you might want to use
  622. an explicit BODY.
  623. All transients have a (possibly nil) value, which is exported
  624. when suffix commands are called, so that they can consume that
  625. value. For some transients it might be necessary to have a sort
  626. of secondary value, called a scope. Such a scope would usually
  627. be set in the commands `interactive' form and has to be passed
  628. to the setup function:
  629. (transient-setup \\='NAME nil nil :scope SCOPE)
  630. \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... GROUP... [BODY...])"
  631. (declare (debug (&define name lambda-list
  632. [&optional lambda-doc]
  633. [&rest keywordp sexp]
  634. [&rest vectorp]
  635. [&optional ("interactive" interactive) def-body]))
  636. (indent defun)
  637. (doc-string 3))
  638. (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body)
  639. (transient--expand-define-args args)))
  640. `(progn
  641. (defalias ',name
  642. ,(if body
  643. `(lambda ,arglist ,@body)
  644. `(lambda ()
  645. (interactive)
  646. (transient-setup ',name))))
  647. (put ',name 'interactive-only t)
  648. (put ',name 'function-documentation ,docstr)
  649. (put ',name 'transient--prefix
  650. (,(or class 'transient-prefix) :command ',name ,@slots))
  651. (put ',name 'transient--layout
  652. ',(cl-mapcan (lambda (s) (transient--parse-child name s))
  653. suffixes)))))
  654. (defmacro transient-define-suffix (name arglist &rest args)
  655. "Define NAME as a transient suffix command.
  656. ARGLIST are the arguments that the command takes.
  657. DOCSTRING is the documentation string and is optional.
  658. These arguments can optionally be followed by key-value pairs.
  659. Each key has to be a keyword symbol, either `:class' or a
  660. keyword argument supported by the constructor of that class.
  661. The `transient-suffix' class is used if the class is not
  662. specified explicitly.
  663. The BODY must begin with an `interactive' form that matches
  664. ARGLIST. The infix arguments are usually accessed by using
  665. `transient-args' inside `interactive'.
  666. \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... BODY...)"
  667. (declare (debug (&define name lambda-list
  668. [&optional lambda-doc]
  669. [&rest keywordp sexp]
  670. ("interactive" interactive)
  671. def-body))
  672. (indent defun)
  673. (doc-string 3))
  674. (pcase-let ((`(,class ,slots ,_ ,docstr ,body)
  675. (transient--expand-define-args args)))
  676. `(progn
  677. (defalias ',name (lambda ,arglist ,@body))
  678. (put ',name 'interactive-only t)
  679. (put ',name 'function-documentation ,docstr)
  680. (put ',name 'transient--suffix
  681. (,(or class 'transient-suffix) :command ',name ,@slots)))))
  682. (defmacro transient-define-infix (name _arglist &rest args)
  683. "Define NAME as a transient infix command.
  684. ARGLIST is always ignored and reserved for future use.
  685. DOCSTRING is the documentation string and is optional.
  686. The key-value pairs are mandatory. All transient infix commands
  687. are equal to each other (but not eq), so it is meaningless to
  688. define an infix command without also setting at least `:class'
  689. and one other keyword (which it is depends on the used class,
  690. usually `:argument' or `:variable').
  691. Each key has to be a keyword symbol, either `:class' or a keyword
  692. argument supported by the constructor of that class. The
  693. `transient-switch' class is used if the class is not specified
  694. explicitly.
  695. The function definitions is always:
  696. (lambda ()
  697. (interactive)
  698. (let ((obj (transient-suffix-object)))
  699. (transient-infix-set obj (transient-infix-read obj)))
  700. (transient--show))
  701. `transient-infix-read' and `transient-infix-set' are generic
  702. functions. Different infix commands behave differently because
  703. the concrete methods are different for different infix command
  704. classes. In rare case the above command function might not be
  705. suitable, even if you define your own infix command class. In
  706. that case you have to use `transient-suffix-command' to define
  707. the infix command and use t as the value of the `:transient'
  708. keyword.
  709. \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)"
  710. (declare (debug (&define name lambda-list
  711. [&optional lambda-doc]
  712. [&rest keywordp sexp]))
  713. (indent defun)
  714. (doc-string 3))
  715. (pcase-let ((`(,class ,slots ,_ ,docstr ,_)
  716. (transient--expand-define-args args)))
  717. `(progn
  718. (defalias ',name ,(transient--default-infix-command))
  719. (put ',name 'interactive-only t)
  720. (put ',name 'function-documentation ,docstr)
  721. (put ',name 'transient--suffix
  722. (,(or class 'transient-switch) :command ',name ,@slots)))))
  723. (defalias 'transient-define-argument 'define-infix-command
  724. "Define NAME as a transient infix command.
  725. Only use this alias to define an infix command that actually
  726. sets an infix argument. To define a infix command that, for
  727. example, sets a variable use `transient-define-infix' instead.
  728. \(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)")
  729. (defun transient--expand-define-args (args)
  730. (let (class keys suffixes docstr)
  731. (when (stringp (car args))
  732. (setq docstr (pop args)))
  733. (while (keywordp (car args))
  734. (let ((k (pop args))
  735. (v (pop args)))
  736. (if (eq k :class)
  737. (setq class v)
  738. (push k keys)
  739. (push v keys))))
  740. (while (let ((arg (car args)))
  741. (or (vectorp arg)
  742. (and arg (symbolp arg))))
  743. (push (pop args) suffixes))
  744. (list (if (eq (car-safe class) 'quote)
  745. (cadr class)
  746. class)
  747. (nreverse keys)
  748. (nreverse suffixes)
  749. docstr
  750. args)))
  751. (defun transient--parse-child (prefix spec)
  752. (cl-etypecase spec
  753. (symbol (let ((value (symbol-value spec)))
  754. (if (and (listp value)
  755. (or (listp (car value))
  756. (vectorp (car value))))
  757. (cl-mapcan (lambda (s) (transient--parse-child prefix s)) value)
  758. (transient--parse-child prefix value))))
  759. (vector (when-let ((c (transient--parse-group prefix spec))) (list c)))
  760. (list (when-let ((c (transient--parse-suffix prefix spec))) (list c)))
  761. (string (list spec))))
  762. (defun transient--parse-group (prefix spec)
  763. (setq spec (append spec nil))
  764. (cl-symbol-macrolet
  765. ((car (car spec))
  766. (pop (pop spec)))
  767. (let (level class args)
  768. (when (integerp car)
  769. (setq level pop))
  770. (when (stringp car)
  771. (setq args (plist-put args :description pop)))
  772. (while (keywordp car)
  773. (let ((k pop))
  774. (if (eq k :class)
  775. (setq class pop)
  776. (setq args (plist-put args k pop)))))
  777. (vector (or level (oref-default 'transient-child level))
  778. (or class
  779. (if (vectorp car)
  780. 'transient-columns
  781. 'transient-column))
  782. args
  783. (cl-mapcan (lambda (s) (transient--parse-child prefix s)) spec)))))
  784. (defun transient--parse-suffix (prefix spec)
  785. (let (level class args)
  786. (cl-symbol-macrolet
  787. ((car (car spec))
  788. (pop (pop spec)))
  789. (when (integerp car)
  790. (setq level pop))
  791. (when (or (stringp car)
  792. (vectorp car))
  793. (setq args (plist-put args :key pop)))
  794. (when (or (stringp car)
  795. (eq (car-safe car) 'lambda)
  796. (and (symbolp car)
  797. (not (commandp car))
  798. (commandp (cadr spec))))
  799. (setq args (plist-put args :description pop)))
  800. (cond
  801. ((keywordp car)
  802. (error "Need command, got %S" car))
  803. ((symbolp car)
  804. (setq args (plist-put args :command pop)))
  805. ((and (commandp car)
  806. (not (stringp car)))
  807. (let ((cmd pop)
  808. (sym (intern (format "transient:%s:%s"
  809. prefix
  810. (or (plist-get args :description)
  811. (plist-get args :key))))))
  812. (defalias sym cmd)
  813. (setq args (plist-put args :command sym))))
  814. ((or (stringp car)
  815. (and car (listp car)))
  816. (let ((arg pop))
  817. (cl-typecase arg
  818. (list
  819. (setq args (plist-put args :shortarg (car arg)))
  820. (setq args (plist-put args :argument (cadr arg)))
  821. (setq arg (cadr arg)))
  822. (string
  823. (when-let ((shortarg (transient--derive-shortarg arg)))
  824. (setq args (plist-put args :shortarg shortarg)))
  825. (setq args (plist-put args :argument arg))))
  826. (setq args (plist-put args :command
  827. (intern (format "transient:%s:%s"
  828. prefix arg))))
  829. (cond ((and car (not (keywordp car)))
  830. (setq class 'transient-option)
  831. (setq args (plist-put args :reader pop)))
  832. ((not (string-suffix-p "=" arg))
  833. (setq class 'transient-switch))
  834. (t
  835. (setq class 'transient-option)))))
  836. (t
  837. (error "Needed command or argument, got %S" car)))
  838. (while (keywordp car)
  839. (let ((k pop))
  840. (cl-case k
  841. (:class (setq class pop))
  842. (:level (setq level pop))
  843. (t (setq args (plist-put args k pop)))))))
  844. (unless (plist-get args :key)
  845. (when-let ((shortarg (plist-get args :shortarg)))
  846. (setq args (plist-put args :key shortarg))))
  847. (list (or level (oref-default 'transient-child level))
  848. (or class 'transient-suffix)
  849. args)))
  850. (defun transient--default-infix-command ()
  851. (cons 'lambda
  852. '(()
  853. (interactive)
  854. (let ((obj (transient-suffix-object)))
  855. (transient-infix-set obj (transient-infix-read obj)))
  856. (transient--show))))
  857. (defun transient--ensure-infix-command (obj)
  858. (let ((cmd (oref obj command)))
  859. (unless (or (commandp cmd)
  860. (get cmd 'transient--infix-command))
  861. (if (or (cl-typep obj 'transient-switch)
  862. (cl-typep obj 'transient-option))
  863. (put cmd 'transient--infix-command
  864. (transient--default-infix-command))
  865. ;; This is not an anonymous infix argument.
  866. (error "Suffix %s is not defined or autoloaded as a command" cmd)))))
  867. (defun transient--derive-shortarg (arg)
  868. (save-match-data
  869. (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg)
  870. (match-string 1 arg))))
  871. ;;; Edit
  872. (defun transient--insert-suffix (prefix loc suffix action)
  873. (let* ((suf (cl-etypecase suffix
  874. (vector (transient--parse-group prefix suffix))
  875. (list (transient--parse-suffix prefix suffix))
  876. (string suffix)))
  877. (mem (transient--layout-member loc prefix))
  878. (elt (car mem)))
  879. (cond
  880. ((not mem)
  881. (message "Cannot insert %S into %s; %s not found"
  882. suffix prefix loc))
  883. ((or (and (vectorp suffix) (not (vectorp elt)))
  884. (and (listp suffix) (vectorp elt))
  885. (and (stringp suffix) (vectorp elt)))
  886. (message "Cannot place %S into %s at %s; %s"
  887. suffix prefix loc
  888. "suffixes and groups cannot be siblings"))
  889. (t
  890. (when (and (listp suffix)
  891. (listp elt))
  892. ;; Both suffixes are key bindings; not heading strings.
  893. (let ((key (transient--spec-key suf)))
  894. (if (equal (transient--kbd key)
  895. (transient--kbd (transient--spec-key elt)))
  896. ;; We must keep `mem' until after we have inserted
  897. ;; behind it, which `transient-remove-suffix' does
  898. ;; not allow us to do.
  899. (let ((spred (transient--suffix-predicate suf))
  900. (epred (transient--suffix-predicate elt)))
  901. ;; If both suffixes have a predicate and they
  902. ;; are not identical, then there is a high
  903. ;; probability that we want to keep both.
  904. (when (or (not spred)
  905. (not epred)
  906. (equal spred epred))
  907. (setq action 'replace)))
  908. (transient-remove-suffix prefix key))))
  909. (cl-ecase action
  910. (insert (setcdr mem (cons elt (cdr mem)))
  911. (setcar mem suf))
  912. (append (setcdr mem (cons suf (cdr mem))))
  913. (replace (setcar mem suf)))))))
  914. ;;;###autoload
  915. (defun transient-insert-suffix (prefix loc suffix)
  916. "Insert a SUFFIX into PREFIX before LOC.
  917. PREFIX is a prefix command, a symbol.
  918. SUFFIX is a suffix command or a group specification (of
  919. the same forms as expected by `transient-define-prefix').
  920. LOC is a command, a key vector, a key description (a string
  921. as returned by `key-description'), or a coordination list
  922. (whose last element may also be a command or key).
  923. See info node `(transient)Modifying Existing Transients'."
  924. (declare (indent defun))
  925. (transient--insert-suffix prefix loc suffix 'insert))
  926. ;;;###autoload
  927. (defun transient-append-suffix (prefix loc suffix)
  928. "Insert a SUFFIX into PREFIX after LOC.
  929. PREFIX is a prefix command, a symbol.
  930. SUFFIX is a suffix command or a group specification (of
  931. the same forms as expected by `transient-define-prefix').
  932. LOC is a command, a key vector, a key description (a string
  933. as returned by `key-description'), or a coordination list
  934. (whose last element may also be a command or key).
  935. See info node `(transient)Modifying Existing Transients'."
  936. (declare (indent defun))
  937. (transient--insert-suffix prefix loc suffix 'append))
  938. ;;;###autoload
  939. (defun transient-replace-suffix (prefix loc suffix)
  940. "Replace the suffix at LOC in PREFIX with SUFFIX.
  941. PREFIX is a prefix command, a symbol.
  942. SUFFIX is a suffix command or a group specification (of
  943. the same forms as expected by `transient-define-prefix').
  944. LOC is a command, a key vector, a key description (a string
  945. as returned by `key-description'), or a coordination list
  946. (whose last element may also be a command or key).
  947. See info node `(transient)Modifying Existing Transients'."
  948. (declare (indent defun))
  949. (transient--insert-suffix prefix loc suffix 'replace))
  950. ;;;###autoload
  951. (defun transient-remove-suffix (prefix loc)
  952. "Remove the suffix or group at LOC in PREFIX.
  953. PREFIX is a prefix command, a symbol.
  954. LOC is a command, a key vector, a key description (a string
  955. as returned by `key-description'), or a coordination list
  956. (whose last element may also be a command or key).
  957. See info node `(transient)Modifying Existing Transients'."
  958. (declare (indent defun))
  959. (transient--layout-member loc prefix 'remove))
  960. (defun transient-get-suffix (prefix loc)
  961. "Return the suffix or group at LOC in PREFIX.
  962. PREFIX is a prefix command, a symbol.
  963. LOC is a command, a key vector, a key description (a string
  964. as returned by `key-description'), or a coordination list
  965. (whose last element may also be a command or key).
  966. See info node `(transient)Modifying Existing Transients'."
  967. (if-let ((mem (transient--layout-member loc prefix)))
  968. (car mem)
  969. (error "%s not found in %s" loc prefix)))
  970. (defun transient-suffix-put (prefix loc prop value)
  971. "Edit the suffix at LOC in PREFIX, setting PROP to VALUE.
  972. PREFIX is a prefix command, a symbol.
  973. SUFFIX is a suffix command or a group specification (of
  974. the same forms as expected by `transient-define-prefix').
  975. LOC is a command, a key vector, a key description (a string
  976. as returned by `key-description'), or a coordination list
  977. (whose last element may also be a command or key).
  978. See info node `(transient)Modifying Existing Transients'."
  979. (let ((suf (transient-get-suffix prefix loc)))
  980. (setf (elt suf 2)
  981. (plist-put (elt suf 2) prop value))))
  982. (defun transient--layout-member (loc prefix &optional remove)
  983. (let ((val (or (get prefix 'transient--layout)
  984. (error "%s is not a transient command" prefix))))
  985. (when (listp loc)
  986. (while (integerp (car loc))
  987. (let* ((children (if (vectorp val) (aref val 3) val))
  988. (mem (transient--nthcdr (pop loc) children)))
  989. (if (and remove (not loc))
  990. (let ((rest (delq (car mem) children)))
  991. (if (vectorp val)
  992. (aset val 3 rest)
  993. (put prefix 'transient--layout rest))
  994. (setq val nil))
  995. (setq val (if loc (car mem) mem)))))
  996. (setq loc (car loc)))
  997. (if loc
  998. (transient--layout-member-1 (transient--kbd loc) val remove)
  999. val)))
  1000. (defun transient--layout-member-1 (loc layout remove)
  1001. (cond ((listp layout)
  1002. (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove))
  1003. layout))
  1004. ((vectorp (car (aref layout 3)))
  1005. (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove))
  1006. (aref layout 3)))
  1007. (remove
  1008. (aset layout 3
  1009. (delq (car (transient--group-member loc layout))
  1010. (aref layout 3)))
  1011. nil)
  1012. (t (transient--group-member loc layout))))
  1013. (defun transient--group-member (loc group)
  1014. (cl-member-if (lambda (suffix)
  1015. (and (listp suffix)
  1016. (let* ((def (nth 2 suffix))
  1017. (cmd (plist-get def :command)))
  1018. (if (symbolp loc)
  1019. (eq cmd loc)
  1020. (equal (transient--kbd
  1021. (or (plist-get def :key)
  1022. (transient--command-key cmd)))
  1023. loc)))))
  1024. (aref group 3)))
  1025. (defun transient--kbd (keys)
  1026. (when (vectorp keys)
  1027. (setq keys (key-description keys)))
  1028. (when (stringp keys)
  1029. (setq keys (kbd keys)))
  1030. keys)
  1031. (defun transient--spec-key (spec)
  1032. (let ((plist (nth 2 spec)))
  1033. (or (plist-get plist :key)
  1034. (transient--command-key
  1035. (plist-get plist :command)))))
  1036. (defun transient--command-key (cmd)
  1037. (when-let ((obj (get cmd 'transient--suffix)))
  1038. (cond ((slot-boundp obj 'key)
  1039. (oref obj key))
  1040. ((slot-exists-p obj 'shortarg)
  1041. (if (slot-boundp obj 'shortarg)
  1042. (oref obj shortarg)
  1043. (transient--derive-shortarg (oref obj argument)))))))
  1044. (defun transient--nthcdr (n list)
  1045. (nthcdr (if (< n 0) (- (length list) (abs n)) n) list))
  1046. ;;; Variables
  1047. (defvar transient-current-prefix nil
  1048. "The transient from which this suffix command was invoked.
  1049. This is an object representing that transient, use
  1050. `transient-current-command' to get the respective command.")
  1051. (defvar transient-current-command nil
  1052. "The transient from which this suffix command was invoked.
  1053. This is a symbol representing that transient, use
  1054. `current-transient-object' to get the respective object.")
  1055. (defvar transient-current-suffixes nil
  1056. "The suffixes of the transient from which this suffix command was invoked.
  1057. This is a list of objects. Usually it is sufficient to instead
  1058. use the function `transient-args', which returns a list of
  1059. values. In complex cases it might be necessary to use this
  1060. variable instead.")
  1061. (defvar transient-exit-hook nil
  1062. "Hook run after exiting a transient.")
  1063. (defvar transient--prefix nil)
  1064. (defvar transient--layout nil)
  1065. (defvar transient--suffixes nil)
  1066. (defconst transient--stay t "Do not exit the transient.")
  1067. (defconst transient--exit nil "Do exit the transient.")
  1068. (defvar transient--exitp nil "Whether to exit the transient.")
  1069. (defvar transient--showp nil "Whether the transient is show in a popup buffer.")
  1070. (defvar transient--helpp nil "Whether help-mode is active.")
  1071. (defvar transient--editp nil "Whether edit-mode is active.")
  1072. (defvar transient--active-infix nil "The active infix awaiting user input.")
  1073. (defvar transient--timer nil)
  1074. (defvar transient--stack nil)
  1075. (defvar transient--buffer-name " *transient*"
  1076. "Name of the transient buffer.")
  1077. (defvar transient--window nil
  1078. "The window used to display the transient popup.")
  1079. (defvar transient--original-window nil
  1080. "The window that was selected before the transient was invoked.
  1081. Usually it remains selected while the transient is active.")
  1082. (define-obsolete-variable-alias 'transient--source-buffer
  1083. 'transient--original-buffer "Transient 0.2.0")
  1084. (defvar transient--original-buffer nil
  1085. "The buffer that was current before the transient was invoked.
  1086. Usually it remains current while the transient is active.")
  1087. (defvar transient--debug nil "Whether put debug information into *Messages*.")
  1088. (defvar transient--history nil)
  1089. (defvar transient--scroll-commands
  1090. '(transient-scroll-up
  1091. transient-scroll-down
  1092. mwheel-scroll
  1093. scroll-bar-toolkit-scroll))
  1094. ;;; Identities
  1095. (defun transient-suffix-object (&optional command)
  1096. "Return the object associated with the current suffix command.
  1097. Each suffix commands is associated with an object, which holds
  1098. additional information about the suffix, such as its value (in
  1099. the case of an infix command, which is a kind of suffix command).
  1100. This function is intended to be called by infix commands, whose
  1101. command definition usually (at least when defined using
  1102. `transient-define-infix') is this:
  1103. (lambda ()
  1104. (interactive)
  1105. (let ((obj (transient-suffix-object)))
  1106. (transient-infix-set obj (transient-infix-read obj)))
  1107. (transient--show))
  1108. \(User input is read outside of `interactive' to prevent the
  1109. command from being added to `command-history'. See #23.)
  1110. Such commands need to be able to access their associated object
  1111. to guide how `transient-infix-read' reads the new value and to
  1112. store the read value. Other suffix commands (including non-infix
  1113. commands) may also need the object to guide their behavior.
  1114. This function attempts to return the object associated with the
  1115. current suffix command even if the suffix command was not invoked
  1116. from a transient. (For some suffix command that is a valid thing
  1117. to do, for others it is not.) In that case nil may be returned
  1118. if the command was not defined using one of the macros intended
  1119. to define such commands.
  1120. The optional argument COMMAND is intended for internal use. If
  1121. you are contemplating using it in your own code, then you should
  1122. probably use this instead:
  1123. (get COMMAND 'transient--suffix)"
  1124. (when command
  1125. (cl-check-type command command))
  1126. (if (or transient--prefix
  1127. transient-current-prefix)
  1128. (cl-find-if (lambda (obj)
  1129. (eq (transient--suffix-command obj)
  1130. (or command this-original-command)))
  1131. (or transient--suffixes
  1132. transient-current-suffixes))
  1133. (when-let ((obj (get (or command this-command) 'transient--suffix))
  1134. (obj (clone obj)))
  1135. (transient-init-scope obj)
  1136. (transient-init-value obj)
  1137. obj)))
  1138. (defun transient--suffix-command (object)
  1139. "Return the command represented by OBJECT.
  1140. If the value of OBJECT's `command' slot is a command, then return
  1141. that. Otherwise it is a symbol whose `transient--infix-command'
  1142. property holds an anonymous command, which is returned instead."
  1143. (cl-check-type object transient-suffix)
  1144. (let ((sym (oref object command)))
  1145. (if (commandp sym)
  1146. sym
  1147. (get sym 'transient--infix-command))))
  1148. (defun transient--suffix-symbol (arg)
  1149. "Return a symbol representing ARG.
  1150. ARG must be a command and/or a symbol. If it is a symbol,
  1151. then just return it. Otherwise return the symbol whose
  1152. `transient--infix-command' property's value is ARG."
  1153. (or (cl-typep arg 'command)
  1154. (cl-typep arg 'symbol)
  1155. (signal 'wrong-type-argument `((command symbol) ,arg)))
  1156. (if (symbolp arg)
  1157. arg
  1158. (let* ((obj (transient-suffix-object))
  1159. (sym (oref obj command)))
  1160. (if (eq (get sym 'transient--infix-command) arg)
  1161. sym
  1162. (catch 'found
  1163. (mapatoms (lambda (sym)
  1164. (when (eq (get sym 'transient--infix-command) arg)
  1165. (throw 'found sym)))))))))
  1166. ;;; Keymaps
  1167. (defvar transient-base-map
  1168. (let ((map (make-sparse-keymap)))
  1169. (define-key map (kbd "ESC ESC ESC") 'transient-quit-all)
  1170. (define-key map (kbd "C-g") 'transient-quit-one)
  1171. (define-key map (kbd "C-q") 'transient-quit-all)
  1172. (define-key map (kbd "C-z") 'transient-suspend)
  1173. (define-key map (kbd "C-v") 'transient-scroll-up)
  1174. (define-key map (kbd "C-M-v") 'transient-scroll-down)
  1175. (define-key map [next] 'transient-scroll-up)
  1176. (define-key map [prior] 'transient-scroll-down)
  1177. map)
  1178. "Parent of other keymaps used by Transient.
  1179. This is the parent keymap of all the keymaps that are used in
  1180. all transients: `transient-map' (which in turn is the parent
  1181. of the transient-specific keymaps), `transient-edit-map' and
  1182. `transient-sticky-map'.
  1183. If you change a binding here, then you might also have to edit
  1184. `transient-sticky-map' and `transient-common-commands'. While
  1185. the latter isn't a proper transient prefix command, it can be
  1186. edited using the same functions as used for transients.
  1187. If you add a new command here, then you must also add a binding
  1188. to `transient-predicate-map'.")
  1189. (defvar transient-map
  1190. (let ((map (make-sparse-keymap)))
  1191. (set-keymap-parent map transient-base-map)
  1192. (define-key map (kbd "C-p") 'universal-argument)
  1193. (define-key map (kbd "C--") 'negative-argument)
  1194. (define-key map (kbd "C-t") 'transient-show)
  1195. (define-key map (kbd "?") 'transient-help)
  1196. (define-key map (kbd "C-h") 'transient-help)
  1197. ;; Also bound to "C-x p" and "C-x n" in transient-common-commands.
  1198. (define-key map (kbd "C-M-p") 'transient-history-prev)
  1199. (define-key map (kbd "C-M-n") 'transient-history-next)
  1200. map)
  1201. "Top-level keymap used by all transients.
  1202. If you add a new command here, then you must also add a binding
  1203. to `transient-predicate-map'. Also see `transient-base-map'.")
  1204. (defvar transient-edit-map
  1205. (let ((map (make-sparse-keymap)))
  1206. (set-keymap-parent map transient-base-map)
  1207. (define-key map (kbd "?") 'transient-help)
  1208. (define-key map (kbd "C-h") 'transient-help)
  1209. (define-key map (kbd "C-x l") 'transient-set-level)
  1210. map)
  1211. "Keymap that is active while a transient in is in \"edit mode\".")
  1212. (defvar transient-sticky-map
  1213. (let ((map (make-sparse-keymap)))
  1214. (set-keymap-parent map transient-base-map)
  1215. (define-key map (kbd "C-g") 'transient-quit-seq)
  1216. map)
  1217. "Keymap that is active while an incomplete key sequence is active.")
  1218. (defvar transient--common-command-prefixes '(?\C-x))
  1219. (put 'transient-common-commands
  1220. 'transient--layout
  1221. (cl-mapcan
  1222. (lambda (s) (transient--parse-child 'transient-common-commands s))
  1223. '([:hide (lambda ()
  1224. (and (not (memq (car transient--redisplay-key)
  1225. transient--common-command-prefixes))
  1226. (not transient-show-common-commands)))
  1227. ["Value commands"
  1228. ("C-x s " "Set" transient-set)
  1229. ("C-x C-s" "Save" transient-save)
  1230. ("C-x p " "Previous value" transient-history-prev)
  1231. ("C-x n " "Next value" transient-history-next)]
  1232. ["Sticky commands"
  1233. ;; Like `transient-sticky-map' except that
  1234. ;; "C-g" has to be bound to a different command.
  1235. ("C-g" "Quit prefix or transient" transient-quit-one)
  1236. ("C-q" "Quit transient stack" transient-quit-all)
  1237. ("C-z" "Suspend transient stack" transient-suspend)]
  1238. ["Customize"
  1239. ("C-x t" transient-toggle-common
  1240. :description (lambda ()
  1241. (if transient-show-common-commands
  1242. "Hide common commands"
  1243. "Show common permanently")))
  1244. ("C-x l" "Show/hide suffixes" transient-set-level)]])))
  1245. (defvar transient-predicate-map
  1246. (let ((map (make-sparse-keymap)))
  1247. (define-key map [handle-switch-frame] 'transient--do-suspend)
  1248. (define-key map [transient-suspend] 'transient--do-suspend)
  1249. (define-key map [transient-help] 'transient--do-stay)
  1250. (define-key map [transient-set-level] 'transient--do-stay)
  1251. (define-key map [transient-history-prev] 'transient--do-stay)
  1252. (define-key map [transient-history-next] 'transient--do-stay)
  1253. (define-key map [universal-argument] 'transient--do-stay)
  1254. (define-key map [negative-argument] 'transient--do-stay)
  1255. (define-key map [digit-argument] 'transient--do-stay)
  1256. (define-key map [transient-quit-all] 'transient--do-quit-all)
  1257. (define-key map [transient-quit-one] 'transient--do-quit-one)
  1258. (define-key map [transient-quit-seq] 'transient--do-stay)
  1259. (define-key map [transient-show] 'transient--do-stay)
  1260. (define-key map [transient-update] 'transient--do-stay)
  1261. (define-key map [transient-toggle-common] 'transient--do-stay)
  1262. (define-key map [transient-set] 'transient--do-call)
  1263. (define-key map [transient-save] 'transient--do-call)
  1264. (define-key map [describe-key-briefly] 'transient--do-stay)
  1265. (define-key map [describe-key] 'transient--do-stay)
  1266. (define-key map [transient-scroll-up] 'transient--do-stay)
  1267. (define-key map [transient-scroll-down] 'transient--do-stay)
  1268. (define-key map [mwheel-scroll] 'transient--do-stay)
  1269. (define-key map [scroll-bar-toolkit-scroll] 'transient--do-stay)
  1270. (define-key map [transient-noop] 'transient--do-noop)
  1271. (define-key map [transient-mouse-push-button] 'transient--do-move)
  1272. (define-key map [transient-push-button] 'transient--do-move)
  1273. (define-key map [transient-backward-button] 'transient--do-move)
  1274. (define-key map [transient-forward-button] 'transient--do-move)
  1275. (define-key map [transient-isearch-backward] 'transient--do-move)
  1276. (define-key map [transient-isearch-forward] 'transient--do-move)
  1277. map)
  1278. "Base keymap used to map common commands to their transient behavior.
  1279. The \"transient behavior\" of a command controls, among other
  1280. things, whether invoking the command causes the transient to be
  1281. exited or not and whether infix arguments are exported before
  1282. doing so.
  1283. Each \"key\" is a command that is common to all transients and
  1284. that is bound in `transient-map', `transient-edit-map',
  1285. `transient-sticky-map' and/or `transient-common-command'.
  1286. Each binding is a \"pre-command\", a function that controls the
  1287. transient behavior of the respective command.
  1288. For transient commands that are bound in individual transients,
  1289. the transient behavior is specified using the `:transient' slot
  1290. of the corresponding object.")
  1291. (defvar transient-popup-navigation-map)
  1292. (defvar transient--transient-map nil)
  1293. (defvar transient--predicate-map nil)
  1294. (defvar transient--redisplay-map nil)
  1295. (defvar transient--redisplay-key nil)
  1296. (defun transient--push-keymap (map)
  1297. (transient--debug " push %s%s" map (if (symbol-value map) "" " VOID"))
  1298. (with-demoted-errors "transient--push-keymap: %S"
  1299. (internal-push-keymap (symbol-value map) 'overriding-terminal-local-map)))
  1300. (defun transient--pop-keymap (map)
  1301. (transient--debug " pop %s%s" map (if (symbol-value map) "" " VOID"))
  1302. (with-demoted-errors "transient--pop-keymap: %S"
  1303. (internal-pop-keymap (symbol-value map) 'overriding-terminal-local-map)))
  1304. (defun transient--make-transient-map ()
  1305. (let ((map (make-sparse-keymap)))
  1306. (set-keymap-parent map (if transient--editp
  1307. transient-edit-map
  1308. transient-map))
  1309. (dolist (obj transient--suffixes)
  1310. (let ((key (oref obj key)))
  1311. (when (vectorp key)
  1312. (setq key (key-description key))
  1313. (oset obj key key))
  1314. (when transient-substitute-key-function
  1315. (setq key (save-match-data
  1316. (funcall transient-substitute-key-function obj)))
  1317. (oset obj key key))
  1318. (let ((kbd (kbd key))
  1319. (cmd (transient--suffix-command obj)))
  1320. (when-let ((conflict (and transient-detect-key-conflicts
  1321. (transient--lookup-key map kbd))))
  1322. (unless (eq cmd conflict)
  1323. (error "Cannot bind %S to %s and also %s"
  1324. (string-trim key)
  1325. cmd conflict)))
  1326. (define-key map kbd cmd))))
  1327. (when transient-enable-popup-navigation
  1328. (setq map
  1329. (make-composed-keymap (list map transient-popup-navigation-map))))
  1330. map))
  1331. (defun transient--make-predicate-map ()
  1332. (let ((map (make-sparse-keymap)))
  1333. (set-keymap-parent map transient-predicate-map)
  1334. (dolist (obj transient--suffixes)
  1335. (let* ((cmd (oref obj command))
  1336. (sub-prefix (and (symbolp cmd) (get cmd 'transient--prefix)))
  1337. (sym (transient--suffix-symbol cmd)))
  1338. (cond
  1339. ((oref obj inapt)
  1340. (define-key map (vector sym) 'transient--do-warn-inapt))
  1341. ((slot-boundp obj 'transient)
  1342. (define-key map (vector sym)
  1343. (let ((do (oref obj transient)))
  1344. (pcase do
  1345. (`t (if sub-prefix
  1346. 'transient--do-replace
  1347. 'transient--do-stay))
  1348. (`nil 'transient--do-exit)
  1349. (_ do)))))
  1350. ((not (lookup-key transient-predicate-map (vector sym)))
  1351. (define-key map (vector sym)
  1352. (if sub-prefix
  1353. 'transient--do-replace
  1354. (or (oref transient--prefix transient-suffix)
  1355. 'transient--do-exit)))))))
  1356. map))
  1357. (defun transient--make-redisplay-map ()
  1358. (setq transient--redisplay-key
  1359. (cl-case this-command
  1360. (transient-update
  1361. (setq transient--showp t)
  1362. (setq unread-command-events
  1363. (listify-key-sequence (this-single-command-raw-keys))))
  1364. (transient-quit-seq
  1365. (setq unread-command-events
  1366. (butlast (listify-key-sequence
  1367. (this-single-command-raw-keys))
  1368. 2))
  1369. (butlast transient--redisplay-key))
  1370. (t nil)))
  1371. (let ((topmap (make-sparse-keymap))
  1372. (submap (make-sparse-keymap)))
  1373. (when transient--redisplay-key
  1374. (define-key topmap (vconcat transient--redisplay-key) submap)
  1375. (set-keymap-parent submap transient-sticky-map))
  1376. (map-keymap-internal
  1377. (lambda (key def)
  1378. (when (and (not (eq key ?\e))
  1379. (listp def)
  1380. (keymapp def))
  1381. (define-key topmap (vconcat transient--redisplay-key (list key))
  1382. 'transient-update)))
  1383. (if transient--redisplay-key
  1384. (lookup-key transient--transient-map (vconcat transient--redisplay-key))
  1385. transient--transient-map))
  1386. topmap))
  1387. ;;; Setup
  1388. (defun transient-setup (&optional name layout edit &rest params)
  1389. "Setup the transient specified by NAME.
  1390. This function is called by transient prefix commands to setup the
  1391. transient. In that case NAME is mandatory, LAYOUT and EDIT must
  1392. be nil and PARAMS may be (but usually is not) used to set e.g. the
  1393. \"scope\" of the transient (see `transient-define-prefix').
  1394. This function is also called internally in which case LAYOUT and
  1395. EDIT may be non-nil."
  1396. (transient--debug 'setup)
  1397. (when (> (minibuffer-depth) 0)
  1398. (user-error "Cannot invoke transient %s while minibuffer is active" name))
  1399. (transient--with-emergency-exit
  1400. (cond
  1401. ((not name)
  1402. ;; Switching between regular and edit mode.
  1403. (transient--pop-keymap 'transient--transient-map)
  1404. (transient--pop-keymap 'transient--redisplay-map)
  1405. (setq name (oref transient--prefix command))
  1406. (setq params (list :scope (oref transient--prefix scope))))
  1407. (transient--transient-map
  1408. ;; Invoked as a ":transient-non-suffix 'transient--do-{stay,call}"
  1409. ;; of an outer prefix. Unlike the usual `transient--do-replace',
  1410. ;; these predicates fail to clean up after the outer prefix.
  1411. (transient--pop-keymap 'transient--transient-map)
  1412. (transient--pop-keymap 'transient--redisplay-map))
  1413. ((not (or layout ; resuming parent/suspended prefix
  1414. transient-current-command)) ; entering child prefix
  1415. (transient--stack-zap)) ; replace suspended prefix, if any
  1416. (edit
  1417. ;; Returning from help to edit.
  1418. (setq transient--editp t)))
  1419. (transient--init-objects name layout params)
  1420. (transient--history-init transient--prefix)
  1421. (setq transient--predicate-map (transient--make-predicate-map))
  1422. (setq transient--transient-map (transient--make-transient-map))
  1423. (setq transient--redisplay-map (transient--make-redisplay-map))
  1424. (setq transient--original-window (selected-window))
  1425. (setq transient--original-buffer (current-buffer))
  1426. (transient--redisplay)
  1427. (transient--init-transient)
  1428. (transient--suspend-which-key-mode)))
  1429. (cl-defgeneric transient-setup-children (group children)
  1430. "Setup the CHILDREN of GROUP.
  1431. If the value of the `setup-children' slot is non-nil, then call
  1432. that function with CHILDREN as the only argument and return the
  1433. value. Otherwise return CHILDREN as is."
  1434. (if (slot-boundp group 'setup-children)
  1435. (funcall (oref group setup-children) children)
  1436. children))
  1437. (defun transient--init-objects (name layout params)
  1438. (setq transient--prefix (transient--init-prefix name params))
  1439. (setq transient--layout (or layout (transient--init-suffixes name)))
  1440. (setq transient--suffixes (transient--flatten-suffixes transient--layout)))
  1441. (defun transient--init-prefix (name &optional params)
  1442. (let ((obj (let ((proto (get name 'transient--prefix)))
  1443. (apply #'clone proto
  1444. :prototype proto
  1445. :level (or (alist-get t (alist-get name transient-levels))
  1446. transient-default-level)
  1447. params))))
  1448. (transient-init-value obj)
  1449. obj))
  1450. (defun transient--init-suffixes (name)
  1451. (let ((levels (alist-get name transient-levels)))
  1452. (cl-mapcan (lambda (c) (transient--init-child levels c))
  1453. (append (get name 'transient--layout)
  1454. (and (not transient--editp)
  1455. (get 'transient-common-commands
  1456. 'transient--layout))))))
  1457. (defun transient--flatten-suffixes (layout)
  1458. (cl-labels ((s (def)
  1459. (cond
  1460. ((stringp def) nil)
  1461. ((listp def) (cl-mapcan #'s def))
  1462. ((transient-group--eieio-childp def)
  1463. (cl-mapcan #'s (oref def suffixes)))
  1464. ((transient-suffix--eieio-childp def)
  1465. (list def)))))
  1466. (cl-mapcan #'s layout)))
  1467. (defun transient--init-child (levels spec)
  1468. (cl-etypecase spec
  1469. (vector (transient--init-group levels spec))
  1470. (list (transient--init-suffix levels spec))
  1471. (string (list spec))))
  1472. (defun transient--init-group (levels spec)
  1473. (pcase-let ((`(,level ,class ,args ,children) (append spec nil)))
  1474. (when (transient--use-level-p level)
  1475. (let ((obj (apply class :level level args)))
  1476. (when (transient--use-suffix-p obj)
  1477. (when-let ((suffixes
  1478. (cl-mapcan (lambda (c) (transient--init-child levels c))
  1479. (transient-setup-children obj children))))
  1480. (oset obj suffixes suffixes)
  1481. (list obj)))))))
  1482. (defun transient--init-suffix (levels spec)
  1483. (pcase-let* ((`(,level ,class ,args) spec)
  1484. (cmd (plist-get args :command))
  1485. (level (or (alist-get (transient--suffix-symbol cmd) levels)
  1486. level)))
  1487. (let ((fn (and (symbolp cmd)
  1488. (symbol-function cmd))))
  1489. (when (autoloadp fn)
  1490. (transient--debug " autoload %s" cmd)
  1491. (autoload-do-load fn)))
  1492. (when (transient--use-level-p level)
  1493. (let ((obj (if-let ((proto (and cmd
  1494. (symbolp cmd)
  1495. (get cmd 'transient--suffix))))
  1496. (apply #'clone proto :level level args)
  1497. (apply class :level level args))))
  1498. (transient--init-suffix-key obj)
  1499. (transient--ensure-infix-command obj)
  1500. (when (transient--use-suffix-p obj)
  1501. (if (transient--inapt-suffix-p obj)
  1502. (oset obj inapt t)
  1503. (transient-init-scope obj)
  1504. (transient-init-value obj))
  1505. (list obj))))))
  1506. (cl-defmethod transient--init-suffix-key ((obj transient-suffix))
  1507. (unless (slot-boundp obj 'key)
  1508. (error "No key for %s" (oref obj command))))
  1509. (cl-defmethod transient--init-suffix-key ((obj transient-argument))
  1510. (if (transient-switches--eieio-childp obj)
  1511. (cl-call-next-method obj)
  1512. (unless (slot-boundp obj 'shortarg)
  1513. (when-let ((shortarg (transient--derive-shortarg (oref obj argument))))
  1514. (oset obj shortarg shortarg)))
  1515. (unless (slot-boundp obj 'key)
  1516. (if (slot-boundp obj 'shortarg)
  1517. (oset obj key (oref obj shortarg))
  1518. (error "No key for %s" (oref obj command))))))
  1519. (defun transient--use-level-p (level &optional edit)
  1520. (or (and transient--editp (not edit))
  1521. (and (>= level 1)
  1522. (<= level (oref transient--prefix level)))))
  1523. (defun transient--use-suffix-p (obj)
  1524. (transient--do-suffix-p
  1525. (oref obj if)
  1526. (oref obj if-not)
  1527. (oref obj if-nil)
  1528. (oref obj if-non-nil)
  1529. (oref obj if-mode)
  1530. (oref obj if-not-mode)
  1531. (oref obj if-derived)
  1532. (oref obj if-not-derived)
  1533. t))
  1534. (defun transient--inapt-suffix-p (obj)
  1535. (transient--do-suffix-p
  1536. (oref obj inapt-if)
  1537. (oref obj inapt-if-not)
  1538. (oref obj inapt-if-nil)
  1539. (oref obj inapt-if-non-nil)
  1540. (oref obj inapt-if-mode)
  1541. (oref obj inapt-if-not-mode)
  1542. (oref obj inapt-if-derived)
  1543. (oref obj inapt-if-not-derived)
  1544. nil))
  1545. (defun transient--do-suffix-p
  1546. (if if-not if-nil if-non-nil if-mode if-not-mode if-derived if-not-derived
  1547. default)
  1548. (cond
  1549. (if (funcall if))
  1550. (if-not (not (funcall if-not)))
  1551. (if-non-nil (symbol-value if-non-nil))
  1552. (if-nil (not (symbol-value if-nil)))
  1553. (if-mode (if (atom if-mode)
  1554. (eq major-mode if-mode)
  1555. (memq major-mode if-mode)))
  1556. (if-not-mode (not (if (atom if-not-mode)
  1557. (eq major-mode if-not-mode)
  1558. (memq major-mode if-not-mode))))
  1559. (if-derived (if (atom if-derived)
  1560. (derived-mode-p if-derived)
  1561. (apply #'derived-mode-p if-derived)))
  1562. (if-not-derived (not (if (atom if-not-derived)
  1563. (derived-mode-p if-not-derived)
  1564. (apply #'derived-mode-p if-not-derived))))
  1565. (t default)))
  1566. (defun transient--suffix-predicate (spec)
  1567. (let ((plist (nth 2 spec)))
  1568. (seq-some (lambda (prop)
  1569. (when-let ((pred (plist-get plist prop)))
  1570. (list prop pred)))
  1571. '( :if :if-not
  1572. :if-nil :if-non-nil
  1573. :if-mode :if-not-mode
  1574. :if-derived :if-not-derived
  1575. :inapt-if :inapt-if-not
  1576. :inapt-if-nil :inapt-if-non-nil
  1577. :inapt-if-mode :inapt-if-not-mode
  1578. :inapt-if-derived :inapt-if-not-derived))))
  1579. ;;; Flow-Control
  1580. (defun transient--init-transient ()
  1581. (transient--debug 'init-transient)
  1582. (transient--push-keymap 'transient--transient-map)
  1583. (transient--push-keymap 'transient--redisplay-map)
  1584. (add-hook 'pre-command-hook #'transient--pre-command)
  1585. (add-hook 'minibuffer-setup-hook #'transient--minibuffer-setup)
  1586. (add-hook 'minibuffer-exit-hook #'transient--minibuffer-exit)
  1587. (add-hook 'post-command-hook #'transient--post-command)
  1588. (advice-add 'abort-recursive-edit :after #'transient--minibuffer-exit)
  1589. (when transient--exitp
  1590. ;; This prefix command was invoked as the suffix of another.
  1591. ;; Prevent `transient--post-command' from removing the hooks
  1592. ;; that we just added.
  1593. (setq transient--exitp 'replace)))
  1594. (defun transient--pre-command ()
  1595. (transient--debug 'pre-command)
  1596. (cond
  1597. ((memq this-command '(transient-update transient-quit-seq))
  1598. (transient--pop-keymap 'transient--redisplay-map))
  1599. ((and transient--helpp
  1600. (not (memq this-command '(transient-quit-one
  1601. transient-quit-all))))
  1602. (cond
  1603. ((transient-help)
  1604. (transient--do-suspend)
  1605. (setq this-command 'transient-suspend)
  1606. (transient--pre-exit))
  1607. ((not (transient--edebug-command-p))
  1608. (setq this-command 'transient-undefined))))
  1609. ((and transient--editp
  1610. (transient-suffix-object)
  1611. (not (memq this-command '(transient-quit-one
  1612. transient-quit-all
  1613. transient-help))))
  1614. (setq this-command 'transient-set-level))
  1615. (t
  1616. (setq transient--exitp nil)
  1617. (when (eq (if-let ((fn (transient--get-predicate-for
  1618. this-original-command)))
  1619. (let ((action (funcall fn)))
  1620. (when (eq action transient--exit)
  1621. (setq transient--exitp (or transient--exitp t)))
  1622. action)
  1623. (if (let ((keys (this-command-keys-vector)))
  1624. (eq (aref keys (1- (length keys))) ?\C-g))
  1625. (setq this-command 'transient-noop)
  1626. (unless (transient--edebug-command-p)
  1627. (setq this-command 'transient-undefined)))
  1628. transient--stay)
  1629. transient--exit)
  1630. (transient--pre-exit)))))
  1631. (defun transient--get-predicate-for (cmd)
  1632. (or (lookup-key transient--predicate-map
  1633. (vector (transient--suffix-symbol cmd)))
  1634. (oref transient--prefix transient-non-suffix)))
  1635. (defun transient--pre-exit ()
  1636. (transient--debug 'pre-exit)
  1637. (transient--delete-window)
  1638. (transient--timer-cancel)
  1639. (transient--pop-keymap 'transient--transient-map)
  1640. (transient--pop-keymap 'transient--redisplay-map)
  1641. (remove-hook 'pre-command-hook #'transient--pre-command)
  1642. (unless transient--showp
  1643. (let ((message-log-max nil))
  1644. (message "")))
  1645. (setq transient--transient-map nil)
  1646. (setq transient--predicate-map nil)
  1647. (setq transient--redisplay-map nil)
  1648. (setq transient--redisplay-key nil)
  1649. (setq transient--showp nil)
  1650. (setq transient--helpp nil)
  1651. (setq transient--editp nil)
  1652. (setq transient--prefix nil)
  1653. (setq transient--layout nil)
  1654. (setq transient--suffixes nil)
  1655. (setq transient--original-window nil)
  1656. (setq transient--original-buffer nil)
  1657. (setq transient--window nil))
  1658. (defun transient--delete-window ()
  1659. (when (window-live-p transient--window)
  1660. (let ((buf (window-buffer transient--window)))
  1661. (with-demoted-errors "Error while exiting transient: %S"
  1662. (delete-window transient--window))
  1663. (kill-buffer buf))))
  1664. (defun transient--export ()
  1665. (setq transient-current-prefix transient--prefix)
  1666. (setq transient-current-command (oref transient--prefix command))
  1667. (setq transient-current-suffixes transient--suffixes)
  1668. (transient--history-push transient--prefix))
  1669. (defun transient--minibuffer-setup ()
  1670. (transient--debug 'minibuffer-setup)
  1671. (unless (> (minibuffer-depth) 1)
  1672. (unless transient--exitp
  1673. (transient--pop-keymap 'transient--transient-map)
  1674. (transient--pop-keymap 'transient--redisplay-map)
  1675. (remove-hook 'pre-command-hook #'transient--pre-command))
  1676. (remove-hook 'post-command-hook #'transient--post-command)))
  1677. (defun transient--minibuffer-exit ()
  1678. (transient--debug 'minibuffer-exit)
  1679. (unless (> (minibuffer-depth) 1)
  1680. (unless transient--exitp
  1681. (transient--push-keymap 'transient--transient-map)
  1682. (transient--push-keymap 'transient--redisplay-map)
  1683. (add-hook 'pre-command-hook #'transient--pre-command))
  1684. (add-hook 'post-command-hook #'transient--post-command)))
  1685. (defun transient--suspend-override (&optional minibuffer-hooks)
  1686. (transient--debug 'suspend-override)
  1687. (transient--pop-keymap 'transient--transient-map)
  1688. (transient--pop-keymap 'transient--redisplay-map)
  1689. (remove-hook 'pre-command-hook #'transient--pre-command)
  1690. (remove-hook 'post-command-hook #'transient--post-command)
  1691. (when minibuffer-hooks
  1692. (remove-hook 'minibuffer-setup-hook #'transient--minibuffer-setup)
  1693. (remove-hook 'minibuffer-exit-hook #'transient--minibuffer-exit)
  1694. (advice-remove 'abort-recursive-edit #'transient--minibuffer-exit)))
  1695. (defun transient--resume-override (&optional minibuffer-hooks)
  1696. (transient--debug 'resume-override)
  1697. (transient--push-keymap 'transient--transient-map)
  1698. (transient--push-keymap 'transient--redisplay-map)
  1699. (add-hook 'pre-command-hook #'transient--pre-command)
  1700. (add-hook 'post-command-hook #'transient--post-command)
  1701. (when minibuffer-hooks
  1702. (add-hook 'minibuffer-setup-hook #'transient--minibuffer-setup)
  1703. (add-hook 'minibuffer-exit-hook #'transient--minibuffer-exit)
  1704. (advice-add 'abort-recursive-edit :after #'transient--minibuffer-exit)))
  1705. (defun transient--post-command ()
  1706. (transient--debug 'post-command)
  1707. (if transient--exitp
  1708. (progn
  1709. (unless (and (eq transient--exitp 'replace)
  1710. (or transient--prefix
  1711. ;; The current command could act as a prefix,
  1712. ;; but decided not to call `transient-setup'.
  1713. (prog1 nil (transient--stack-zap))))
  1714. (remove-hook 'minibuffer-setup-hook #'transient--minibuffer-setup)
  1715. (remove-hook 'minibuffer-exit-hook #'transient--minibuffer-exit)
  1716. (advice-remove 'abort-recursive-edit #'transient--minibuffer-exit)
  1717. (remove-hook 'post-command-hook #'transient--post-command))
  1718. (setq transient-current-prefix nil)
  1719. (setq transient-current-command nil)
  1720. (setq transient-current-suffixes nil)
  1721. (let ((resume (and transient--stack
  1722. (not (memq transient--exitp '(replace suspend))))))
  1723. (setq transient--exitp nil)
  1724. (setq transient--helpp nil)
  1725. (setq transient--editp nil)
  1726. (run-hooks 'transient-exit-hook)
  1727. (when resume
  1728. (transient--stack-pop))))
  1729. (transient--pop-keymap 'transient--redisplay-map)
  1730. (setq transient--redisplay-map (transient--make-redisplay-map))
  1731. (transient--push-keymap 'transient--redisplay-map)
  1732. (unless (eq this-command (oref transient--prefix command))
  1733. (transient--redisplay))))
  1734. (defun transient--stack-push ()
  1735. (transient--debug 'stack-push)
  1736. (push (list (oref transient--prefix command)
  1737. transient--layout
  1738. transient--editp
  1739. :scope (oref transient--prefix scope))
  1740. transient--stack))
  1741. (defun transient--stack-pop ()
  1742. (transient--debug 'stack-pop)
  1743. (and transient--stack
  1744. (prog1 t (apply #'transient-setup (pop transient--stack)))))
  1745. (defun transient--stack-zap ()
  1746. (transient--debug 'stack-zap)
  1747. (setq transient--stack nil))
  1748. (defun transient--redisplay ()
  1749. (if (or (eq transient-show-popup t)
  1750. transient--showp)
  1751. (unless (memq this-command transient--scroll-commands)
  1752. (transient--show))
  1753. (when (and (numberp transient-show-popup)
  1754. (not (zerop transient-show-popup))
  1755. (not transient--timer))
  1756. (transient--timer-start))
  1757. (transient--show-brief)))
  1758. (defun transient--timer-start ()
  1759. (setq transient--timer
  1760. (run-at-time (abs transient-show-popup) nil
  1761. (lambda ()
  1762. (transient--timer-cancel)
  1763. (transient--show)
  1764. (let ((message-log-max nil))
  1765. (message ""))))))
  1766. (defun transient--timer-cancel ()
  1767. (when transient--timer
  1768. (cancel-timer transient--timer)
  1769. (setq transient--timer nil)))
  1770. (defun transient--debug (arg &rest args)
  1771. (when transient--debug
  1772. (if (symbolp arg)
  1773. (message "-- %-16s (cmd: %s, event: %S, exit: %s)"
  1774. arg
  1775. (transient--suffix-symbol this-command)
  1776. (key-description (this-command-keys-vector))
  1777. transient--exitp)
  1778. (apply #'message arg args))))
  1779. (defun transient--emergency-exit ()
  1780. "Exit the current transient command after an error occurred.
  1781. When no transient is active (i.e. when `transient--prefix') is
  1782. nil, then do nothing."
  1783. (transient--debug 'emergency-exit)
  1784. (when transient--prefix
  1785. (setq transient--stack nil)
  1786. (setq transient--exitp t)
  1787. (transient--pre-exit)
  1788. (transient--post-command)))
  1789. ;;; Pre-Commands
  1790. (defun transient--do-stay ()
  1791. "Call the command without exporting variables and stay transient."
  1792. transient--stay)
  1793. (defun transient--do-noop ()
  1794. "Call `transient-noop' and stay transient."
  1795. (setq this-command 'transient-noop)
  1796. transient--stay)
  1797. (defun transient--do-warn ()
  1798. "Call `transient-undefined' and stay transient."
  1799. (setq this-command 'transient-undefined)
  1800. transient--stay)
  1801. (defun transient--do-warn-inapt ()
  1802. "Call `transient-inapt' and stay transient."
  1803. (setq this-command 'transient-inapt)
  1804. transient--stay)
  1805. (defun transient--do-call ()
  1806. "Call the command after exporting variables and stay transient."
  1807. (transient--export)
  1808. transient--stay)
  1809. (defun transient--do-exit ()
  1810. "Call the command after exporting variables and exit the transient."
  1811. (transient--export)
  1812. (transient--stack-zap)
  1813. transient--exit)
  1814. (defun transient--do-replace ()
  1815. "Call the transient prefix command, replacing the active transient."
  1816. (transient--export)
  1817. (transient--stack-push)
  1818. (setq transient--exitp 'replace)
  1819. transient--exit)
  1820. (defun transient--do-suspend ()
  1821. "Suspend the active transient, saving the transient stack."
  1822. (transient--stack-push)
  1823. (setq transient--exitp 'suspend)
  1824. transient--exit)
  1825. (defun transient--do-quit-one ()
  1826. "If active, quit help or edit mode, else exit the active transient."
  1827. (cond (transient--helpp
  1828. (setq transient--helpp nil)
  1829. transient--stay)
  1830. (transient--editp
  1831. (setq transient--editp nil)
  1832. (transient-setup)
  1833. transient--stay)
  1834. (t transient--exit)))
  1835. (defun transient--do-quit-all ()
  1836. "Exit all transients without saving the transient stack."
  1837. (transient--stack-zap)
  1838. transient--exit)
  1839. (defun transient--do-move ()
  1840. "Call the command if `transient-enable-popup-navigation' is non-nil.
  1841. In that case behave like `transient--do-stay', otherwise similar
  1842. to `transient--do-warn'."
  1843. (unless transient-enable-popup-navigation
  1844. (setq this-command 'transient-popup-navigation-help))
  1845. transient--stay)
  1846. (put 'transient--do-stay 'transient-color 'transient-blue)
  1847. (put 'transient--do-noop 'transient-color 'transient-blue)
  1848. (put 'transient--do-warn 'transient-color 'transient-blue)
  1849. (put 'transient--do-warn-inapt 'transient-color 'transient-blue)
  1850. (put 'transient--do-call 'transient-color 'transient-blue)
  1851. (put 'transient--do-exit 'transient-color 'transient-red)
  1852. (put 'transient--do-replace 'transient-color 'transient-red)
  1853. (put 'transient--do-suspend 'transient-color 'transient-red)
  1854. (put 'transient--do-quit-one 'transient-color 'transient-red)
  1855. (put 'transient--do-quit-all 'transient-color 'transient-red)
  1856. (put 'transient--do-move 'transient-color 'transient-blue)
  1857. ;;; Commands
  1858. (defun transient-noop ()
  1859. "Do nothing at all."
  1860. (interactive))
  1861. (defun transient-undefined ()
  1862. "Warn the user that the pressed key is not bound to any suffix."
  1863. (interactive)
  1864. (transient--invalid "Unbound suffix"))
  1865. (defun transient-inapt ()
  1866. "Warn the user that the invoked command is inapt."
  1867. (interactive)
  1868. (transient--invalid "Inapt command"))
  1869. (defun transient--invalid (msg)
  1870. (ding)
  1871. (message "%s: `%s' (Use `%s' to abort, `%s' for help) [%s]"
  1872. msg
  1873. (propertize (key-description (this-single-command-keys))
  1874. 'face 'font-lock-warning-face)
  1875. (propertize "C-g" 'face 'transient-key)
  1876. (propertize "?" 'face 'transient-key)
  1877. (propertize (symbol-name (transient--suffix-symbol
  1878. this-original-command))
  1879. 'face 'font-lock-warning-face)))
  1880. (defun transient-toggle-common ()
  1881. "Toggle whether common commands are always shown."
  1882. (interactive)
  1883. (setq transient-show-common-commands (not transient-show-common-commands)))
  1884. (defun transient-suspend ()
  1885. "Suspend the current transient.
  1886. It can later be resumed using `transient-resume' while no other
  1887. transient is active."
  1888. (interactive))
  1889. (defun transient-quit-all ()
  1890. "Exit all transients without saving the transient stack."
  1891. (interactive))
  1892. (defun transient-quit-one ()
  1893. "Exit the current transients, possibly returning to the previous."
  1894. (interactive))
  1895. (defun transient-quit-seq ()
  1896. "Abort the current incomplete key sequence."
  1897. (interactive))
  1898. (defun transient-update ()
  1899. "Redraw the transient's state in the popup buffer."
  1900. (interactive))
  1901. (defun transient-show ()
  1902. "Show the transient's state in the popup buffer."
  1903. (interactive)
  1904. (setq transient--showp t))
  1905. (defvar-local transient--restore-winconf nil)
  1906. (defvar transient-resume-mode)
  1907. (defun transient-help ()
  1908. "Show help for the active transient or one of its suffixes."
  1909. (interactive)
  1910. (if (called-interactively-p 'any)
  1911. (setq transient--helpp t)
  1912. (with-demoted-errors "transient-help: %S"
  1913. (when (lookup-key transient--transient-map
  1914. (this-single-command-raw-keys))
  1915. (setq transient--helpp nil)
  1916. (let ((winconf (current-window-configuration)))
  1917. (transient-show-help
  1918. (if (eq this-original-command 'transient-help)
  1919. transient--prefix
  1920. (or (transient-suffix-object)
  1921. this-original-command)))
  1922. (setq transient--restore-winconf winconf))
  1923. (fit-window-to-buffer nil (frame-height) (window-height))
  1924. (transient-resume-mode)
  1925. (message "Type \"q\" to resume transient command.")
  1926. t))))
  1927. (defun transient-set-level (&optional command level)
  1928. "Set the level of the transient or one of its suffix commands."
  1929. (interactive
  1930. (let ((command this-original-command)
  1931. (prefix (oref transient--prefix command)))
  1932. (and (or (not (eq command 'transient-set-level))
  1933. (and transient--editp
  1934. (setq command prefix)))
  1935. (list command
  1936. (let ((keys (this-single-command-raw-keys)))
  1937. (and (lookup-key transient--transient-map keys)
  1938. (string-to-number
  1939. (let ((transient--active-infix
  1940. (transient-suffix-object command)))
  1941. (transient--show)
  1942. (transient--read-number-N
  1943. (format "Set level for `%s': "
  1944. (transient--suffix-symbol command))
  1945. nil nil (not (eq command prefix)))))))))))
  1946. (cond
  1947. ((not command)
  1948. (setq transient--editp t)
  1949. (transient-setup))
  1950. (level
  1951. (let* ((prefix (oref transient--prefix command))
  1952. (alist (alist-get prefix transient-levels))
  1953. (sym (transient--suffix-symbol command)))
  1954. (if (eq command prefix)
  1955. (progn (oset transient--prefix level level)
  1956. (setq sym t))
  1957. (oset (transient-suffix-object command) level level))
  1958. (setf (alist-get sym alist) level)
  1959. (setf (alist-get prefix transient-levels) alist))
  1960. (transient-save-levels))
  1961. (t
  1962. (transient-undefined))))
  1963. (defun transient-set ()
  1964. "Save the value of the active transient for this Emacs session."
  1965. (interactive)
  1966. (transient-set-value (or transient--prefix transient-current-prefix)))
  1967. (defun transient-save ()
  1968. "Save the value of the active transient persistenly across Emacs sessions."
  1969. (interactive)
  1970. (transient-save-value (or transient--prefix transient-current-prefix)))
  1971. (defun transient-history-next ()
  1972. "Switch to the next value used for the active transient."
  1973. (interactive)
  1974. (let* ((obj transient--prefix)
  1975. (pos (1- (oref obj history-pos)))
  1976. (hst (oref obj history)))
  1977. (if (< pos 0)
  1978. (user-error "End of history")
  1979. (oset obj history-pos pos)
  1980. (oset obj value (nth pos hst))
  1981. (mapc #'transient-init-value transient--suffixes))))
  1982. (defun transient-history-prev ()
  1983. "Switch to the previous value used for the active transient."
  1984. (interactive)
  1985. (let* ((obj transient--prefix)
  1986. (pos (1+ (oref obj history-pos)))
  1987. (hst (oref obj history))
  1988. (len (length hst)))
  1989. (if (> pos (1- len))
  1990. (user-error "End of history")
  1991. (oset obj history-pos pos)
  1992. (oset obj value (nth pos hst))
  1993. (mapc #'transient-init-value transient--suffixes))))
  1994. (defun transient-scroll-up (&optional arg)
  1995. "Scroll text of transient popup window upward ARG lines.
  1996. If ARG is nil scroll near full screen. This is a wrapper
  1997. around `scroll-up-command' (which see)."
  1998. (interactive "^P")
  1999. (with-selected-window transient--window
  2000. (scroll-up-command arg)))
  2001. (defun transient-scroll-down (&optional arg)
  2002. "Scroll text of transient popup window down ARG lines.
  2003. If ARG is nil scroll near full screen. This is a wrapper
  2004. around `scroll-down-command' (which see)."
  2005. (interactive "^P")
  2006. (with-selected-window transient--window
  2007. (scroll-down-command arg)))
  2008. (defun transient-resume ()
  2009. "Resume a previously suspended stack of transients."
  2010. (interactive)
  2011. (cond (transient--stack
  2012. (let ((winconf transient--restore-winconf))
  2013. (kill-local-variable 'transient--restore-winconf)
  2014. (when transient-resume-mode
  2015. (transient-resume-mode -1)
  2016. (quit-window))
  2017. (when winconf
  2018. (set-window-configuration winconf)))
  2019. (transient--stack-pop))
  2020. (transient-resume-mode
  2021. (kill-local-variable 'transient--restore-winconf)
  2022. (transient-resume-mode -1)
  2023. (quit-window))
  2024. (t
  2025. (message "No suspended transient command"))))
  2026. ;;; Value
  2027. ;;;; Init
  2028. (cl-defgeneric transient-init-scope (obj)
  2029. "Set the scope of the suffix object OBJ.
  2030. The scope is actually a property of the transient prefix, not of
  2031. individual suffixes. However it is possible to invoke a suffix
  2032. command directly instead of from a transient. In that case, if
  2033. the suffix expects a scope, then it has to determine that itself
  2034. and store it in its `scope' slot.
  2035. This function is called for all suffix commands, but unless a
  2036. concrete method is implemented this falls through to the default
  2037. implementation, which is a noop.")
  2038. (cl-defmethod transient-init-scope ((_ transient-suffix))
  2039. "Noop." nil)
  2040. (cl-defgeneric transient-init-value (_)
  2041. "Set the initial value of the object OBJ.
  2042. This function is called for all prefix and suffix commands.
  2043. For suffix commands (including infix argument commands) the
  2044. default implementation is a noop. Classes derived from the
  2045. abstract `transient-infix' class must implement this function.
  2046. Non-infix suffix commands usually don't have a value."
  2047. nil)
  2048. (cl-defmethod transient-init-value :around ((obj transient-prefix))
  2049. "If bound, then call OBJ's `init-value' function.
  2050. Otherwise call the primary method according to objects class."
  2051. (if (slot-boundp obj 'init-value)
  2052. (funcall (oref obj init-value) obj)
  2053. (cl-call-next-method obj)))
  2054. (cl-defmethod transient-init-value :around ((obj transient-infix))
  2055. "If bound, then call OBJ's `init-value' function.
  2056. Otherwise call the primary method according to objects class."
  2057. (if (slot-boundp obj 'init-value)
  2058. (funcall (oref obj init-value) obj)
  2059. (cl-call-next-method obj)))
  2060. (cl-defmethod transient-init-value ((obj transient-prefix))
  2061. (if (slot-boundp obj 'value)
  2062. (oref obj value)
  2063. (oset obj value
  2064. (if-let ((saved (assq (oref obj command) transient-values)))
  2065. (cdr saved)
  2066. (if-let ((default (and (slot-boundp obj 'default-value)
  2067. (oref obj default-value))))
  2068. (if (functionp default)
  2069. (funcall default)
  2070. default)
  2071. nil)))))
  2072. (cl-defmethod transient-init-value ((obj transient-switch))
  2073. (oset obj value
  2074. (car (member (oref obj argument)
  2075. (oref transient--prefix value)))))
  2076. (cl-defmethod transient-init-value ((obj transient-option))
  2077. (oset obj value
  2078. (transient--value-match (format "\\`%s\\(.*\\)" (oref obj argument)))))
  2079. (cl-defmethod transient-init-value ((obj transient-switches))
  2080. (oset obj value
  2081. (transient--value-match (oref obj argument-regexp))))
  2082. (defun transient--value-match (re)
  2083. (when-let ((match (cl-find-if (lambda (v)
  2084. (and (stringp v)
  2085. (string-match re v)))
  2086. (oref transient--prefix value))))
  2087. (match-string 1 match)))
  2088. (cl-defmethod transient-init-value ((obj transient-files))
  2089. (oset obj value
  2090. (cdr (assoc "--" (oref transient--prefix value)))))
  2091. ;;;; Read
  2092. (cl-defgeneric transient-infix-read (obj)
  2093. "Determine the new value of the infix object OBJ.
  2094. This function merely determines the value; `transient-infix-set'
  2095. is used to actually store the new value in the object.
  2096. For most infix classes this is done by reading a value from the
  2097. user using the reader specified by the `reader' slot (using the
  2098. `transient-infix' method described below).
  2099. For some infix classes the value is changed without reading
  2100. anything in the minibuffer, i.e. the mere act of invoking the
  2101. infix command determines what the new value should be, based
  2102. on the previous value.")
  2103. (cl-defmethod transient-infix-read :around ((obj transient-infix))
  2104. "Highlight the infix in the popup buffer.
  2105. Also arrange for the transient to be exited in case of an error
  2106. because otherwise Emacs would get stuck in an inconsistent state,
  2107. which might make it necessary to kill it from the outside."
  2108. (let ((transient--active-infix obj))
  2109. (transient--show))
  2110. (transient--with-emergency-exit
  2111. (cl-call-next-method obj)))
  2112. (cl-defmethod transient-infix-read ((obj transient-infix))
  2113. "Read a value while taking care of history.
  2114. This method is suitable for a wide variety of infix commands,
  2115. including but not limited to inline arguments and variables.
  2116. If you do not use this method for your own infix class, then
  2117. you should likely replicate a lot of the behavior of this
  2118. method. If you fail to do so, then users might not appreciate
  2119. the lack of history, for example.
  2120. Only for very simple classes that toggle or cycle through a very
  2121. limited number of possible values should you replace this with a
  2122. simple method that does not handle history. (E.g. for a command
  2123. line switch the only possible values are \"use it\" and \"don't use
  2124. it\", in which case it is pointless to preserve history.)"
  2125. (with-slots (value multi-value always-read allow-empty choices) obj
  2126. (if (and value
  2127. (not multi-value)
  2128. (not always-read)
  2129. transient--prefix)
  2130. (oset obj value nil)
  2131. (let* ((overriding-terminal-local-map nil)
  2132. (reader (oref obj reader))
  2133. (prompt (transient-prompt obj))
  2134. (value (if multi-value (mapconcat #'identity value ",") value))
  2135. (history-key (or (oref obj history-key)
  2136. (oref obj command)))
  2137. (transient--history (alist-get history-key transient-history))
  2138. (transient--history (if (or (null value)
  2139. (eq value (car transient--history)))
  2140. transient--history
  2141. (cons value transient--history)))
  2142. (initial-input (and transient-read-with-initial-input
  2143. (car transient--history)))
  2144. (history (if initial-input
  2145. (cons 'transient--history 1)
  2146. 'transient--history))
  2147. (value
  2148. (cond
  2149. (reader (funcall reader prompt initial-input history))
  2150. (multi-value
  2151. (completing-read-multiple prompt choices nil nil
  2152. initial-input history))
  2153. (choices
  2154. (completing-read prompt choices nil t initial-input history))
  2155. (t (read-string prompt initial-input history)))))
  2156. (cond ((and (equal value "") (not allow-empty))
  2157. (setq value nil))
  2158. ((and (equal value "\"\"") allow-empty)
  2159. (setq value "")))
  2160. (when value
  2161. (when (and (bound-and-true-p ivy-mode)
  2162. (stringp (car transient--history)))
  2163. (set-text-properties 0 (length (car transient--history)) nil
  2164. (car transient--history)))
  2165. (setf (alist-get history-key transient-history)
  2166. (delete-dups transient--history)))
  2167. value))))
  2168. (cl-defmethod transient-infix-read ((obj transient-switch))
  2169. "Toggle the switch on or off."
  2170. (if (oref obj value) nil (oref obj argument)))
  2171. (cl-defmethod transient-infix-read ((obj transient-switches))
  2172. "Cycle through the mutually exclusive switches.
  2173. The last value is \"don't use any of these switches\"."
  2174. (let ((choices (mapcar (apply-partially #'format (oref obj argument-format))
  2175. (oref obj choices))))
  2176. (if-let ((value (oref obj value)))
  2177. (cadr (member value choices))
  2178. (car choices))))
  2179. (cl-defmethod transient-infix-read ((command symbol))
  2180. "Elsewhere use the reader of the infix command COMMAND.
  2181. Use this if you want to share an infix's history with a regular
  2182. stand-alone command."
  2183. (cl-letf (((symbol-function #'transient--show) #'ignore))
  2184. (transient-infix-read (get command 'transient--suffix))))
  2185. ;;;; Readers
  2186. (defun transient-read-file (prompt _initial-input _history)
  2187. "Read a file."
  2188. (file-local-name (expand-file-name (read-file-name prompt))))
  2189. (defun transient-read-existing-file (prompt _initial-input _history)
  2190. "Read an existing file."
  2191. (file-local-name (expand-file-name (read-file-name prompt nil nil t))))
  2192. (defun transient-read-directory (prompt _initial-input _history)
  2193. "Read a directory."
  2194. (file-local-name (expand-file-name (read-directory-name prompt))))
  2195. (defun transient-read-existing-directory (prompt _initial-input _history)
  2196. "Read an existing directory."
  2197. (file-local-name (expand-file-name (read-directory-name prompt nil nil t))))
  2198. (defun transient-read-number-N0 (prompt initial-input history)
  2199. "Read a natural number (including zero) and return it as a string."
  2200. (transient--read-number-N prompt initial-input history t))
  2201. (defun transient-read-number-N+ (prompt initial-input history)
  2202. "Read a natural number (excluding zero) and return it as a string."
  2203. (transient--read-number-N prompt initial-input history nil))
  2204. (defun transient--read-number-N (prompt initial-input history include-zero)
  2205. (save-match-data
  2206. (cl-block nil
  2207. (while t
  2208. (let ((str (read-from-minibuffer prompt initial-input nil nil history)))
  2209. (cond ((string-equal str "")
  2210. (cl-return nil))
  2211. ((string-match-p (if include-zero
  2212. "\\`\\(0\\|[1-9][0-9]*\\)\\'"
  2213. "\\`[1-9][0-9]*\\'")
  2214. str)
  2215. (cl-return str))))
  2216. (message "Please enter a natural number (%s zero)."
  2217. (if include-zero "including" "excluding"))
  2218. (sit-for 1)))))
  2219. (defun transient-read-date (prompt default-time _history)
  2220. "Read a date using `org-read-date' (which see)."
  2221. (require 'org)
  2222. (when (fboundp 'org-read-date)
  2223. (org-read-date 'with-time nil nil prompt default-time)))
  2224. ;;;; Prompt
  2225. (cl-defgeneric transient-prompt (obj)
  2226. "Return the prompt to be used to read infix object OBJ's value.")
  2227. (cl-defmethod transient-prompt ((obj transient-infix))
  2228. "Return the prompt to be used to read infix object OBJ's value.
  2229. This implementation should be suitable for almost all infix
  2230. commands.
  2231. If the value of OBJ's `prompt' slot is non-nil, then it must be
  2232. a string or a function. If it is a string, then use that. If
  2233. it is a function, then call that with OBJ as the only argument.
  2234. That function must return a string, which is then used as the
  2235. prompt.
  2236. Otherwise, if the value of either the `argument' or `variable'
  2237. slot of OBJ is a string, then base the prompt on that (preferring
  2238. the former), appending either \"=\" (if it appears to be a
  2239. command-line option) or \": \".
  2240. Finally fall through to using \"(BUG: no prompt): \" as the
  2241. prompt."
  2242. (if-let ((prompt (oref obj prompt)))
  2243. (let ((prompt (if (functionp prompt)
  2244. (funcall prompt obj)
  2245. prompt)))
  2246. (if (stringp prompt)
  2247. prompt
  2248. "(BUG: no prompt): "))
  2249. (or (when-let ((arg (and (slot-boundp obj 'argument) (oref obj argument))))
  2250. (if (and (stringp arg) (string-suffix-p "=" arg))
  2251. arg
  2252. (concat arg ": ")))
  2253. (when-let ((var (and (slot-boundp obj 'variable) (oref obj variable))))
  2254. (and (stringp var)
  2255. (concat var ": ")))
  2256. "(BUG: no prompt): ")))
  2257. ;;;; Set
  2258. (defvar transient--unset-incompatible t)
  2259. (cl-defgeneric transient-infix-set (obj value)
  2260. "Set the value of infix object OBJ to value.")
  2261. (cl-defmethod transient-infix-set ((obj transient-infix) value)
  2262. "Set the value of infix object OBJ to value."
  2263. (oset obj value value))
  2264. (cl-defmethod transient-infix-set :around ((obj transient-argument) value)
  2265. "Unset incompatible infix arguments."
  2266. (let ((arg (if (slot-boundp obj 'argument)
  2267. (oref obj argument)
  2268. (oref obj argument-regexp))))
  2269. (if-let ((sic (and value arg transient--unset-incompatible))
  2270. (spec (oref transient--prefix incompatible))
  2271. (incomp (remove arg (cl-find-if (lambda (elt) (member arg elt)) spec))))
  2272. (progn
  2273. (cl-call-next-method obj value)
  2274. (dolist (arg incomp)
  2275. (when-let ((obj (cl-find-if (lambda (obj)
  2276. (and (slot-boundp obj 'argument)
  2277. (equal (oref obj argument) arg)))
  2278. transient--suffixes)))
  2279. (let ((transient--unset-incompatible nil))
  2280. (transient-infix-set obj nil)))))
  2281. (cl-call-next-method obj value))))
  2282. (cl-defmethod transient-set-value ((obj transient-prefix))
  2283. (oset (oref obj prototype) value (transient-get-value))
  2284. (transient--history-push obj))
  2285. ;;;; Save
  2286. (cl-defmethod transient-save-value ((obj transient-prefix))
  2287. (let ((value (transient-get-value)))
  2288. (oset (oref obj prototype) value value)
  2289. (setf (alist-get (oref obj command) transient-values) value)
  2290. (transient-save-values))
  2291. (transient--history-push obj))
  2292. ;;;; Get
  2293. (defun transient-args (prefix)
  2294. "Return the value of the transient prefix command PREFIX.
  2295. If the current command was invoked from the transient prefix
  2296. command PREFIX, then return the active infix arguments. If
  2297. the current command was not invoked from PREFIX, then return
  2298. the set, saved or default value for PREFIX."
  2299. (delq nil (mapcar 'transient-infix-value (transient-suffixes prefix))))
  2300. (defun transient-suffixes (prefix)
  2301. "Return the suffix objects of the transient prefix command PREFIX."
  2302. (if (eq transient-current-command prefix)
  2303. transient-current-suffixes
  2304. (let ((transient--prefix (transient--init-prefix prefix)))
  2305. (transient--flatten-suffixes
  2306. (transient--init-suffixes prefix)))))
  2307. (defun transient-get-value ()
  2308. (delq nil (mapcar (lambda (obj)
  2309. (and (or (not (slot-exists-p obj 'unsavable))
  2310. (not (oref obj unsavable)))
  2311. (transient-infix-value obj)))
  2312. transient-current-suffixes)))
  2313. (cl-defgeneric transient-infix-value (obj)
  2314. "Return the value of the suffix object OBJ.
  2315. This function is called by `transient-args' (which see), meaning
  2316. this function is how the value of a transient is determined so
  2317. that the invoked suffix command can use it.
  2318. Currently most values are strings, but that is not set in stone.
  2319. Nil is not a value, it means \"no value\".
  2320. Usually only infixes have a value, but see the method for
  2321. `transient-suffix'.")
  2322. (cl-defmethod transient-infix-value ((_ transient-suffix))
  2323. "Return nil, which means \"no value\".
  2324. Infix arguments contribute the the transient's value while suffix
  2325. commands consume it. This function is called for suffixes anyway
  2326. because a command that both contributes to the transient's value
  2327. and also consumes it is not completely unconceivable.
  2328. If you define such a command, then you must define a derived
  2329. class and implement this function because this default method
  2330. does nothing." nil)
  2331. (cl-defmethod transient-infix-value ((obj transient-infix))
  2332. "Return the value of OBJ's `value' slot."
  2333. (oref obj value))
  2334. (cl-defmethod transient-infix-value ((obj transient-option))
  2335. "Return (concat ARGUMENT VALUE) or nil.
  2336. ARGUMENT and VALUE are the values of the respective slots of OBJ.
  2337. If VALUE is nil, then return nil. VALUE may be the empty string,
  2338. which is not the same as nil."
  2339. (when-let ((value (oref obj value)))
  2340. (concat (oref obj argument) value)))
  2341. (cl-defmethod transient-infix-value ((_ transient-variable))
  2342. "Return nil, which means \"no value\".
  2343. Setting the value of a variable is done by, well, setting the
  2344. value of the variable. I.e. this is a side-effect and does not
  2345. contribute to the value of the transient."
  2346. nil)
  2347. (cl-defmethod transient-infix-value ((obj transient-files))
  2348. "Return (cons ARGUMENT VALUE) or nil.
  2349. ARGUMENT and VALUE are the values of the respective slots of OBJ.
  2350. If VALUE is nil, then return nil. VALUE may be the empty string,
  2351. which is not the same as nil."
  2352. (when-let ((value (oref obj value)))
  2353. (cons (oref obj argument) value)))
  2354. ;;;; Utilities
  2355. (defun transient-arg-value (arg args)
  2356. "Return the value of ARG as it appears in ARGS.
  2357. For a switch return a boolean. For an option return the value as
  2358. a string, using the empty string for the empty value, or nil if
  2359. the option does not appear in ARGS."
  2360. (if (string-match-p "=\\'" arg)
  2361. (save-match-data
  2362. (when-let ((match (let ((re (format "\\`%s\\(?:=\\(.+\\)\\)?\\'"
  2363. (substring arg 0 -1))))
  2364. (cl-find-if (lambda (a)
  2365. (and (stringp a)
  2366. (string-match re a)))
  2367. args))))
  2368. (or (match-string 1 match) "")))
  2369. (and (member arg args) t)))
  2370. ;;; History
  2371. (cl-defgeneric transient--history-key (obj)
  2372. "Return OBJ's history key.
  2373. If the value of the `history-key' slot is non-nil, then return
  2374. that. Otherwise return the value of the `command' slot."
  2375. (or (oref obj history-key)
  2376. (oref obj command)))
  2377. (cl-defgeneric transient--history-push (obj)
  2378. "Push the current value of OBJ to its entry in `transient-history'."
  2379. (let ((key (transient--history-key obj)))
  2380. (setf (alist-get key transient-history)
  2381. (let ((args (transient-get-value)))
  2382. (cons args (delete args (alist-get key transient-history)))))))
  2383. (cl-defgeneric transient--history-init (obj)
  2384. "Initialize OBJ's `history' slot.
  2385. This is the transient-wide history; many individual infixes also
  2386. have a history of their own.")
  2387. (cl-defmethod transient--history-init ((obj transient-prefix))
  2388. "Initialize OBJ's `history' slot from the variable `transient-history'."
  2389. (let ((val (oref obj value)))
  2390. (oset obj history
  2391. (cons val (delete val (alist-get (transient--history-key obj)
  2392. transient-history))))))
  2393. ;;; Draw
  2394. (defun transient--show-brief ()
  2395. (let ((message-log-max nil))
  2396. (if (and transient-show-popup (<= transient-show-popup 0))
  2397. (message "%s-" (key-description (this-command-keys)))
  2398. (message
  2399. "%s- [%s] %s"
  2400. (key-description (this-command-keys))
  2401. (oref transient--prefix command)
  2402. (mapconcat
  2403. #'identity
  2404. (sort
  2405. (cl-mapcan
  2406. (lambda (suffix)
  2407. (let ((key (kbd (oref suffix key))))
  2408. ;; Don't list any common commands.
  2409. (and (not (memq (oref suffix command)
  2410. `(,(lookup-key transient-map key)
  2411. ,(lookup-key transient-sticky-map key)
  2412. ;; From transient-common-commands:
  2413. transient-set
  2414. transient-save
  2415. transient-history-prev
  2416. transient-history-next
  2417. transient-quit-one
  2418. transient-toggle-common
  2419. transient-set-level)))
  2420. (list (propertize (oref suffix key) 'face 'transient-key)))))
  2421. transient--suffixes)
  2422. #'string<)
  2423. (propertize "|" 'face 'transient-unreachable-key))))))
  2424. (defun transient--show ()
  2425. (transient--timer-cancel)
  2426. (setq transient--showp t)
  2427. (let ((buf (get-buffer-create transient--buffer-name))
  2428. (focus nil))
  2429. (unless (window-live-p transient--window)
  2430. (setq transient--window
  2431. (display-buffer buf transient-display-buffer-action)))
  2432. (with-selected-window transient--window
  2433. (when transient-enable-popup-navigation
  2434. (setq focus (button-get (point) 'command)))
  2435. (erase-buffer)
  2436. (set-window-hscroll transient--window 0)
  2437. (set-window-dedicated-p transient--window t)
  2438. (set-window-parameter transient--window 'no-other-window t)
  2439. (setq window-size-fixed t)
  2440. (when (bound-and-true-p tab-line-format)
  2441. (setq tab-line-format nil))
  2442. (setq mode-line-format (if (eq transient-mode-line-format 'line)
  2443. nil
  2444. transient-mode-line-format))
  2445. (setq mode-line-buffer-identification
  2446. (symbol-name (oref transient--prefix command)))
  2447. (if transient-enable-popup-navigation
  2448. (setq-local cursor-in-non-selected-windows 'box)
  2449. (setq cursor-type nil))
  2450. (setq display-line-numbers nil)
  2451. (setq show-trailing-whitespace nil)
  2452. (transient--insert-groups)
  2453. (when (or transient--helpp transient--editp)
  2454. (transient--insert-help))
  2455. (when (and (eq transient-mode-line-format 'line)
  2456. window-system)
  2457. (let ((face
  2458. (if-let ((f (and (transient--semantic-coloring-p)
  2459. (transient--prefix-color transient--prefix))))
  2460. `(,@(and (>= emacs-major-version 27) '(:extend t))
  2461. :background ,(face-foreground f))
  2462. 'transient-separator)))
  2463. (insert (propertize "__" 'face face 'display '(space :height (1))))
  2464. (insert (propertize "\n" 'face face 'line-height t))))
  2465. (let ((window-resize-pixelwise t)
  2466. (window-size-fixed nil))
  2467. (fit-window-to-buffer nil nil 1))
  2468. (goto-char (point-min))
  2469. (when transient-force-fixed-pitch
  2470. (transient--force-fixed-pitch))
  2471. (when transient-enable-popup-navigation
  2472. (transient--goto-button focus)))))
  2473. (defun transient--insert-groups ()
  2474. (let ((groups (cl-mapcan (lambda (group)
  2475. (let ((hide (oref group hide)))
  2476. (and (not (and (functionp hide)
  2477. (funcall hide)))
  2478. (list group))))
  2479. transient--layout))
  2480. group)
  2481. (while (setq group (pop groups))
  2482. (transient--insert-group group)
  2483. (when groups
  2484. (insert ?\n)))))
  2485. (cl-defgeneric transient--insert-group (group)
  2486. "Format GROUP and its elements and insert the result.")
  2487. (cl-defmethod transient--insert-group :before ((group transient-group))
  2488. "Insert GROUP's description, if any."
  2489. (when-let ((desc (transient-format-description group)))
  2490. (insert desc ?\n)))
  2491. (cl-defmethod transient--insert-group ((group transient-row))
  2492. (transient--maybe-pad-keys group)
  2493. (dolist (suffix (oref group suffixes))
  2494. (insert (transient-format suffix))
  2495. (insert " "))
  2496. (insert ?\n))
  2497. (cl-defmethod transient--insert-group ((group transient-column))
  2498. (transient--maybe-pad-keys group)
  2499. (dolist (suffix (oref group suffixes))
  2500. (let ((str (transient-format suffix)))
  2501. (insert str)
  2502. (unless (string-match-p ".\n\\'" str)
  2503. (insert ?\n)))))
  2504. (cl-defmethod transient--insert-group ((group transient-columns))
  2505. (let* ((columns
  2506. (mapcar
  2507. (lambda (column)
  2508. (transient--maybe-pad-keys column group)
  2509. (let ((rows (mapcar 'transient-format (oref column suffixes))))
  2510. (when-let ((desc (transient-format-description column)))
  2511. (push desc rows))
  2512. rows))
  2513. (oref group suffixes)))
  2514. (rs (apply #'max (mapcar #'length columns)))
  2515. (cs (length columns))
  2516. (cw (mapcar (lambda (col) (apply #'max (mapcar #'length col)))
  2517. columns))
  2518. (cc (transient--seq-reductions-from (apply-partially #'+ 3) cw 0)))
  2519. (dotimes (r rs)
  2520. (dotimes (c cs)
  2521. (insert (make-string (- (nth c cc) (current-column)) ?\s))
  2522. (when-let ((cell (nth r (nth c columns))))
  2523. (insert cell))
  2524. (when (= c (1- cs))
  2525. (insert ?\n))))))
  2526. (cl-defmethod transient--insert-group ((group transient-subgroups))
  2527. (let* ((subgroups (oref group suffixes))
  2528. (n (length subgroups)))
  2529. (dotimes (s n)
  2530. (let ((subgroup (nth s subgroups)))
  2531. (transient--maybe-pad-keys subgroup group)
  2532. (transient--insert-group subgroup)
  2533. (when (< s (1- n))
  2534. (insert ?\n))))))
  2535. (cl-defgeneric transient-format (obj)
  2536. "Format and return OBJ for display.
  2537. When this function is called, then the current buffer is some
  2538. temporary buffer. If you need the buffer from which the prefix
  2539. command was invoked to be current, then do so by temporarily
  2540. making `transient--original-buffer' current.")
  2541. (cl-defmethod transient-format ((arg string))
  2542. "Return the string ARG after applying the `transient-heading' face."
  2543. (propertize arg 'face 'transient-heading))
  2544. (cl-defmethod transient-format ((_ null))
  2545. "Return a string containing just the newline character."
  2546. "\n")
  2547. (cl-defmethod transient-format ((arg integer))
  2548. "Return a string containing just the ARG character."
  2549. (char-to-string arg))
  2550. (cl-defmethod transient-format :around ((obj transient-infix))
  2551. "When reading user input for this infix, then highlight it."
  2552. (let ((str (cl-call-next-method obj)))
  2553. (when (eq obj transient--active-infix)
  2554. (setq str (concat str "\n"))
  2555. (add-face-text-property
  2556. (if (eq this-command 'transient-set-level) 3 0)
  2557. (length str)
  2558. 'transient-active-infix nil str))
  2559. str))
  2560. (cl-defmethod transient-format :around ((obj transient-suffix))
  2561. "When edit-mode is enabled, then prepend the level information.
  2562. Optional support for popup buttons is also implemented here."
  2563. (let ((str (concat
  2564. (and transient--editp
  2565. (let ((level (oref obj level)))
  2566. (propertize (format " %s " level)
  2567. 'face (if (transient--use-level-p level t)
  2568. 'transient-enabled-suffix
  2569. 'transient-disabled-suffix))))
  2570. (cl-call-next-method obj))))
  2571. (when (oref obj inapt)
  2572. (set-text-properties 0 (length str)
  2573. (list 'face 'transient-inapt-suffix)
  2574. str))
  2575. (if transient-enable-popup-navigation
  2576. (make-text-button str nil
  2577. 'type 'transient-button
  2578. 'command (transient--suffix-command obj))
  2579. str)))
  2580. (cl-defmethod transient-format ((obj transient-infix))
  2581. "Return a string generated using OBJ's `format'.
  2582. %k is formatted using `transient-format-key'.
  2583. %d is formatted using `transient-format-description'.
  2584. %v is formatted using `transient-format-value'."
  2585. (format-spec (oref obj format)
  2586. `((?k . ,(transient-format-key obj))
  2587. (?d . ,(transient-format-description obj))
  2588. (?v . ,(transient-format-value obj)))))
  2589. (cl-defmethod transient-format ((obj transient-suffix))
  2590. "Return a string generated using OBJ's `format'.
  2591. %k is formatted using `transient-format-key'.
  2592. %d is formatted using `transient-format-description'."
  2593. (format-spec (oref obj format)
  2594. `((?k . ,(transient-format-key obj))
  2595. (?d . ,(transient-format-description obj)))))
  2596. (cl-defgeneric transient-format-key (obj)
  2597. "Format OBJ's `key' for display and return the result.")
  2598. (cl-defmethod transient-format-key ((obj transient-suffix))
  2599. "Format OBJ's `key' for display and return the result."
  2600. (let ((key (oref obj key))
  2601. (cmd (oref obj command)))
  2602. (if transient--redisplay-key
  2603. (let ((len (length transient--redisplay-key))
  2604. (seq (cl-coerce (edmacro-parse-keys key t) 'list)))
  2605. (cond
  2606. ((equal (seq-take seq len) transient--redisplay-key)
  2607. (let ((pre (key-description (vconcat (seq-take seq len))))
  2608. (suf (key-description (vconcat (seq-drop seq len)))))
  2609. (setq pre (replace-regexp-in-string "RET" "C-m" pre t))
  2610. (setq pre (replace-regexp-in-string "TAB" "C-i" pre t))
  2611. (setq suf (replace-regexp-in-string "RET" "C-m" suf t))
  2612. (setq suf (replace-regexp-in-string "TAB" "C-i" suf t))
  2613. ;; We use e.g. "-k" instead of the more correct "- k",
  2614. ;; because the former is prettier. If we did that in
  2615. ;; the definition, then we want to drop the space that
  2616. ;; is reinserted above. False-positives are possible
  2617. ;; for silly bindings like "-C-c C-c".
  2618. (unless (string-match-p " " key)
  2619. (setq pre (replace-regexp-in-string " " "" pre))
  2620. (setq suf (replace-regexp-in-string " " "" suf)))
  2621. (concat (propertize pre 'face 'default)
  2622. (and (string-prefix-p (concat pre " ") key) " ")
  2623. (transient--colorize-key suf cmd)
  2624. (save-excursion
  2625. (when (string-match " +\\'" key)
  2626. (match-string 0 key))))))
  2627. ((transient--lookup-key transient-sticky-map (kbd key))
  2628. (transient--colorize-key key cmd))
  2629. (t
  2630. (propertize key 'face 'transient-unreachable-key))))
  2631. (transient--colorize-key key cmd))))
  2632. (defun transient--colorize-key (key command)
  2633. (propertize key 'face
  2634. (or (and (transient--semantic-coloring-p)
  2635. (transient--suffix-color command))
  2636. 'transient-key)))
  2637. (cl-defmethod transient-format-key :around ((obj transient-argument))
  2638. (let ((key (cl-call-next-method obj)))
  2639. (cond ((not transient-highlight-mismatched-keys))
  2640. ((not (slot-boundp obj 'shortarg))
  2641. (add-face-text-property
  2642. 0 (length key) 'transient-nonstandard-key nil key))
  2643. ((not (string-equal key (oref obj shortarg)))
  2644. (add-face-text-property
  2645. 0 (length key) 'transient-mismatched-key nil key)))
  2646. key))
  2647. (cl-defgeneric transient-format-description (obj)
  2648. "Format OBJ's `description' for display and return the result.")
  2649. (cl-defmethod transient-format-description ((obj transient-child))
  2650. "The `description' slot may be a function, in which case that is
  2651. called inside the correct buffer (see `transient-insert-group')
  2652. and its value is returned to the caller."
  2653. (when-let ((desc (oref obj description)))
  2654. (if (functionp desc)
  2655. (with-current-buffer transient--original-buffer
  2656. (funcall desc))
  2657. desc)))
  2658. (cl-defmethod transient-format-description ((obj transient-group))
  2659. "Format the description by calling the next method. If the result
  2660. doesn't use the `face' property at all, then apply the face
  2661. `transient-heading' to the complete string."
  2662. (when-let ((desc (cl-call-next-method obj)))
  2663. (if (text-property-not-all 0 (length desc) 'face nil desc)
  2664. desc
  2665. (propertize desc 'face 'transient-heading))))
  2666. (cl-defmethod transient-format-description :around ((obj transient-suffix))
  2667. "Format the description by calling the next method. If the result
  2668. is nil, then use \"(BUG: no description)\" as the description.
  2669. If the OBJ's `key' is currently unreachable, then apply the face
  2670. `transient-unreachable' to the complete string."
  2671. (let ((desc (or (cl-call-next-method obj)
  2672. (and (slot-boundp transient--prefix 'suffix-description)
  2673. (funcall (oref transient--prefix suffix-description)
  2674. obj))
  2675. (propertize "(BUG: no description)" 'face 'error))))
  2676. (if (transient--key-unreachable-p obj)
  2677. (propertize desc 'face 'transient-unreachable)
  2678. desc)))
  2679. (cl-defgeneric transient-format-value (obj)
  2680. "Format OBJ's value for display and return the result.")
  2681. (cl-defmethod transient-format-value ((obj transient-suffix))
  2682. (propertize (oref obj argument)
  2683. 'face (if (oref obj value)
  2684. 'transient-argument
  2685. 'transient-inactive-argument)))
  2686. (cl-defmethod transient-format-value ((obj transient-option))
  2687. (let ((value (oref obj value)))
  2688. (propertize (concat (oref obj argument)
  2689. (if (listp value)
  2690. (mapconcat #'identity value ",")
  2691. value))
  2692. 'face (if value
  2693. 'transient-value
  2694. 'transient-inactive-value))))
  2695. (cl-defmethod transient-format-value ((obj transient-switches))
  2696. (with-slots (value argument-format choices) obj
  2697. (format (propertize argument-format
  2698. 'face (if value
  2699. 'transient-value
  2700. 'transient-inactive-value))
  2701. (concat
  2702. (propertize "[" 'face 'transient-inactive-value)
  2703. (mapconcat
  2704. (lambda (choice)
  2705. (propertize choice 'face
  2706. (if (equal (format argument-format choice) value)
  2707. 'transient-value
  2708. 'transient-inactive-value)))
  2709. choices
  2710. (propertize "|" 'face 'transient-inactive-value))
  2711. (propertize "]" 'face 'transient-inactive-value)))))
  2712. (cl-defmethod transient-format-value ((obj transient-files))
  2713. (let ((argument (oref obj argument)))
  2714. (if-let ((value (oref obj value)))
  2715. (propertize (concat argument " "
  2716. (mapconcat (lambda (f) (format "%S" f))
  2717. (oref obj value) " "))
  2718. 'face 'transient-argument)
  2719. (propertize argument 'face 'transient-inactive-argument))))
  2720. (defun transient--key-unreachable-p (obj)
  2721. (and transient--redisplay-key
  2722. (let ((key (oref obj key)))
  2723. (not (or (equal (seq-take (cl-coerce (edmacro-parse-keys key t) 'list)
  2724. (length transient--redisplay-key))
  2725. transient--redisplay-key)
  2726. (transient--lookup-key transient-sticky-map (kbd key)))))))
  2727. (defun transient--lookup-key (keymap key)
  2728. (let ((val (lookup-key keymap key)))
  2729. (and val (not (integerp val)) val)))
  2730. (defun transient--maybe-pad-keys (group &optional parent)
  2731. (when-let ((pad (if (slot-boundp group 'pad-keys)
  2732. (oref group pad-keys)
  2733. (and parent
  2734. (slot-boundp parent 'pad-keys)
  2735. (oref parent pad-keys)))))
  2736. (let ((width (apply #'max
  2737. (cons (if (integerp pad) pad 0)
  2738. (mapcar (lambda (suffix)
  2739. (length (oref suffix key)))
  2740. (oref group suffixes))))))
  2741. (dolist (suffix (oref group suffixes))
  2742. (oset suffix key
  2743. (truncate-string-to-width (oref suffix key) width nil ?\s))))))
  2744. (defun transient-command-summary-or-name (obj)
  2745. "Return the summary or name of the command represented by OBJ.
  2746. If the command has a doc-string, then return the first line of
  2747. that, else its name.
  2748. Intended to be temporarily used as the `:suffix-description' of
  2749. a prefix command, while porting a regular keymap to a transient."
  2750. (let ((command (transient--suffix-symbol (oref obj command))))
  2751. (if-let ((doc (documentation command)))
  2752. (propertize (car (split-string doc "\n")) 'face 'font-lock-doc-face)
  2753. (propertize (symbol-name command) 'face 'font-lock-function-name-face))))
  2754. ;;; Help
  2755. (cl-defgeneric transient-show-help (obj)
  2756. "Show help for OBJ's command.")
  2757. (cl-defmethod transient-show-help ((obj transient-prefix))
  2758. "Show the info manual, manpage or command doc-string.
  2759. Show the first one that is specified."
  2760. (if-let ((manual (oref obj info-manual)))
  2761. (info manual)
  2762. (if-let ((manpage (oref obj man-page)))
  2763. (transient--show-manpage manpage)
  2764. (transient--describe-function (oref obj command)))))
  2765. (cl-defmethod transient-show-help ((_ transient-suffix))
  2766. "Show the command doc-string."
  2767. (if (eq this-original-command 'transient-help)
  2768. (if-let ((manpage (oref transient--prefix man-page)))
  2769. (transient--show-manpage manpage)
  2770. (transient--describe-function (oref transient--prefix command)))
  2771. (transient--describe-function this-original-command)))
  2772. (cl-defmethod transient-show-help ((obj transient-infix))
  2773. "Show the manpage if defined or the command doc-string.
  2774. If the manpage is specified, then try to jump to the correct
  2775. location."
  2776. (if-let ((manpage (oref transient--prefix man-page)))
  2777. (transient--show-manpage manpage (ignore-errors (oref obj argument)))
  2778. (transient--describe-function this-original-command)))
  2779. ;; `cl-generic-generalizers' doesn't support `command' et al.
  2780. (cl-defmethod transient-show-help (cmd)
  2781. "Show the command doc-string."
  2782. (transient--describe-function cmd))
  2783. (defun transient--show-manpage (manpage &optional argument)
  2784. (require 'man)
  2785. (let* ((Man-notify-method 'meek)
  2786. (buf (Man-getpage-in-background manpage))
  2787. (proc (get-buffer-process buf)))
  2788. (while (and proc (eq (process-status proc) 'run))
  2789. (accept-process-output proc))
  2790. (switch-to-buffer buf)
  2791. (when argument
  2792. (transient--goto-argument-description argument))))
  2793. (defun transient--describe-function (fn)
  2794. (describe-function fn)
  2795. (select-window (get-buffer-window (help-buffer))))
  2796. (defun transient--goto-argument-description (arg)
  2797. (goto-char (point-min))
  2798. (let ((case-fold-search nil)
  2799. ;; This matches preceding/proceeding options. Options
  2800. ;; such as "-a", "-S[<keyid>]", and "--grep=<pattern>"
  2801. ;; are matched by this regex without the shy group.
  2802. ;; The ". " in the shy group is for options such as
  2803. ;; "-m parent-number", and the "-[^[:space:]]+ " is
  2804. ;; for options such as "--mainline parent-number"
  2805. (others "-\\(?:. \\|-[^[:space:]]+ \\)?[^[:space:]]+"))
  2806. (when (re-search-forward
  2807. (if (equal arg "--")
  2808. ;; Special case.
  2809. "^[\t\s]+\\(--\\(?: \\|$\\)\\|\\[--\\]\\)"
  2810. ;; Should start with whitespace and may have
  2811. ;; any number of options before and/or after.
  2812. (format
  2813. "^[\t\s]+\\(?:%s, \\)*?\\(?1:%s\\)%s\\(?:, %s\\)*$"
  2814. others
  2815. ;; Options don't necessarily end in an "="
  2816. ;; (e.g., "--gpg-sign[=<keyid>]")
  2817. (string-remove-suffix "=" arg)
  2818. ;; Simple options don't end in an "=". Splitting this
  2819. ;; into 2 cases should make getting false positives
  2820. ;; less likely.
  2821. (if (string-suffix-p "=" arg)
  2822. ;; "[^[:space:]]*[^.[:space:]]" matches the option
  2823. ;; value, which is usually after the option name
  2824. ;; and either '=' or '[='. The value can't end in
  2825. ;; a period, as that means it's being used at the
  2826. ;; end of a sentence. The space is for options
  2827. ;; such as '--mainline parent-number'.
  2828. "\\(?: \\|\\[?=\\)[^[:space:]]*[^.[:space:]]"
  2829. ;; Either this doesn't match anything (e.g., "-a"),
  2830. ;; or the option is followed by a value delimited
  2831. ;; by a "[", "<", or ":". A space might appear
  2832. ;; before this value, as in "-f <file>". The
  2833. ;; space alternative is for options such as
  2834. ;; "-m parent-number".
  2835. "\\(?:\\(?: \\| ?[\\[<:]\\)[^[:space:]]*[^.[:space:]]\\)?")
  2836. others))
  2837. nil t)
  2838. (goto-char (match-beginning 1)))))
  2839. (defun transient--insert-help ()
  2840. (unless (looking-back "\n\n" 2)
  2841. (insert "\n"))
  2842. (when transient--helpp
  2843. (insert
  2844. (format (propertize "\
  2845. Type a %s to show help for that suffix command, or %s to show manual.
  2846. Type %s to exit help.\n"
  2847. 'face 'transient-heading)
  2848. (propertize "<KEY>" 'face 'transient-key)
  2849. (propertize "?" 'face 'transient-key)
  2850. (propertize "C-g" 'face 'transient-key))))
  2851. (when transient--editp
  2852. (unless transient--helpp
  2853. (insert
  2854. (format (propertize "\
  2855. Type a %s to set level for that suffix command.
  2856. Type %s to set what levels are available for this prefix command.\n"
  2857. 'face 'transient-heading)
  2858. (propertize "<KEY>" 'face 'transient-key)
  2859. (propertize "C-x l" 'face 'transient-key))))
  2860. (with-slots (level) transient--prefix
  2861. (insert
  2862. (format (propertize "
  2863. Suffixes on levels %s are available.
  2864. Suffixes on levels %s and %s are unavailable.\n"
  2865. 'face 'transient-heading)
  2866. (propertize (format "1-%s" level)
  2867. 'face 'transient-enabled-suffix)
  2868. (propertize " 0 "
  2869. 'face 'transient-disabled-suffix)
  2870. (propertize (format ">=%s" (1+ level))
  2871. 'face 'transient-disabled-suffix))))))
  2872. (defvar transient-resume-mode-map
  2873. (let ((map (make-sparse-keymap)))
  2874. (define-key map [remap Man-quit] 'transient-resume)
  2875. (define-key map [remap Info-exit] 'transient-resume)
  2876. (define-key map [remap quit-window] 'transient-resume)
  2877. map)
  2878. "Keymap for `transient-resume-mode'.
  2879. This keymap remaps every command that would usually just quit the
  2880. documentation buffer to `transient-resume', which additionally
  2881. resumes the suspended transient.")
  2882. (define-minor-mode transient-resume-mode
  2883. "Auxiliary minor-mode used to resume a transient after viewing help.")
  2884. ;;; Compatibility
  2885. ;;;; Popup Navigation
  2886. (defun transient-popup-navigation-help ()
  2887. "Inform the user how to enable popup navigation commands."
  2888. (interactive)
  2889. (message "This command is only available if `%s' is non-nil"
  2890. 'transient-enable-popup-navigation))
  2891. (define-button-type 'transient-button
  2892. 'face nil
  2893. 'action (lambda (button)
  2894. (let ((command (button-get button 'command)))
  2895. ;; Yes, I know that this is wrong(tm).
  2896. ;; Unfortunately it is also necessary.
  2897. (setq this-original-command command)
  2898. (call-interactively command))))
  2899. (defvar transient-popup-navigation-map
  2900. (let ((map (make-sparse-keymap)))
  2901. (define-key map (kbd "<down-mouse-1>") 'transient-noop)
  2902. (define-key map (kbd "<mouse-1>") 'transient-mouse-push-button)
  2903. (define-key map (kbd "RET") 'transient-push-button)
  2904. (define-key map (kbd "<up>") 'transient-backward-button)
  2905. (define-key map (kbd "C-p") 'transient-backward-button)
  2906. (define-key map (kbd "<down>") 'transient-forward-button)
  2907. (define-key map (kbd "C-n") 'transient-forward-button)
  2908. (define-key map (kbd "C-r") 'transient-isearch-backward)
  2909. (define-key map (kbd "C-s") 'transient-isearch-forward)
  2910. map))
  2911. (defun transient-mouse-push-button (&optional pos)
  2912. "Invoke the suffix the user clicks on."
  2913. (interactive (list last-command-event))
  2914. (push-button pos))
  2915. (defun transient-push-button ()
  2916. "Invoke the selected suffix command."
  2917. (interactive)
  2918. (with-selected-window transient--window
  2919. (push-button)))
  2920. (defun transient-backward-button (n)
  2921. "Move to the previous button in the transient popup buffer.
  2922. See `backward-button' for information about N."
  2923. (interactive "p")
  2924. (with-selected-window transient--window
  2925. (backward-button n t)))
  2926. (defun transient-forward-button (n)
  2927. "Move to the next button in the transient popup buffer.
  2928. See `forward-button' for information about N."
  2929. (interactive "p")
  2930. (with-selected-window transient--window
  2931. (forward-button n t)))
  2932. (defun transient--goto-button (command)
  2933. (if (not command)
  2934. (forward-button 1)
  2935. (while (and (ignore-errors (forward-button 1))
  2936. (not (eq (button-get (button-at (point)) 'command) command))))
  2937. (unless (eq (button-get (button-at (point)) 'command) command)
  2938. (goto-char (point-min))
  2939. (forward-button 1))))
  2940. ;;;; Popup Isearch
  2941. (defvar transient--isearch-mode-map
  2942. (let ((map (make-sparse-keymap)))
  2943. (set-keymap-parent map isearch-mode-map)
  2944. (define-key map [remap isearch-exit] 'transient-isearch-exit)
  2945. (define-key map [remap isearch-cancel] 'transient-isearch-cancel)
  2946. (define-key map [remap isearch-abort] 'transient-isearch-abort)
  2947. map))
  2948. (defun transient-isearch-backward (&optional regexp-p)
  2949. "Do incremental search backward.
  2950. With a prefix argument, do an incremental regular expression
  2951. search instead."
  2952. (interactive "P")
  2953. (transient--isearch-setup)
  2954. (let ((isearch-mode-map transient--isearch-mode-map))
  2955. (isearch-mode nil regexp-p)))
  2956. (defun transient-isearch-forward (&optional regexp-p)
  2957. "Do incremental search forward.
  2958. With a prefix argument, do an incremental regular expression
  2959. search instead."
  2960. (interactive "P")
  2961. (transient--isearch-setup)
  2962. (let ((isearch-mode-map transient--isearch-mode-map))
  2963. (isearch-mode t regexp-p)))
  2964. (defun transient-isearch-exit ()
  2965. "Like `isearch-exit' but adapted for `transient'."
  2966. (interactive)
  2967. (isearch-exit)
  2968. (transient--isearch-exit))
  2969. (defun transient-isearch-cancel ()
  2970. "Like `isearch-cancel' but adapted for `transient'."
  2971. (interactive)
  2972. (condition-case nil (isearch-cancel) (quit))
  2973. (transient--isearch-exit))
  2974. (defun transient-isearch-abort ()
  2975. "Like `isearch-abort' but adapted for `transient'."
  2976. (interactive)
  2977. (condition-case nil (isearch-abort) (quit))
  2978. (transient--isearch-exit))
  2979. (defun transient--isearch-setup ()
  2980. (select-window transient--window)
  2981. (transient--suspend-override))
  2982. (defun transient--isearch-exit ()
  2983. (select-window transient--original-window)
  2984. (transient--resume-override))
  2985. ;;;; Hydra Color Emulation
  2986. (defun transient--semantic-coloring-p ()
  2987. (and transient-semantic-coloring
  2988. (not transient--helpp)
  2989. (not transient--editp)))
  2990. (defun transient--suffix-color (command)
  2991. (or (get command 'transient-color)
  2992. (get (transient--get-predicate-for command) 'transient-color)))
  2993. (defun transient--prefix-color (command)
  2994. (let* ((nonsuf (or (oref command transient-non-suffix)
  2995. 'transient--do-warn))
  2996. (nonsuf (if (memq nonsuf '(transient--do-noop transient--do-warn))
  2997. 'disallow
  2998. (get nonsuf 'transient-color)))
  2999. (suffix (if-let ((pred (oref command transient-suffix)))
  3000. (get pred 'transient-color)
  3001. (if (eq nonsuf 'transient-red)
  3002. 'transient-red
  3003. 'transient-blue))))
  3004. (pcase (list suffix nonsuf)
  3005. (`(transient-red disallow) 'transient-amaranth)
  3006. (`(transient-blue disallow) 'transient-teal)
  3007. (`(transient-red transient-red) 'transient-pink)
  3008. (`(transient-red transient-blue) 'transient-red)
  3009. (`(transient-blue transient-blue) 'transient-blue))))
  3010. ;;;; Edebug
  3011. (defun transient--edebug--recursive-edit (fn arg-mode)
  3012. (transient--debug 'edebug--recursive-edit)
  3013. (if (not transient--prefix)
  3014. (funcall fn arg-mode)
  3015. (transient--suspend-override t)
  3016. (funcall fn arg-mode)
  3017. (transient--resume-override t)))
  3018. (advice-add 'edebug--recursive-edit :around 'transient--edebug--recursive-edit)
  3019. (defun transient--abort-edebug ()
  3020. (when (bound-and-true-p edebug-active)
  3021. (transient--emergency-exit)))
  3022. (advice-add 'abort-recursive-edit :before 'transient--abort-edebug)
  3023. (advice-add 'top-level :before 'transient--abort-edebug)
  3024. (defun transient--edebug-command-p ()
  3025. (and (bound-and-true-p edebug-active)
  3026. (or (memq this-command '(top-level abort-recursive-edit))
  3027. (string-prefix-p "edebug" (symbol-name this-command)))))
  3028. ;;;; Miscellaneous
  3029. (declare-function which-key-mode "which-key" (&optional arg))
  3030. (defun transient--suspend-which-key-mode ()
  3031. (when (bound-and-true-p which-key-mode)
  3032. (which-key-mode -1)
  3033. (add-hook 'transient-exit-hook 'transient--resume-which-key-mode)))
  3034. (defun transient--resume-which-key-mode ()
  3035. (unless transient--prefix
  3036. (which-key-mode 1)
  3037. (remove-hook 'transient-exit-hook 'transient--resume-which-key-mode)))
  3038. (defun transient-bind-q-to-quit ()
  3039. "Modify some keymaps to bind \"q\" to the appropriate quit command.
  3040. \"C-g\" is the default binding for such commands now, but Transient's
  3041. predecessor Magit-Popup used \"q\" instead. If you would like to get
  3042. that binding back, then call this function in your init file like so:
  3043. (with-eval-after-load \\='transient
  3044. (transient-bind-q-to-quit))
  3045. Individual transients may already bind \"q\" to something else
  3046. and such a binding would shadow the quit binding. If that is the
  3047. case then \"Q\" is bound to whatever \"q\" would have been bound
  3048. to by setting `transient-substitute-key-function' to a function
  3049. that does that. Of course \"Q\" may already be bound to something
  3050. else, so that function binds \"M-q\" to that command instead.
  3051. Of course \"M-q\" may already be bound to something else, but
  3052. we stop there."
  3053. (define-key transient-base-map "q" 'transient-quit-one)
  3054. (define-key transient-sticky-map "q" 'transient-quit-seq)
  3055. (setq transient-substitute-key-function
  3056. 'transient-rebind-quit-commands))
  3057. (defun transient-rebind-quit-commands (obj)
  3058. "See `transient-bind-q-to-quit'."
  3059. (let ((key (oref obj key)))
  3060. (cond ((string-equal key "q") "Q")
  3061. ((string-equal key "Q") "M-q")
  3062. (t key))))
  3063. (defun transient--force-fixed-pitch ()
  3064. (require 'face-remap)
  3065. (face-remap-reset-base 'default)
  3066. (face-remap-add-relative 'default 'fixed-pitch))
  3067. ;;;; Missing from Emacs
  3068. (defun transient--seq-reductions-from (function sequence initial-value)
  3069. (let ((acc (list initial-value)))
  3070. (seq-doseq (elt sequence)
  3071. (push (funcall function (car acc) elt) acc))
  3072. (nreverse acc)))
  3073. (defun transient-plist-to-alist (plist)
  3074. (let (alist)
  3075. (while plist
  3076. (push (cons (let* ((symbol (pop plist))
  3077. (name (symbol-name symbol)))
  3078. (if (eq (aref name 0) ?:)
  3079. (intern (substring name 1))
  3080. symbol))
  3081. (pop plist))
  3082. alist))
  3083. (nreverse alist)))
  3084. ;;; Font-Lock
  3085. (defconst transient-font-lock-keywords
  3086. (eval-when-compile
  3087. `((,(concat "("
  3088. (regexp-opt (list "transient-define-prefix"
  3089. "transient-define-infix"
  3090. "transient-define-argument"
  3091. "transient-define-suffix")
  3092. t)
  3093. "\\_>[ \t'\(]*"
  3094. "\\(\\(?:\\sw\\|\\s_\\)+\\)?")
  3095. (1 'font-lock-keyword-face)
  3096. (2 'font-lock-function-name-face nil t)))))
  3097. (font-lock-add-keywords 'emacs-lisp-mode transient-font-lock-keywords)
  3098. ;;; Auxiliary Classes
  3099. ;;;; `transient-lisp-variable'
  3100. (defclass transient-lisp-variable (transient-variable)
  3101. ((reader :initform transient-lisp-variable--reader)
  3102. (always-read :initform t)
  3103. (set-value :initarg :set-value :initform set))
  3104. "[Experimental] Class used for Lisp variables.")
  3105. (cl-defmethod transient-init-value ((obj transient-lisp-variable))
  3106. (oset obj value (symbol-value (oref obj variable))))
  3107. (cl-defmethod transient-infix-set ((obj transient-lisp-variable) value)
  3108. (funcall (oref obj set-value)
  3109. (oref obj variable)
  3110. (oset obj value value)))
  3111. (cl-defmethod transient-format-description ((obj transient-lisp-variable))
  3112. (or (oref obj description)
  3113. (symbol-name (oref obj variable))))
  3114. (cl-defmethod transient-format-value ((obj transient-lisp-variable))
  3115. (propertize (prin1-to-string (oref obj value))
  3116. 'face 'transient-value))
  3117. (cl-defmethod transient-prompt ((obj transient-lisp-variable))
  3118. (format "Set %s: " (oref obj variable)))
  3119. (defun transient-lisp-variable--reader (prompt initial-input _history)
  3120. (read--expression prompt initial-input))
  3121. ;;; _
  3122. (provide 'transient)
  3123. ;; Local Variables:
  3124. ;; indent-tabs-mode: nil
  3125. ;; End:
  3126. ;;; transient.el ends here