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.

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