Skip to content

ventruvian/vertico-timer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
 
 
 
 
 
 
 
 

Repository files navigation

vertico-timer

In a vertico-session type:

  • 9 to select the ninth candidate (w/o confirming RET)
  • 1 1 to select the 11th
  • 8 i to insert the 8th candidate into the minibuffer
  • 1 2 a to call embark-act on the 12th candidate
  • M-i regular digit behaviour for the completion-session

Example Recordings:

Pay attention to the keys registered in the keycast log buffer on the left. Recordings are made with ambrevar’s gif-screencast.

  • Selecting the 12th candidate in a find-file completion. Keys: 1 2, note that no RET is pressed for confirmation (the recorded command with no key-event is the timer executing that action).

screencasts/vertico-timer-demo-basic.gif

  • Invoking embark-act on the 12th variable discovered by helpful-variable. Keys: 1 2 a.

screencasts/vertico-timer-demo-act.gif

Introduction

vertico-timer is a vertico extension that builds on vertico-indexed and around the same basic idea of selecting candidates with digit keys. The fastest way to vertico is usually to narrow the list to one candidate and press RET, however you need to know enough of a candidate’s name (and be a quick typer). This package aims to speed up the process for those times that you have to look at the list.

While vertico-indexed-mode allows you to select candidates by their indices using prefix arguments, this package makes it even faster by allowing direct selection with digit keys. So instead of pressing C-u 9 RET to select the ninth candidate you simply hit 9. Instead of C-u 1 1 RET you type 1 1.

Beware: This introduces a highly opinionated tradeoff. What if you actually want to input numbers into the minibuffer? While you could always quote-insert them with C-q, if you actually do find yourself filtering by numbers they usually come in a bunch, (i.e. filtering denotes).

So the proposed tradeoff by this package is a per-session toggle. This effectively adds a “key-prefix” (the toggle) to the rare case (inputting numbers) and removes it from the common case (selecting candidates). You may find you barely ever need to input numbers into the minibuffer, but this depends on personal usage-patterns.

This package concerns itself with more bells and whistles but really this covers the practical use-case. If you think this functionality can benefit your usage and you want to shave off time from completing candidates, I recommend to jump over to the minimal configuration after installation and get a feel for the basic mechanics before looking at anything fancy.

How it works

vertico-timer still uses prefix-arg in the background to allow selecting multi-digit candidates (hitting 1 2 will select the 12th candidate). It will pass that baton to vertico-indexed.

After receiving the first digit key a timer starts that executes the default action on the candidate whose index corresponds to the value of prefix-arg. This gives you time to append a second digit to prefix-arg.

The timer can be instructed to reset itself with each keypress this is helpful if you define single keys to change the action performed on a target.

Requirements

  • Emacs 29.1 or later
  • vertico
  • vertico-indexed (included in vertico)

Installation

The package is available at https://github.com/ventruvian/vertico-timer.

Doom Emacs

Add to your packages.el:

(package! vertico-timer :recipe (:host github :repo "ventruvian/vertico-timer"))

Elpaca

(use-package vertico-timer
  :ensure (:host github :repo "ventruvian/vertico-timer")
  :after vertico)

straight.el

(use-package vertico-timer
  :straight (:host github :repo "ventruvian/vertico-timer"))

Configuration

Configuring the timer as well as a session toggle is strongly recommended. It is kept rather short (but usable) by default to push you to set one yourself. Everybody has their own sweet spot.

You’ll also want a toggle for number input. Otherwise you’d need to prefix every number that you want to insert with C-q.

A minimum recommended configuration:

(use-package vertico-timer
  ;; Seconds to wait before executing the action
  :custom (vertico-timer-timeout-seconds 0.25)
  ;; Actually input numbers into the minibuffer
  :bind (:map vertico-map ("M-i" . vertico-timer-toggle-in-session))
  :hook (vertico-mode . vertico-timer-mode))

A more advanced configuration utilizing changes to the exit action:

