This is keymap-popup.info, produced by makeinfo version 7.1.1 from
keymap-popup.texi.

INFO-DIR-SECTION Emacs misc features
START-INFO-DIR-ENTRY
* Keymap Popup: (keymap-popup). Define a keymap once, dispatch keys and show a popup menu.
END-INFO-DIR-ENTRY


File: keymap-popup.info,  Node: Top,  Next: Introduction,  Up: (dir)

Keymap Popup User Manual
************************

* Menu:

* Introduction::
* Installation::
* Quick start::
* Defining keymaps::
* Showing the popup::
* Special bindings::
* Prompting and re-entry::
* Annotating existing keymaps::
* Customization::
* When to use this vs transient::
* Contributing::

-- The Detailed Node Listing --

Installation

* From source::
* With use-package::

Defining keymaps

* Macro syntax::
* Groups and rows::
* Dynamic descriptions::
* Sharing bindings with parent::
* Adding and removing entries at runtime::

Showing the popup

* Display backends::
* Persistent popups::
* Dismissing programmatically::

Special bindings

* Sub-menus with keymap::
* Toggles with switch::
* Stay-open commands with stay-open::
* Disabling entries with inapt-if::
* Hiding entries with if::
* Prefix-arg variants with c-u::

Annotating existing keymaps

* Attaching descriptions built from data::

Customization

* User options::
* Faces::



File: keymap-popup.info,  Node: Introduction,  Next: Installation,  Prev: Top,  Up: Top

1 Introduction
**************

‘keymap-popup’ defines a normal Emacs keymap with embedded descriptions,
and renders it as a popup menu on demand.  One definition, two uses:
direct key dispatch and an interactive menu.

   Requires Emacs 29.1 or later (for ‘defvar-keymap’).  No external
dependencies.


File: keymap-popup.info,  Node: Installation,  Next: Quick start,  Prev: Introduction,  Up: Top

2 Installation
**************

* Menu:

* From source::
* With use-package::


File: keymap-popup.info,  Node: From source,  Next: With use-package,  Up: Installation

