Skip to content

Commit c3a5b77

Browse files
transient: add configurable delay before showing popup (#2187)
* transient: add configurable delay before showing popup Add a configurable delay before the transient popup appears, similar to Doom Emacs's which-key-idle-delay. The popup waits 500ms (default) before showing; if the user completes the key sequence before the timer fires, the popup never appears — avoiding flicker for fast typists. Once the popup is already visible (navigating sub-menus), subsequent keymaps show immediately. New exported variable: lem/transient:*transient-popup-delay* - 500 (default): 500ms delay - 0 or nil: show immediately * transient: document delay variables and rationale for global timer Address code-contractor feedback: - Expand *transient-popup-delay* docstring to describe user intent. - Document why *transient-delay-timer* is held as global state: it mirrors the lifecycle of *transient-popup-window* and must be reachable from hide-transient and keymap-activate so a pending popup can be canceled regardless of which call site aborts the transient sequence — the contract's well-documented exception to the functional-style rule.
1 parent 94f40b8 commit c3a5b77

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

extensions/transient/keymap.lisp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,32 @@
1414
(*transient-always-show*
1515
(or keymap *root-keymap*)))))
1616

17+
(defun show-transient-with-delay (keymap)
18+
"show the transient popup for KEYMAP, either immediately or after a delay.
19+
if the popup is already visible (e.g. navigating sub-menus), show immediately.
20+
otherwise, wait *transient-popup-delay* milliseconds before showing."
21+
(cancel-transient-delay-timer)
22+
(let ((delay *transient-popup-delay*))
23+
(if (or (null delay)
24+
(<= delay 0)
25+
(transient-window-alive-p))
26+
;; show immediately: no delay configured, or popup already visible (sub-menu navigation)
27+
(show-transient keymap)
28+
;; delayed: start a one-shot timer
29+
(let ((target-keymap keymap))
30+
(setf *transient-delay-timer*
31+
(start-timer
32+
(make-timer (lambda ()
33+
(let ((resolved (resolve-transient-keymap target-keymap)))
34+
(when resolved
35+
(show-transient resolved))))
36+
:name "transient-popup-delay")
37+
delay))))))
38+
1739
(defmethod keymap-activate ((keymap keymap))
1840
(let ((resolved (resolve-transient-keymap keymap)))
1941
(if resolved
20-
(show-transient resolved)
42+
(show-transient-with-delay resolved)
2143
(hide-transient))))
2244

2345
(defgeneric mode-transient-keymap (mode)

extensions/transient/popup.lisp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@
2727
nil
2828
"whether to always show the transient buffer. by default only keymaps that have show-p set are shown.")
2929

30+
(defvar *transient-popup-delay*
31+
500
32+
"User-configurable delay in milliseconds before the transient popup appears.
33+
Set to 0 or nil to show immediately (no delay). Default is 500ms.
34+
When the user completes a key sequence faster than this delay, the
35+
popup never appears, avoiding flicker for fast typists.")
36+
37+
(defvar *transient-delay-timer*
38+
nil
39+
"Internal timer scheduling the delayed popup display, or nil when idle.
40+
Held as global state to mirror the lifecycle of `*transient-popup-window*':
41+
both must be reachable from `hide-transient' and from any subsequent
42+
`keymap-activate' so the pending popup can be canceled regardless of
43+
which call site aborts the transient sequence.")
44+
3045
(define-attribute transient-matched-key-attribute
3146
(t
3247
:foreground (attribute-foreground (ensure-attribute 'syntax-string-attribute))))
@@ -513,8 +528,16 @@ prefixes marked as :intermediate-p are flattened and shown with concatenated key
513528
(max 0 (- current *transient-horizontal-scroll-amount*))))
514529
(redraw-display)))
515530

531+
(defun cancel-transient-delay-timer ()
532+
"cancel any pending delayed popup timer."
533+
(when (and *transient-delay-timer*
534+
(not (timer-expired-p *transient-delay-timer*)))
535+
(stop-timer *transient-delay-timer*))
536+
(setf *transient-delay-timer* nil))
537+
516538
(defun hide-transient ()
517539
"hide (delete) the transient window."
540+
(cancel-transient-delay-timer)
518541
(when (and *transient-popup-window*
519542
(not (deleted-window-p *transient-popup-window*)))
520543
(modeline-remove-status-list 'transient-scroll-status)

extensions/transient/transient.lisp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(:use :cl :lem)
33
(:export
44
:*transient-always-show*
5+
:*transient-popup-delay*
56
:*transient-mode-keymap*
67
:define-transient
78
:define-prefix

0 commit comments

Comments
 (0)