(use-package vertico-timer

  :custom ; in doom prefer `setq!' over :custom
  ;; Tell the timer which action you run most often
  (vertico-timer-default-action #'vertico-directory-enter)
  ;; Seconds to wait before executing the action
  (vertico-timer-timeout-seconds 0.25)
  ;; Get extra time for a third key to choose an exit action
  (vertico-timer-key-interaction 'reset)
  ;; Change action hint for default action
  (vertico-timer-default-action-hint "exit")

  :hook (vertico-mode . vertico-timer-mode)
  ;; Placeholder bindings, find ones that suit you!
  :bind (:map vertico-map
              ;; "Prefix" to allow for number input
              ("s-t" . vertico-timer-toggle-in-session)
              ;; Pre-digit key if you know you'll want to insert into the minibuffer
              ("s-i" . vertico-timer-prep-insert-action)
              ;; Probably rarely used. Change back to default action
              ("s-d" . vertico-timer-prep-default-action)
              ;; Cycle through registered action pre-digit-press
              ;; Doing this probably won't make you very fast
              ("s-<right>" . vertico-timer-cycle-actions-forward)
              ("s-<left>"  . vertico-timer-cycle-actions-backward))

  :config
  ;; Activate the action hint in the minibuffer (in doom prefer `setq!')
  (setopt vertico-timer-action-hint-format
          vertico-timer-action-hint-default-format)

  ;; embark-become acts on the input not the candidate so we insert first
  (defun i/embark-become ()
    (interactive)
    (vertico-insert)
    (call-interactively #'embark-become))

  ;; Call embark-act by pressing "a" after digits or "s-a" before
  (vertico-timer-register-actions
   "a" embark-act :name "act" :prep-key "s-a"
   "b" i/embark-become :name "become" :prep-key "s-b"
   "SPC" embark-select :name "select" :prep-key "M-SPC" :keep t))

Note: The keybindings presented here are not a recommendation, I use totally different ones myself, but since my keyboard layout is differnt from everybody elses there is little sense in sharing mine. These keybindings should be seen as placeholders. If you come up with a set of keybindings that are compatible with regular keyboards and that work well for you please share them and I’ll update this section.

Customization Options

  • vertico-timer-timeout-seconds: Seconds to wait before executing action (default 0.5)
  • vertico-timer-default-action: Default action function (default vertico-exit)
  • vertico-timer-default-action-hint: Hint for default action in overlay (default “default”)
  • vertico-timer-insert-action-hint: Hint for insert action (default “insert”)
  • vertico-timer-key-interaction: How timer interacts with successive digit presses (“reset” or “none”, default “reset”)
  • vertico-timer-action-hint-fstring: Format string for action hint overlay

Usage

Enable vertico-timer-mode globally to activate the timer-based selection. It will automatically enable vertico-indexed-mode (if not already enabled).

Use digit keys to select candidates while completing. Digit keys will highlight the corresponding candidate and start a timer. The action executed by the timer can be changed. If enabled, the current action will be overlayed in the minibuffer.

Available actions during timer:

  • TAB / i: Insert the candidate
  • RET: Cancel the timer (and execute the default action)
  • Your own keys registered via vertico-timer-register-actions

If you need to disable the behavior during a single completion session (e.g., to input digits as text), bind vertico-timer-toggle-in-session.

Changing the Action on the Target

For the period the timer runs a high-precedence keymap (a transient map) is activated. The digit keys are bound to it. (Nearly) All other keys are empty.

This keymap vertico-timer-map provides facilties to influence the action the timer will take on the target with a single letter. For example RET is bound to it to execute the default action on the candidate right away.

If you want to change the exit-action on a candidate with multi-digit index (i.e. 11) you may be pressed for time. You can instruct the timer to reset with every successive digit you press by setting vertico-timer-key-interaction to 'reset.

Defining Custom Actions

Use vertico-timer-register-actions with a list of bindings (can be single letters) and commands to be register them in vertico-timer-map. For example:

(defun i/print-cand ()
  (interactive)
  (message "hello from %s" (vertico--candidate)))

;; If you want the action hint add :name keys here
(vertico-timer-register-actions
 "p" i/print-cand
 "a" embark-act)

With this snippet you will have two extra actions available after selection. When you press 3 p a greeting will appear from the 3rd candidate and if you press 3 a you will invoke embark-act on it. One is probably more useful than the other.

Here is a little comparison of keypresses between this configuration of vertico-timer and a setup with embark’s quick-start settings:

Use Casevertico-indexedvertico-timer
Select 5th candidateC-u 5 RET5
Select 10th candidateC-u 1 0 RET1 0
Insert 7th candidateC-u 7 TAB7 i
7 TAB
embark-act on 6thC-u 6 C-.6 a
embark-become on 11thC-u 1 1 TAB C-. B1 1 b

Okay that last one was a bit contrieved as you could use the same exit action (combining vertico-insert with embark-become) in vertico-indexed, but you’d need another unweildly keychord for it.

Displaying an Action Hint

You can activate an overlay in the minibuffer that displays your current choice of action by setting vertico-timer-action-hint-format to a valid (possibly propertized) string. %s will be replaced by the name of your action in the minibuffer. The default will get you started:

(setopt vertico-timer-action-hint-format
        vertico-timer-action-hint-default-format)

Using setopt (or setq! in doomland) will validate the string first, if you use setq you have to ensure that your string contains exactly one %s as the format parameter.

The name shown is the value of the :name key in vertico-timer-register-actions. So in the example from earlier add:

(defun i/print-cand ()
  (interactive)
  (message "hello from %s" (vertico--candidate)))

(vertico-timer-register-actions
 "p" i/print-cand :name "greet"
 "a" embark-act :name "act")

If you leave out the :name key a generated name like cust-g408 will be shown for each of your anonymous actions. This is probably not very helpful so if you use the action hint set a name as well.

Preparing an Action

You can also prepare an action if you know you want to change it but haven’t found the candidate yet, effectively inverting the mental model. An “insert-action” (calling vertico-insert) and a “default-action” are preconfigured, bind vertico-timer-prep-insert-action and vertico-timer-prep-default-action in vertico-map if you want to be able to set these before you start the timer.

Your custom actions can be prepared by passing a :prep-key argument to vertico-timer-register-actions:

(keymap-set vertico-map "s-i" #'vertico-timer-prep-insert-action)

(vertico-timer-register-actions
 "a" embark-act :name "act" :prep-key "s-a")

Now you will be able to hit s-i 3 to insert the 3rd candidate into the minibuffer with leasure or s-a 3 to embark-act on it (again these keybindings are completely made up and not battle-tested).

NOTE: vertico-quick is probably the more prudent choice for this workflow, since it’s letter overlays are always easily in reach. A slight counterpoint could be that they only appear after hitting the keybinding. In the future I may provide an option to switch to vertico-quick with prep-keys.

Preserving an Action throughout the Session

You may define an action that doesn’t exit the minibuffer (say embark-select). By default vertico-timer reset’s the action back to default each time it runs it. However you may want to run that same operation again.

For example if you are in a consult-yank-pop session and you want to embark-select a couple of candidates for bulk insertion with embark-act-all then it is bothersome to have to hit SPC after each digit again or prep the action in between digit keypresses. Instead you can define the action to stay active by passing a :keep keyword argument to vertico-timer-register-actions. If any such action is switched to it will stick around as long as the minibuffer is active.

(vertico-timer-register-actions
 "SPC" embark-select :keep t)

So in the yank-pop example you would hit: M-y 3 SPC 4 5 6 to mark 4 candidates and yank them all into the buffer with embark-act-all.

This goes particularly well with a prep-key, with the below configuration you can M-y M-SPC 3 4 5 6 to achieve the same result.

(vertico-timer-register-actions
 "SPC" embark-select :prep-key "M-SPC" :keep t)

Cyclying Through Actions

Two functions are available to cycle all available exit actions:

  • vertico-timer-cycle-actions-forward
  • vertico-timer-cycle-actions-backward

If you want you can bind them, but cycling through things never encourages speed, but they may aid in discovery of the functionality. If you invoke a cycle command the action hint will display for the session regardless of the global setting.

Workflow Suggestions

Summarizing, some possible workflows come to mind, let’s name them for pizzazz:

Versatile
Set a long timer (>0.5s). Prefer exit actions over waiting for timeout. Use default action on RET, but have custom actions on easy keys for versatility.
Speed
Short timer (~0.25s) and unset timer-reset. Use default action mostly, let timer confirm selection automatically. Requires fast typing, and the timer saves a keypress.
Compromise
Short timer (​0.25s), but with ~vertico-timer-key-interaction set to reset, allowing the second digit to reset the timer to be able to choose an exit action.
Goofy
Press a :prep-key first to select an action, then digits to select candidate. Maybe useful when you know the action but not the candidate yet, e.g., embark-become before finding the target command. Compatible with 1.-~3.~

Commands

  • vertico-timer-mode: Global minor mode to enable/disable
  • vertico-timer-toggle-in-session: Toggle mode for current session
  • vertico-timer-cycle-actions-forward: Cycle to next registered action
  • vertico-timer-cycle-actions-backward: Cycle to previous registered action
  • vertico-timer-prep-default-action: Prepare default action
  • vertico-timer-prep-insert-action: Prepare insert action
  • Generate your own commands with vertico-timer-register-actions

Keybindings

Two keymaps are relevant for this package:

vertico-map
For all commands that prepare an action as well as for the session-toggle. This package doesn’t bind to vertico-map on it’s own but you are adviced to bind some keys there to be able to use this package effectively. The :prep-key binding passed to vertico-timer-register-actions will end up in this map.
vertico-timer-map
All command active during the timer-duration. Default actions are bound to RET and TAB / i, the digit keys are populate for base functionality and the bindings of vertico-timer-register-actions end up here.

During the timer:

  • Digit keys: Select candidate and interact with timer
  • TAB / i: Switch to insert action
  • RET: Confirm default action
  • Custom keys as registered

In vertico-map:

  • digit keys: Adapted for timer selection
  • s-t: Toggle in session (example)
  • Prep keys for advance action selection (examples s-a, etc.)
  • Cycling keys (examples s-<right>, etc.)

Advice for Binding Keys

  • Take some time to consider the keys you bind. The value of this package drastically depends on how comfortably you can type keys in succesion.
  • If your keyboard (-layout) allows accessing digit keys with one hand (i.e. if you have a number block) consider binding custom actions to keys accessible with the other hand.

Contributions

Contributions welcome! Feel free to check the issues page or submit a pull requests or edit the wiki to share your custom actions.

License

This package is licensed under GPLv3. See LICENCE for details.

About

An extension for rapid selection in emacs' completion framework vertico.

Topics

Resources

License

Stars

Watchers

Forks