2.1 From source
===============

     (add-to-list 'load-path "/path/to/keymap-popup")
     (require 'keymap-popup)


File: keymap-popup.info,  Node: With use-package,  Prev: From source,  Up: Installation

2.2 With ‘use-package’
======================

     (use-package keymap-popup
       :ensure t)


File: keymap-popup.info,  Node: Quick start,  Next: Defining keymaps,  Prev: Installation,  Up: Top

3 Quick start
*************

     (keymap-popup-define my-commands-map
       "My commands."
       :group "Edit"
       "c" ("Comment" comment-dwim)
       "r" ("Rename" rename-file)
       :group "View"
       "g" ("Refresh" revert-buffer)
       "b" ("Bury buffer" bury-buffer))

     (keymap-set some-mode-map "C-c m" my-commands-map)

   The macro auto-binds ‘h’ inside the keymap, so ‘C-c m h’ opens the
popup.  ‘q’ or ‘C-g’ dismisses it.


File: keymap-popup.info,  Node: Defining keymaps,  Next: Showing the popup,  Prev: Quick start,  Up: Top

4 Defining keymaps
******************

* Menu:

* Macro syntax::
* Groups and rows::
* Dynamic descriptions::
* Sharing bindings with parent::
* Adding and removing entries at runtime::


File: keymap-popup.info,  Node: Macro syntax,  Next: Groups and rows,  Up: Defining keymaps

4.1 Macro syntax
================

     (keymap-popup-define MAP-NAME
       DOCSTRING
       :popup-key KEY  :exit-key KEY
       :parent KEYMAP  :description STRING-OR-FN
       :persistent BOOL
       BINDING...)

   Only ‘MAP-NAME’ and the bindings are required.  Leading options are
optional and may appear in any order before the first binding.
‘keymap-popup-annotate’ accepts the display keywords above except
‘:parent’, but does _not_ apply defaults for ‘:popup-key’ or ‘:exit-key’
- it never binds keys unless asked.  A popup key may not replace an
ordinary existing binding; such a collision signals an error.

   A binding takes one of three forms:

     KEY (DESC COMMAND     [PROP VALUE...])    ; ordinary command
     KEY (DESC :switch VAR [PROP VALUE...])    ; toggle
     KEY (DESC :keymap MAP [PROP VALUE...])    ; sub-menu

   • ‘KEY’ - ‘key-valid-p’ string.
   • ‘DESC’ - string or zero-arg function (called at render time in the
     user's buffer, so it sees buffer-local state).
   • ‘COMMAND’ - command symbol or a ‘(lambda () (interactive) ...)’.
   • ‘MAP’ - keymap variable symbol.  It may be defined after its
     parent; its value is resolved when the sub-menu opens.
   • ‘PROP’ - one of ‘:if’, ‘:inapt-if’, ‘:stay-open’, ‘:c-u’ (see *note
     Special bindings::).  ‘:stay-open’ is a boolean flag and may omit
     its ‘t’ value.

   Declarations are checked when the macro expands.  Invalid keys,
malformed entries, missing switch variables or sub-menu targets,
duplicate or unknown properties, and unsupported options report the
offending entry directly.


File: keymap-popup.info,  Node: Groups and rows,  Next: Dynamic descriptions,  Prev: Macro syntax,  Up: Defining keymaps

4.2 Groups and rows
===================

     (keymap-popup-define my-feed-map
       :group "Navigation"
       "n" ("Next" next-line :stay-open t)
       "p" ("Previous" previous-line :stay-open t)
       :group "Tags"
       "r" ("Toggle read" my-feed-toggle-read :stay-open t)
       :row
       :group "Feeds"
       "A" ("Add feed" my-feed-add)
       "D" ("Remove feed" my-feed-remove))

   ‘:row’ between groups starts a fresh row of columns beneath the
first.  Groups may attach their own predicates: ‘:group ("Bulk" :if PRED
:inapt-if PRED)’.


File: keymap-popup.info,  Node: Dynamic descriptions,  Next: Sharing bindings with parent,  Prev: Groups and rows,  Up: Defining keymaps

4.3 Dynamic descriptions
========================

     (keymap-popup-define my-encryption-map
       :description (lambda ()
                      (format "Encryption: %s" my-chat-encryption))
       "o" ("OMEMO" my-chat-encryption-set-omemo)
       "g" ("OpenPGP" my-chat-encryption-set-openpgp)
       "p" ("Plaintext" my-chat-encryption-set-plaintext))

   The header is re-rendered after each command.


File: keymap-popup.info,  Node: Sharing bindings with parent,  Next: Adding and removing entries at runtime,  Prev: Dynamic descriptions,  Up: Defining keymaps

4.4 Sharing bindings with ‘:parent’
===================================

Same semantics as ‘defvar-keymap’'s ‘:parent’.  The popup walks the
parent chain, so parent bindings appear in the child's menu under their
original group.

   If a key is bound in both parent and child, the child wins for both
dispatch and display.  Shadowed parent entries are dropped from the
popup, and a parent group whose entries are all shadowed disappears
entirely.

     (keymap-popup-define my-items-mode-map
       :parent my-dashboard-common-map
       :group "Items"
       "RET" ("Open" my-items-open)
       "q" ("Back" my-items-back))


File: keymap-popup.info,  Node: Adding and removing entries at runtime,  Prev: Sharing bindings with parent,  Up: Defining keymaps

4.5 Adding and removing entries at runtime
==========================================

     (keymap-popup-add-entry my-commands-map
                             "l" "Log message" #'my-log-message
                             "Edit")

     (keymap-popup-remove-entry my-commands-map "l")

   Signatures:

   • ‘(keymap-popup-add-entry KEYMAP KEY DESC COMMAND &optional GROUP)’
   • ‘(keymap-popup-remove-entry KEYMAP KEY)’

   Both mutate the keymap and its descriptions in place.  Re-adding a
key replaces the prior entry (in whatever group it was in), matching
‘keymap-set’'s semantics.  Both require existing popup metadata;
removing the last entry clears that metadata.


File: keymap-popup.info,  Node: Showing the popup,  Next: Special bindings,  Prev: Defining keymaps,  Up: Top

5 Showing the popup
*******************

     ;;;###autoload
     (defun yeetube ()
       (interactive)
       (keymap-popup yeetube-mode-map))

   ‘C-g’ always dismisses, regardless of the configured ‘:exit-key’.

* Menu:

* Display backends::
* Persistent popups::
* Dismissing programmatically::


File: keymap-popup.info,  Node: Display backends,  Next: Persistent popups,  Up: Showing the popup

5.1 Display backends
====================

‘keymap-popup-backend’ selects ‘keymap-popup-backend-side-window’
(default) or ‘keymap-popup-backend-child-frame’.  Bind it locally for a
one-off floating popup:

     (defun my-commands-floating ()
       (interactive)
       (let ((keymap-popup-backend #'keymap-popup-backend-child-frame))
         (keymap-popup my-commands-map)))


File: keymap-popup.info,  Node: Persistent popups,  Next: Dismissing programmatically,  Prev: Display backends,  Up: Showing the popup

5.2 Persistent popups
=====================

By default the popup dismisses after a suffix command runs.
‘:persistent t’ keeps it open until the exit key or ‘C-g’:

     (keymap-popup-define my-map :persistent t ...)
     (keymap-popup-annotate dired-mode-map :popup-key "?" :persistent t ...)
     (setopt keymap-popup-persistent t)        ; global default

   Per-keymap setting wins over the global one.  Use ‘:persistent nil’
on a definition or annotation to disable a non-nil global default for
that keymap.


File: keymap-popup.info,  Node: Dismissing programmatically,  Prev: Persistent popups,  Up: Showing the popup

5.3 Dismissing programmatically
===============================

Call ‘keymap-popup-dismiss’ from a command to close the popup before
transitioning elsewhere:

     (defun my-open-detail ()
       (interactive)
       (keymap-popup-dismiss)
       (pop-to-buffer "*detail*"))


File: keymap-popup.info,  Node: Special bindings,  Next: Prompting and re-entry,  Prev: Showing the popup,  Up: Top

6 Special bindings
******************

Predicate quick reference:

   • ‘:if’ - _hide_ the entry (or group) when the predicate returns nil.
   • ‘:inapt-if’ - _grey out_ the entry and block its key when the
     predicate returns non-nil.

   Predicates may be evaluated repeatedly.  ‘:if’ runs during ordinary
keymap lookup as well as popup rendering; ‘:inapt-if’ runs while
rendering and handling popup keys.  Keep both cheap and free of side
effects.

* Menu:

* Sub-menus with keymap::
* Toggles with switch::
* Stay-open commands with stay-open::
* Disabling entries with inapt-if::
* Hiding entries with if::
* Prefix-arg variants with c-u::


File: keymap-popup.info,  Node: Sub-menus with keymap,  Next: Toggles with switch,  Up: Special bindings

6.1 Sub-menus with ‘:keymap’
============================

     (keymap-popup-define my-dashboard-mode-map
       :group "Navigate"
       "n" ("Nodes" :keymap my-dashboard-nodes-map)
       "t" ("Themata" :keymap my-dashboard-themata-map)
       :group "More"
       "x" ("Import/Export" :keymap my-dashboard-io-map))

   Sub-menu entries use the ‘keymap-popup-submenu’ face.  Nesting is
unlimited; the exit key pops one level.


File: keymap-popup.info,  Node: Toggles with switch,  Next: Stay-open commands with stay-open,  Prev: Sub-menus with keymap,  Up: Special bindings

6.2 Toggles with ‘:switch’
==========================

     (keymap-popup-define yeetube-mode-map
       :group "Actions"
       "o" ("Settings" :switch yeetube--show-settings)
       :group "Settings"
       "V" ("No video" :switch yeetube-mpv-no-video
            :if (lambda () yeetube--show-settings)))

   The variable is auto-declared with ‘defvar-local’; each buffer gets
its own value.  Switches always keep the popup open - no need to add
‘:stay-open t’.


File: keymap-popup.info,  Node: Stay-open commands with stay-open,  Next: Disabling entries with inapt-if,  Prev: Toggles with switch,  Up: Special bindings

6.3 Stay-open commands with ‘:stay-open’
========================================

     (keymap-popup-define my-mark-map
       :group "Mark"
       "m" ("Mark" dired-mark :stay-open)
       "u" ("Unmark" dired-unmark :stay-open)
       "U" ("Unmark all" dired-unmark-all-marks))

   For an entire popup, prefer ‘:persistent t’ (see *note Persistent
popups::).


File: keymap-popup.info,  Node: Disabling entries with inapt-if,  Next: Hiding entries with if,  Prev: Stay-open commands with stay-open,  Up: Special bindings

6.4 Disabling entries with ‘:inapt-if’
======================================

     (keymap-popup-define yeetube-mode-map
       :group "Settings"
       "p" ("Toggle pause" yeetube-mpv-toggle-pause
            :inapt-if (lambda () (not (get-process yeetube-mpv--process-name)))))

     ;; Group-level applies to every entry in the group:
     (keymap-popup-define my-bulk-map
       :group ("Bulk" :inapt-if (lambda () (not (my-marked-files))))
       "c" ("Chmod" my-do-chmod)
       "z" ("Compress" my-do-compress))

   Inapt entries render in ‘keymap-popup-inapt’ and print "Command
unavailable" when pressed.


File: keymap-popup.info,  Node: Hiding entries with if,  Next: Prefix-arg variants with c-u,  Prev: Disabling entries with inapt-if,  Up: Special bindings

6.5 Hiding entries with ‘:if’
=============================

     (keymap-popup-define yeetube-mode-map
       "o" ("Settings" :switch yeetube--show-settings)
       :group ("Playback" :if (lambda () yeetube--show-settings))
       "p" ("Toggle pause" yeetube-mpv-toggle-pause)
       "v" ("Toggle video" yeetube-mpv-toggle-video))


File: keymap-popup.info,  Node: Prefix-arg variants with c-u,  Prev: Hiding entries with if,  Up: Special bindings

6.6 Prefix-arg variants with ‘:c-u’
===================================

     (keymap-popup-define my-greet-map
       :group "Actions"
       "a" ("Greet" my-greet :c-u "SHOUT (C-u)"))

   The ‘:c-u’ label renders in ‘shadow’ alongside the description as a
hint.  Pressing ‘C-u’ enters prefix-arg mode: ‘:c-u’ entries switch to
‘warning’, all other entries dim with ‘shadow’, and
‘universal-argument-map’ takes over so further ‘C-u’, digits, and ‘-’
compose the prefix as usual.  Pressing the key then runs the command
with ‘current-prefix-arg’ set, exactly like any ‘interactive "P"’
command.


File: keymap-popup.info,  Node: Prompting and re-entry,  Next: Annotating existing keymaps,  Prev: Special bindings,  Up: Top

7 Prompting and re-entry
************************

Suffix commands dismiss the popup when they return.  For commands that
prompt (‘read-string’, ‘completing-read’, ...) and should leave the
popup visible, either set ‘:persistent t’ on the keymap or re-invoke
‘keymap-popup’ at the end of the command:

     (defun my-edit-set-title ()
       (interactive)
       (let ((new (read-string "Title: ")))
         (my-db-set-title new))
       (keymap-popup my-edit-map))

     (keymap-popup-define my-edit-map
       :group "Edit"
       "t" ("Title" my-edit-set-title))

   The same pattern works with a lambda in the binding itself:

     (keymap-popup-define my-settings-map
       :group "Settings"
       "n" ((lambda () (format "Limit: %d" my--limit))
            (lambda ()
              (interactive)
              (setq-local my--limit (read-number "Limit: "))
              (keymap-popup my-settings-map))))


File: keymap-popup.info,  Node: Annotating existing keymaps,  Next: Customization,  Prev: Prompting and re-entry,  Up: Top

8 Annotating existing keymaps
*****************************

‘keymap-popup-annotate’ attaches descriptions to a keymap you don't own
(like ‘dired-mode-map’) without rebinding its keys.  Keys are resolved
at popup time via ‘where-is-internal’, so the popup tracks the user's
current bindings.

     (keymap-popup-annotate dired-mode-map
       :popup-key "?"
       :persistent t
       :description (lambda ()
                      (format "Dired: %s" (abbreviate-file-name default-directory)))
       :group "File"
       dired-find-file "Open"
       dired-do-copy "Copy"
       dired-do-rename "Rename"
       dired-do-delete "Delete"
       :group "Navigate"
       dired-up-directory "Up"
       dired-previous-line "Prev"
       dired-next-line "Next"
       :group "More"
       "M" ("Mark operations" :keymap my-dired-mark-map))

   Entries are ‘COMMAND-SYMBOL DESCRIPTION’ (the key comes from the
user's binding).  String-key entries are allowed too - that's how
sub-menus attach to an annotated keymap.

* Menu:

* Attaching descriptions built from data::


File: keymap-popup.info,  Node: Attaching descriptions built from data,  Up: Annotating existing keymaps

8.1 Attaching descriptions built from data
==========================================

Both macros take bindings written literally in source.  When the menu
itself is computed at runtime - an anonymous keymap, entries generated
from a list - use the function ‘keymap-popup-attach’:

     (defun my-project-popup ()
       "Pick a recent project from a popup."
       (interactive)
       (let* ((projects (seq-take (project-known-project-roots) 9))
              (map (make-sparse-keymap))
              (bindings
               (cl-loop for root in projects
                        for key from ?1
                        for cmd = (let ((r root))
                                    (lambda () (interactive)
                                      (project-switch-project r)))
                        do (keymap-set map (char-to-string key) cmd)
                        append (list (char-to-string key)
                                     (list (abbreviate-file-name root) cmd)))))
         (keymap-popup (keymap-popup-attach
                        map (append '(:group "Projects") bindings)))))

   Signature:

   • ‘(keymap-popup-attach KEYMAP BINDINGS &rest OPTS)’

   BINDINGS is an evaluated list in the same shape as
‘keymap-popup-annotate’'s body; commands and predicates are actual
objects rather than forms.  Function symbols may precede their
definitions; they must be callable when the popup uses them.  OPTS
accepts ‘:exit-key’, ‘:description’, and ‘:persistent’.  Reattaching
replaces prior descriptions and options, so omitted options return to
their defaults.  Unlike the macros, no keys are bound and no commands
are defined - keyed entries should already be bound in KEYMAP.  Runtime
values and options are validated before metadata is attached.  ‘:switch’
entries require ‘keymap-popup-define’ and are rejected by this runtime
API.  Returns KEYMAP.


File: keymap-popup.info,  Node: Customization,  Next: When to use this vs transient,  Prev: Annotating existing keymaps,  Up: Top

9 Customization
***************

* Menu:

* User options::
* Faces::


File: keymap-popup.info,  Node: User options,  Next: Faces,  Up: Customization

9.1 User options
================

   • ‘keymap-popup-backend’ - display function; built-ins are
     ‘keymap-popup-backend-side-window’ (default) and
     ‘keymap-popup-backend-child-frame’.
   • ‘keymap-popup-display-action’ - ‘display-buffer’ action for the
     side-window backend.
   • ‘keymap-popup-buffer-parameters’ - buffer-local parameters applied
     to the popup buffer.
   • ‘keymap-popup-child-frame-parameters’ - frame parameters for the
     child-frame backend.
   • ‘keymap-popup-persistent’ - global default for ‘:persistent’.
   • ‘keymap-popup-default-popup-key’ - default ‘:popup-key’ (‘h’).
   • ‘keymap-popup-default-exit-key’ - default ‘:exit-key’ (‘q’).


File: keymap-popup.info,  Node: Faces,  Prev: User options,  Up: Customization

9.2 Faces
=========

   • ‘keymap-popup-key’ - key string (inherits ‘help-key-binding’).
   • ‘keymap-popup-group-header’ - group header.
   • ‘keymap-popup-value’ - ‘[on]’ / ‘[off]’ and inline values.
   • ‘keymap-popup-submenu’ - sub-menu entries.
   • ‘keymap-popup-inapt’ - disabled entries (inherits ‘shadow’).


File: keymap-popup.info,  Node: When to use this vs transient,  Next: Contributing,  Prev: Customization,  Up: Top

10 When to use this vs ‘transient’
**********************************

‘transient’ is its own abstraction: a class hierarchy built on EIEIO,
with prefix/suffix/infix objects and a custom dispatch loop.  The menu
is not an Emacs keymap.  It fits codebases that already lean on EIEIO
and benefit from typed, extensible class machinery (magit being the
canonical example).

   ‘keymap-popup’ is just a ‘defvar-keymap’ plus metadata, displayed via
the built-in ‘set-transient-map’ primitive (which predates the
‘transient’ package by years and is unrelated to it, despite the naming
collision).  Use it when you want a real keymap that also knows how to
draw itself: bindings show up in ‘where-is’ and ‘describe-key’, and the
popup does not block ‘M-x’.  There is no class hierarchy to learn, and
state lives wherever you choose to put it.


File: keymap-popup.info,  Node: Contributing,  Prev: When to use this vs transient,  Up: Top

11 Contributing
***************

Source is a single file, ‘keymap-popup.el’, with tests in
‘tests/keymap-popup-tests.el’.  Run them with:

     emacs --batch -l ert -l keymap-popup.el \
                   -l tests/keymap-popup-tests.el \
                   -f ert-run-tests-batch-and-exit



Tag Table:
Node: Top256
Node: Introduction1288
Node: Installation1689
Node: From source1866
Node: With use-package2074
Node: Quick start2266
Node: Defining keymaps2831
Node: Macro syntax3126
Node: Groups and rows4892
Node: Dynamic descriptions5578
Node: Sharing bindings with parent6126
Node: Adding and removing entries at runtime6926
Node: Showing the popup7750
Node: Display backends8172
Node: Persistent popups8664
Node: Dismissing programmatically9328
Node: Special bindings9722
Node: Sub-menus with keymap10510
Node: Toggles with switch11056
Node: Stay-open commands with stay-open11683
Node: Disabling entries with inapt-if12213
Node: Hiding entries with if12999
Node: Prefix-arg variants with c-u13494
Node: Prompting and re-entry14256
Node: Annotating existing keymaps15316
Node: Attaching descriptions built from data16525
Node: Customization18538
Node: User options18741
Node: Faces19580
Node: When to use this vs transient20025
Node: Contributing21016

End Tag Table


Local Variables:
coding: utf-8
End:
